воскресенье, сентября 25, 2005

Использование const. Часть 2.

Продолжаем захватывающую беседу о возможных использованиях многоликого const. Начало было тут: Использование const. Часть 1.

const данные в классе
Значения const данных класса задаются один раз и навсегда в конструкторе.

class CFoo
{
const int num;
public:
CFoo(int anum);
};
CFoo::CFoo(int anum):num(anum)
{
...
}
Интересный момент со static const данными класса. Вообще для данных целого типа (enum, int, char) их значения можно задавать прямо в объявлении класса. Вот этот код правильный с точки зрения стандарта:
class CFoo
{
public:
static const int num=50;
};
Но в Visual C++ 6.0 такое задание значения не работает, это один из багов Visual C++ 6.0. Тут задавать значение static const переменной следует отдельно. Не могу сказать, что эта бага меня сильно расстраивает. Вместо того, чтобы запоминать, когда можно при объявлении писать инициализацию, когда нельзя, лучше сразу так написать:
class CFoo
{
public:
static const int num ;
};

const int CFoo::num = 20;
const функции в классе
Функция класса, объявленная const, трактует this как указатель на константу. Вообще тип this в методе класса X будет X*. Но если метод класса объявлена как const, то тип this будет const X*. В таких методах не может быть ничего присвоено переменным класса, которые не объявлены как static или как mutablemutable потом). Также const-функции не могут возвращать не const ссылки и указатели на данные класса и не могут вызывать не const функции класса. const-функции иногда называют инспекторами (inspector), а остальные мутаторами (mutator). Я пыталась найти для mutator перевод, который будет звучать получше, но в переводах я ничего подходящего не встречала, а Яндекс.Лингво смог мне предложить только "ртутный вентиль", что сюда явно не подходит.
class CFoo
{
public:
int inspect() const; // Эта функция обещает не менять *this
int mutate(); // Эта функция может менять *this
};
В классе могут присутствовать две функции отличающиеся только const.
class CFoo
{
...
public:
int func () const;
int func ();
};
Не всякая функция может быть объявлена константной. Конструкторы и деструкторы не могут быть объявлены как const. Также не бывает static const функций.
class CFoo
{
int i;
public:
static int func () const; //ошибка
};
Константный класс
Официально такого понятия как константный класс (const class) не существует. Но часто под этим понимается объявление вида const CFoo p;.
Экземпляр класса CFoo, объявленный таким образом, обещает сохранить физическое состояние класса, не менять его. Как следствие, он не может вызвать не const функции класса CFoo. Все данные, не объявленные как const, начинают трактоваться как const. То есть
int становится int const
int * становится int * const
const int * становится int const *const
и так далее.


На этом все о const, но будет еще отдельный пост о том, как избавиться от const, то есть о mutable и const_cast.


Ссылки по теме:
Advice From the C++ Experts: Be Const-Correct
Const Correctness in C++

comp.lang.c++.moderated Const member functions
comp.lang.c++.moderated static const member of class
comp.lang.c++.moderated const classes and const members

Technorati tag:

18 коммент.:

Unknown комментирует...

"Но в Visual C++ 6.0 такое задание значения не работает, это один из багов Visual C++ 6.0. Тут задавать значение static const переменной следует отдельно." - это не баг, а требования стандарта. У Шилдта даже об этом написано в описании модификатора const...

archimed7592 комментирует...

это не баг, а требования стандарта.
А можно цитату из стандарта(не из Шилдта :D), где требуется задавать значение отдельно?

Со своей стороны приведу:
IS C++98,C++03.
9.4.2/4

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

Другими словами Алёна права, только она не упомянула, что, даже указав значение константы прямо в определении класса, всё равно придётся делать определение этой константы как обыкновенного static-data-member, причём, в определении значение константы указываться не должно.

Alena комментирует...

2vova
это не баг, а требования стандарта. У Шилдта даже об этом написано в описании модификатора const...
Тут archimed7592 уже очень подробно ответил, добавить мне нечего.

std.denis комментирует...

может mutator это изменятель/изменитель/модификатор ?
хотя "модификатор" уже используется в другом контексте. а "изменятель" как-то слишком забавно звучит

Анонимный комментирует...

Вообще тип this в методе класса X будет X*

