C++. Функции класса, которые объявлены с константным (const) указателем this и непостоянным (volatile) указателем this

Функции класса, которые объявлены с константным (const) указателем this и непостоянным (volatile) указателем this


Содержание



1. Как выглядит общая форма объявления функции с константным указателем this?

Объявление функции в классе (на примере функций MyFun1(), MyFun2()) с константным указателем this имеет следующий общий вид:

class CMyClass
{
    private:
    // скрытые члены данных и методы класса
    // ...

    public:
    // функция объявлена с константным указателем this
    return_type MyFun1(parameters) const; // out-of-line
    return_type MyFun2(parameters) const // inline реализация
    {
        // тело функции
        // ...
    }
}

// реализация out-of-line функции
return_type CMyClass::MyFun1(parameters) const
{
    // тело функции MyFun1()
    // ...
}

где

  • return_type – тип, который возвращается той или иной функцией;
  • parameters – параметры, которые получает функция.

В вышеприведенном фрагменте, в классе CMyClass объявляются две функции с константным указателем this. Функция MyFun1() реализована за пределами класса (типа out-of-line). Функция MyFun2() реализована в классе (типа inline). Перед телом каждой функции стоит ключевое слово const.

2. В чем состоит суть объявления функции с константным указателем this?

В функции с константным указателем this перед телом функции помещается ключевое слово const. Это означает, что тело функции имеет ограниченные возможности использования.

Следует заметить, что в этом случае ключевое слово const не имеет отношение к значению, которое возвращается функцией. Оно имеет значение только для указателя this, используемого в функции.

Если функция объявлена с константным указателем this, то в теле функции запрещается изменять данные класса. При попытке изменить данные класса, будет возникать ошибка компиляции.

3. Пример, который демонстрирует отличие между функциями с константным указателем this и без него

Отличие между обычной функцией и функцией с константным указателем this хорошо видно в реализации нижеследующего класса.

Пусть задан класс CRadius, реализующий радиус некоторого объекта или геометрической фигуры. В классе реализованы:

  • внутренний член данных radius типа double;
  • конструктор CRadius();
  • обычную функцию GetRadius(), которая увеличивает радиус вдвое и возвращает его значение;
  • функцию GetRadius2(), объявленную с константным указателем this. Данная функция возвращает значение внутренней переменной radius.
// класс CRadius
class CRadius
{
    double radius;

    public:
    CRadius(void); // конструктор класса по умолчанию

    // Обычная функция-член класса,
    // этой функции неявно передается указатель: CRadius * const this
    double GetRadius(void)
    {
        radius *= 2.0; // разрешено
        return radius;
    }

    // Функция-член класса, объявленная с const
    // этой функции передается указатель: const CRadius * const this
    double GetRadius2(void) const
    {
        //radius = 2.0; // Запрещено! Ошибка компиляции
        return radius;
    }
};

Если в функции GetRadius2() попробовать изменить значение внутренней переменной radius, то выйдет ошибка компиляции:

Error: 'radius' cannot be modified because it is being accessed through a const object

Ключевое слово const перед телом функции в классе означает, что функции запрещается вносить любые изменения в члены данных класса.
Использование класса CRadius в некотором программном коде, например, обработчике события:

CRadius CR;

double d;
d = CR.GetRadius2(); // d = 1.0 - вызов функции с константным this
d = CR.GetRadius(); // d = 1.0 - вызов обычной функции-члена

4. Каким образом модифицируется указатель this, который передается функции-члену, которая объявлена с константным указателем this?

Пусть задан класс CRadius, в котором есть функция-член с константным указателем this:

// класс CRadius
class CRadius
{
    double radius;

    public:
    CRadius(void); // конструктор класса по умолчанию

    // Обычная функция-член класса,
    // этой функции неявно передается указатель: CRadius * const this
    double GetRadius(void)
    {
        radius *= 2.0; // разрешено
        return radius;
    }

    // Функция-член класса, объявленная с const
    // этой функции передается указатель: const CRadius * const this
    double GetRadius2(void) const
    {
        //radius = 2.0; // Запрещено! Ошибка компиляции
        return radius;
    }
};

Указатель this в классе есть невидимым, для функций класса он передается неявно. Это означает, что при попытке явного описания указателя this в классе, компилятор выдаст ошибку.
Обычной функции-члену класса GetRadius() указатель this передается в неявном виде как:

CRadius * const this;

Функции-члену GetRadius2() невидимый указатель this передается с ключевым словом const:

// модифицированный указатель this
const CRadius * const this;

Запись

const CRadius

