在const方法中调用非const方法的成员

8

我很惊讶地发现“const”中存在这个“漏洞”:

#include <stdio.h>

class A
{
  int r ;
public:
  A():r(0){}

  void nonconst()
  {
    puts( "I am in ur nonconst method" ) ;
    r++;
  }
} ;

class B
{
  A a ;
  A* aPtr ;

public:
  B(){ aPtr = new A() ; }

  void go() const
  {
    //a.nonconst() ;      // illegal
    aPtr->nonconst() ;  //legal
  }
} ;

int main()
{
  B b ;
  b.go() ;
}

所以基本上从const方法B::go(),如果通过指针引用类型为A的对象,则可以调用非常量成员函数(称为nonconst())。

为什么会这样呢?似乎是个问题(在我的代码中确实是这样的)。


这相当出乎意料。我在Mac上使用g++ 4.2.1运行它,代码编译通过了(让我非常惊讶)。我很好奇这个问题的答案。 - Sergey Kalinichenko
1
这种行为在第一次遇到时通常会让人感到惊讶。不久前,我曾经提出了这个问题,以了解是否有将constness传播到指向对象的解决方案。 - Luc Touraille
3个回答

11

当类型为B的对象是const时,那么它的所有成员都是const,这意味着在B::go()的持续时间内,其两个成员实际上是

A const a;
A * const aPtr;
第一个是类型为A的常量对象,只能调用const成员函数。然而,第二个是指向非常量A的常量指针。在B::go()函数中,您不能合法地说aPtr = ,因为那将修改常量aPtr。
指向常量A的指针将声明为A const* aPtr或const A* aPtr,这将使调用非常量A::nonconst()不合法。

啊,非常聪明的回答。有点学究气。 - bobobobo

6

“const” 对象的不可变性不能通过指针扩展到其他对象。在您的示例中,“const” 部分是整个对象 a,或者是指针 aPtr。由于 aPtr 是 A * 而不是 const A *,因此可以调用非 const 方法。

如果您更改

A* aPtr ;

为了

const A* aPtr ;

那么您将无法调用aPtr->nonconst()


嗯...如果您需要使用它的非const功能,那么这将使对象A几乎无用。我认为标记方法为const是不改变对象状态信息的契约...我认为__所有__成员都应该被标记为const,甚至是指针!我有一个dirty位,每当调用非const方法时就会设置它。但现在,即使一些const方法也应该设置dirty位!真是个惊喜。 - bobobobo
2
当一个对象是const时,它的所有成员也都是const。在const方法中,您不能更改aPtr指向的内容。但是,当前对象被视为const并不会影响任何其他对象的const属性。 - Greg Hewgill
“@bobobobo 我认为所有成员都应该被标记为const” 是的,当然。但这并不意味着其他对象也是const的,希望如此。 - curiousguy

0

const的语言定义

我很惊讶地发现了这个“const”中的“漏洞”:

没有。

const 均匀地应用于所有类成员:在类 Cconst 成员函数中,this 的类型为 const C *,因此对于声明为类型 T 的成员 C::mem

class C {
// ...
    T mem;
};

this->mem 的类型为 const T

请根据类型来确定所有成员的声明类型 T 和相应的 const 限定类型。

看起来像是个问题(在我的代码中确实是这样,我发现了它)。

仅仅因为“规则的系统应用”没有做到你期望的事情,并不意味着规则有问题,而是你的期望有问题。

你应该写下你的期望,看看你是否期望对不同类型进行非统一的 const 应用。

编程时,你必须进行逻辑推理。当没有逻辑原因时,你应该推断事物,而不是期望它们。

正确使用 const

为什么呢?

你的类被称为A和B,很难理解什么构成了逻辑状态,什么不构成。;) 你提出了一个“道德”问题(不仅仅是关于合法/非法C++程序的问题),而你的代码片段没有“道德”价值。如果你实际上发布相关代码,我们可能会对其进行一些“道德”判断。
逻辑状态
你应该声明那些不改变所应用对象的“逻辑状态”的函数为const。
这意味着你必须定义你的类实例的“逻辑状态”是什么:它是一个抽象概念,只有你可以定义,因为它是一个高级概念。“逻辑状态”与你的类应该解决的问题相关。 然后你可以区分哪些变量对逻辑状态有贡献:*(b.aPtr)对b的逻辑状态有贡献吗?
密切相关的问题
你知道拷贝构造函数吗?
关于拷贝赋值运算符呢?
关于析构函数呢?

实际上,如果投票者能够添加某种解释,那将会有所帮助。 - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接