C ++,类,常量和奇怪的语法

| 我今天正在重新阅读c ++入门(第四版)-有关成员函数和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的POINT不是为了使您无法修改值吗?     
已邀请:
我会说托尼(Tony)标记为正确的答案是错误的,而迈克尔·伯尔(Michael Burr)的答案是正确的。 更明确地说出他的话(至少对我来说): 有两个可能引起误解的地方: 隐式const的工作方式 在构造const对象期间解释
this
的方式 1.隐式常量 隐式const(当成为
const
时在
ConstCheater
内部的含义)不会将
cc
变成
pointer-to-const
而是变成
const-pointer
,也就是说,当您执行此操作时:
  const ConstCheater cc(7); 
内部来自:
  ConstCheater * ccp;
...至...
  ConstCheater * const ccp;
而不是...
  const ConstCheater * ccp;    
这可能是预期的。 2.
const
物体的构造 不过,更奇怪的是,允许将
this
传递给构造函数中的
cpp
\的初始值设定项,因为人们认为ѭ1should应被视为
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; 
因此该对象在构造过程中实际上不是常量。 我不确定我是否完全理解其原因,但迈克尔·伯尔(Michael Burr)指出,提供预期行为似乎存在逻辑和技术障碍,因此该标准似乎可以消除当前有些奇怪的行为。 我最近问了一个相关的问题:为什么C ++没有const构造函数?但是到目前为止,我还没有真正完全理解为什么它站不住脚的原因,尽管我认为这将给C ++开发人员带来负担,他们必须为他们想要创建const对象的任何类定义一个笨拙的const构造函数。 。     
即使
getccp()
const
方法,它也不保证您对返回的引用进行操作。该方法本身不会修改对象,因此不会违反规则。 如果返回a24ѭ,那就不一样了。 如您的示例所示,
const
的复杂性远不只是将其应用于对象。 C ++常见问题解答中有一个有关const正确性的部分,尤其涉及您在此处强调的情况。     
允许构造函数修改“ 3”对象的值,是的。但是,如果不是,那该怎么办呢? 由于构造函数具有这种访问权限,因此可以将其“转发”给其他人或“保存”以供以后使用。当然,这样做可能不是一个好主意。 在这种情况下,C ++的安全机制不会阻止您构建格式错误的程序。 C ++并非万无一失。因此,请小心!     
在构造函数完成其操作之前,对象不会变为const。因此,“ 1”是存储非常量内存的指针,但此后不久便发生变化。这就是为什么它首先允许分配的原因,因为您没有做错任何事情。这意味着ѭ13是指向const的指针,但是编译器没有意识到这一点。它没有办法;毕竟,您将其声明为非常量。这仍然是未定义的行为,不是您的编译器真正希望能够帮助您捕获的类型。     
真正的问题不是ѭ29的行为-行上没有错误:
const ConstCheater cc(7);
它使用应为constwith1ѭ指针的指针初始化非const指针。但是,构造函数不能为
const
(9.3.2 / 5,但是稍加思考,就应该清楚为什么)。因此,允许构造函数使用指向const对象(或即将成为const的对象)的指针来初始化非const指针。那就是你在开车的洞。 关于为什么允许它,我想标准很难尝试去封闭漏洞,因为它必须枚举构造函数的ѭ1all必须被视为
const
的所有方式以及所有方式。构造const对象时,必须将其视为“ 35”。这似乎是一项艰巨的任务。     
您的对象不是完全不变的:它只是您创建的指向它的引用,它是恒定的。     
您正在制作
const
是指
ConstCheater
ConstCheater
中的任何内容都不会使
value
成为
const
。     
const
限定符限制在对象上调用非const方法,因此问题是您的设计允许您通过const方法为成员提供非const引用。 常见的方法是
      Member& getccp()       {return *member;}
const Member& getccp() const {return *member;} 
在某些情况下,如果对象的逻辑一致性不受其成员的外部修改的影响,则可以允许
      Member& getccp() const {return *member;}
逻辑和形式常数差异的另一个例子是
mutable
成员。即mutable可以是在最后一个“ 3”方法调用时计算的术语,如果您在下一次调用时获得相同的输入,则可以轻松地返回存储的值。     

要回复问题请先登录注册