означает, что объект типа CRadius есть константным объектом. И поэтому, изменять значения объекта в теле функции GetRadius2() нельзя. Итак, нельзя изменять значения внутренних членов-данных класса в функции GetRadius2(). Попытка изменить значения внутреннего члена данных radius в теле функции GetRadius2() вызовет ошибку компиляции.

5. Для чего нужно использовать функции класса с константным указателем this?

Функции с константным указателем this целесообразно использовать в случаях, когда нужно, чтобы функция-член базового класса случайно не переопределилась в производных классах. В этом случае константный указатель this служит защитой данных базового класса от случайного их изменения в производных классах.

6. Что будет, если ключевое слово const разместить перед именем функции, а не перед телом функции?

Это есть важно. В этом случае, функция возвращает константное значение. Но изменять внутренние данные класса в теле функции можно.
Ключевое слово const перед именем функции относится к значению, которое возвращается функцией. Ключевое слово const перед телом функции относится к указателю this.

7. Можно ли объявлять ключевое слово const для статических функций-членов?

Объявление статической функции-члена с ключевым словом const не имеет смысла. При вызове статической функции-члена, невидимый указатель this не передается этой функции. При попытке объявления статической функции-члена с константным указателем this компилятор выдаст ошибку

... modifiers not allowed on static members functions

8. Какая общая форма объявления функции-члена с непостоянным указателем this? Ключевое слово volatile

Для того, чтобы объявить функцию-член с непостоянным указателем this используется ключевое слово volatile.
Общая форма объявления функции с непостоянным указателем this в классе имеет приблизительно следующий вид:

class CMyClass
{
    // скрытые члены данных и методы класса
    // ...

    public:
    // общедоступные члены данных и методы класса
    // функция-член с непостоянным указателем this
    return_type MyFun1(parameters) volatile; // out-of-line
    return_type MyFun2(parameters) volatile // inline
    {
        // тело функции
        // ...
    }
}

// реализация out-of-line функции
return_type CMyClass::MyFun1() volatile
{
    // тело функции MyFun1()
    // ...
}

где

  • return_type – тип, который возвращается той или другой функцией;
  • parameters – параметры, которые получает функция.

В вышеприведенном фрагменте, в классе CMyClass объявляются две функции с непостоянным (volatile) указателем this. Функция MyFun1() реализована за пределами класса (типа out-of-line). Функция MyFun2() реализована в классе (типа inline). Перед телом каждой функции стоит ключевое слово volatile.

9. В каких случаях целесообразно объявлять функцию с непостоянным (volatile) указателем this?

Объявление функции как volatile целесообразно использовать в случаях, когда нужно обрабатывать объект класса различными процессами, к которым можно отнести:

  • обработка объекта процессором;
  • обработка с помощью фоновых прикладных программ;
  • обработка с помощью программ обработки прерываний.

10. Что дает использование ключевого слова volatile для функции-члена класса?

Функция-член класса, которая объявлена с ключевым словом volatile трактуется компилятором в разных случаях по разному. Для таких функций компилятор отключает некоторые виды оптимизации в зависимости от типа объявленного объекта.
Функции-члены класса, которые объявлены с ключевым словом volatile можно использовать как для автоматических объектов так и для непостоянных (volatile) объектов.
Для константных объектов использование функций с непостоянным указателем запрещено. Опять же, все зависит от настроек и реализации компилятора.

11. Пример использования функций-членов класса с непостоянным указателем this

Пусть дан класс CRadius. В классе реализованы:

  • внутренний член-данных radius;
  • конструктор CRadius();
  • обычная функция-член класса GetRadius();
  • две функции-члены класса с непостоянным указателем this: GetRadius2() и SetRadius().
// класс CRadius
class CRadius
{
    double radius;

    public:
    CRadius(void); // конструктор класса по умолчанию

    // Обычная функция-член класса,
    // этой функции неявно передается CRadius * const this
    double GetRadius(void)
    {
        return radius;
    }

    // Функция-член класса, объявленная с volatile
    // этой функции передается volatile CRadius * const this
    double GetRadius2(void) volatile
    {
        return radius;
    }

    // Функция-член класса, объявленная с volatile
    void SetRadius(double r) volatile
    {
        radius = r;
    }
};

Использование класса в другом программном коде

CRadius CR1; // автоматический объект
volatile CRadius CR2; // непостоянный объект

double d;

// автоматический объект
CR1.SetRadius(5.5);
d = CR1.GetRadius2(); // d = 5.5

// непостоянный объект
CR2.SetRadius(6.7);
d = CR2.GetRadius2(); // d = 6.7

Как видно из программного кода, функции-члены с непостоянным (volatile) указателем this можно использовать для автоматических и непостоянных объектов. В этом случае, компилятор включает различные виды оптимизации в зависимости от типа объекта.


Связанные темы