C ++, классы, Const и странный синтаксис - PullRequest
7 голосов
/ 14 апреля 2011

Сегодня я перечитывал учебник по С ++ (4-е изд.) - раздел о функциях-членах, ссылках на const и т. Д., И я придумал эту маленькую странную программу:

using std::cout;
using std::endl;

class ConstCheater
{
public:
    ConstCheater(int avalue) : ccp(this), value(avalue) {}
    ConstCheater& getccp() const {return *ccp;}
    int value;
private:
    ConstCheater* ccp;
};

int main()
{
    const ConstCheater cc(7); //Initialize the value to 7
    cout << cc.value << endl;
    cc.getccp().value = 4;    //Now setting it to 4, even though it's const!
    cout << cc.value << endl;
    cc.value = 4;             //This is illegal
    return 0;
}

Мой вопрос - почему c ++ допускает такой синтаксис? Почему я могу редактировать обычные элементы данных в классе, когда он объявлен как const? Разве ТОЧКА const не позволяет изменить значения?

Ответы [ 8 ]

6 голосов
/ 14 апреля 2011

Несмотря на то, что getccp() - это метод const, он не дает никаких обещаний относительно того, что вы делаете с возвращаемой ссылкой.Сам метод не изменяет объект и, следовательно, не нарушает правила.

Если бы он возвратил const ConstCheater&, тогда это было бы иначе.

Как показывает ваш пример, const намного сложнее, чем просто применить его к объекту.в FAQ по C ++ есть раздел const правильность и, в частности, , он охватывает случай , который вы выделяете здесь.

2 голосов
/ 05 августа 2011

Я бы сказал, что ответ Тони, который вы пометили как правильный, неправильный, а ответ Майкла Барра правильный.

Чтобы изложить то, что он сказал более четко (по крайней мере, для меня):

Есть два возможных места для недопонимания:

  1. Как работает неявное const
  2. Способ интерпретации this при создании объекта const

1.Неявное Const

Неявное const (внутренняя константа для ConstCheater, когда она сделана const) не превращает cc в pointer-to-const, а скорее const-pointer, то есть когдаВы делаете это:

  const ConstCheater cc(7); 

Внутренне идет от:

  ConstCheater * ccp;

... до ...

  ConstCheater * const ccp;

... а не ...

  const ConstCheater * ccp;    

, что, возможно, ожидалось.

2.Построение const объекта

Более странно то, что this разрешено передавать инициализатору cpp в конструкторе, поскольку this, как можно подумать, следует рассматривать какpointer-to-const, и, следовательно, не является допустимым значением для передачи const-pointer.

То есть можно ожидать:

 ...: ccp(this) ... // expected to fail but doesnt

потерпит неудачу, потому что концептуально можно ожидать, чтоэто было (несколько) эквивалентно:

 const ConstCheater         cc(7);
 const ConstCheater * const this = &cc; // const-pointer-to-const

и, таким образом, вы могли бы подумать, что:

 ConstCheater * const ccp = this; //expected error!

потерпит неудачу!Но это не так, потому что, очевидно, во время строительства очевидно, что this обрабатывается специально, как если бы это было:

 const ConstCheater * this = &cc; 

, и, таким образом, объект фактически не является постоянным во время строительства.

Яне уверен, что я полностью понимаю причину, но Майкл Барр указывает на то, что, по-видимому, существует логический и технический барьер для обеспечения ожидаемого поведения, поэтому стандарт, похоже, отражает нынешнее несколько странное поведение.

Я недавно спросилсвязанный с этим вопрос: Почему C ++ не имеет конструктора const? , но до сих пор не совсем понял причины, по которым он был бы несостоятельным, хотя я полагаю, что это обременительно для разработчиков на C ++определить неуклюжий конструктор const для любого класса, из которого они хотели бы создать объект const.

2 голосов
/ 14 апреля 2011

Реальная проблема не в поведении ConstCheater::getccp(), а в том, что в строке нет ошибки:

const ConstCheater cc(7);

, которая инициализирует неконстантный указатель с тем, что должно быть постоянным this указателем.Тем не менее, конструкторы не могут быть const (9.3.2 / 5, но если подумать, должно быть понятно почему).Таким образом, конструктору разрешается инициализировать неконстантный указатель указателем на константный объект (или объект, который «собирается стать» константой).Хотя это дыра, которую вы продвигаете.

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

2 голосов
/ 14 апреля 2011

Объекты не становятся постоянными до тех пор, пока их конструкторы не закончат свою работу. Таким образом, this - это указатель на неконстантную память, когда вы сохраняете ее, но вскоре меняется. Вот почему это позволило назначение в первую очередь, потому что вы не сделали ничего плохого. Это означает, что cpp является указателем на const, но компилятор этого не понимает. Это не имеет никакого способа; Вы объявили это неконстантным, в конце концов. Это все еще неопределенное поведение, это просто не тот тип, на который ваш компилятор действительно может надеяться помочь вам поймать.

2 голосов
/ 14 апреля 2011

Конструктор может изменять значение объекта const, да. Но если бы не было, что это могло сделать?

Поскольку конструктор имеет такой доступ, он может «переслать» его кому-либо еще или «сохранить» на потом. Конечно, это может быть плохой идеей.

Это один из случаев, когда механизмы безопасности C ++ не мешают вам создавать неправильно сформированную программу. C ++ совсем не надежен. Так что будьте осторожны!

0 голосов
/ 14 апреля 2011
Квалификатор

const ограничивает вызов неконстантных методов для вашего объекта, поэтому проблема заключается в вашем дизайне, который позволяет вам давать неконстантную ссылку на член с помощью метода const. Общий подход

      Member& getccp()       {return *member;}
const Member& getccp() const {return *member;} 

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

      Member& getccp() const {return *member;}

Другим примером различий в логической и формальной константности являются mutable члены. То есть mutable может быть некоторым термином, который был вычислен при последнем вызове const метода, если вы получите тот же самый ввод при следующем вызове, вы можете легко вернуть сохраненное значение.

0 голосов
/ 14 апреля 2011

То, что вы делаете const - это ссылка на ConstCheater.Ничто в ConstCheater не делает value a const.

0 голосов
/ 14 апреля 2011

Ваш объект не является полностью неизменным: это просто ссылка, которую вы создали, чтобы указать на нее, которая является постоянной.

...