Если быть до конца точным (тем более, что речь идет о const), то указатель this имеет тип X* const

Игорь комментирует...

Скажите пожалуйста, если есть член класса-массив в стиле C, то можно ли при его объявлении (в хэдере) указать в качестве размера ранее объявленную static const int? Попробовал, компилятор ругатеся.

Екклесиаст комментирует...

Другими словами Алёна права, только она не упомянула, что, даже указав значение константы прямо в определении класса, всё равно придётся делать определение этой константы как обыкновенного static-data-member...
На самом деле это требование только к тому, чтобы компилятор выделил память под константную переменную. Для простых типов можно просто написать в хедере например:
static const int sm_size = 16;
и дальше использовать эту константу, только нельзя использовать операции со взятием адреса этой константы ( &sm_size, передача в функцию, где аргумент передаётся по ссылке) - адреса у этой константы нет, так как ей не выделена память. Чем-то напоминает C-ный:
#define sm_size 16
(но не полный аналог).
Для Игоря:
Например в gcc подобный код компилируется:
class AnyClass {
...
static const int sm_size = 16;
char a[ sm_size ];
...
};

Анонимный комментирует...

по поводу перевода mutator'а - просто используйте нормальные словари.

http://www.multitran.ru/c/m.exe?l1=2&l2=1&s=mutator

по математической, технической и программной тематике лучше мультитрана я пока не нашёл словаря

Анонимный комментирует...

Очень хорошеее обяснение по мелодам-консантам

Анонимный комментирует...

Если в классе присутствуют две функции отличающиеся только const, то как знать какая когда будет визвана?

Alena комментирует...

Если в классе присутствуют две функции отличающиеся только const, то как знать какая когда будет визвана?

Очень может быть, что компилятор выдаст сообщение об ошибке "ambiguous call to a function"

Саша К. комментирует...

>> В классе могут присутствовать две функции отличающиеся только const.
>> class CFoo
>> {
>> ...
>> public:
>> int func () const;
>> int func ();
>> };

Как компилятор выбирает одну из них?

Alena комментирует...

2Саша К.

const для const объекта и не const для не const объекта. Примеры тут: http://www.parashift.com/c++-faq/const-overloading.html

Анонимный комментирует...

class Cat
{
public:
Cat (int InitialAge);
~Cat();
int GetAge();
void SetAge (int Age);
void Meow();
private:
int itsAge;
};


когда я в msvc++ 6.0 пытаюсь определить GetAge или Meow, как const - выдаёт ошибку, что дескать не находит перегруженный вариант функции. Хотя в книжке, "как бы", написано, что всё должно быть на ура. Пришлось обойтись без const.

Unknown комментирует...

Добрый день. У меня такой вопрос, а как быть с ситуацией на подобии этой:


static const unsigned short Crc16Table[256] = {
0x0000, 0xC0C1, 0xC181, ... //Не стал копировать всю таблицу ибо содержимое к задаче отношения не имеет
};


Собственно вышеприведенный код не компилируется (gcc какой-то там версии). Я не понимаю, почему unsigned short int вдруг оказывается "non-integral type"
error: invalid in-class initialization of static data member of non-integral type 'const short unsigned int [256]'

Alena комментирует...

2Антон Белоусов

Why I can't initialize non-const static member or static array in class? http://stackoverflow.com/questions/9656941/why-i-cant-initialize-non-const-static-member-or-static-array-in-class

Levon комментирует...

то есть const int CFoo::num = 20; нужно писать первой строчкой(или порядок не важен?) в соответствующем cpp-файле? я сам использую такой вариант, вроде работает:
class CFoo
{
public:
enum{ num = 20 };
};
еще слышал, что при записи
class CFoo
{
public:
static const int num = 50;
};
обязательно нужно добавить магическую строчку
const int CFoo::num;
иначе операция взятия адреса сфейлит, глупости все это?

Анонимный комментирует...

// Example program
#include
int main()
{
const int i =10;
const int* p =&i;
*((int*)(p)) =20;

std::cout<< *((int*)(&i))<<" "<< i<< " "<< &i << " " << p <<"\n";
}

output:
20 10 0x7ffdccf7cbdc 0x7ffdccf7cbdc

Два значения по одному адресу? - почему?