指针和引用作为const对象的成员变量

5

以下代码可以正常编译。不过我想知道它是否符合C++标准。更具体地说,如果我有一个const对象,我能否通过该对象的指针/引用修改变量?

class Foo {
public:
    int* a;
    int& b;
    Foo(int* _a, int& _b) : a(_a), b(_b) {}
};

int main ( int argc, char* argv[] ) {
    int x = 7;
    const Foo bar(&x, x);

    *bar.a = 3; //Leagal?
    bar.b = 7; //Legal?
    return 0;
}
2个回答

8
这是合法的,因为类的const性意味着类成员是常量。a是一个指针,所以指针指向的地址是常量,但存储在该地址的值不一定是常量。因此,bar.a实际上是一个int * const,而不是int const *。
由于在初始化后,引用无法指向另一个实体,因此对于bar.b来说,bar是否声明为const并不重要。
指针的常量变体是常量指针,而不是指向常量的指针。引用的常量变体是引用,而不是指向常量的引用。
小插曲:不管怎样,与const相关的成员引用应该小心,因为以下内容可能会编译通过。
struct Y { int m_a; };
struct X {
   const Y & m_y;
   X (const Y & y) : m_y (y) { }
};

Y y;
y.m_a = 1;
X x (y); // or const X x (y) -- does not matter
// X.m_y.m_a == 1

y.m_a = 2;
// now X.m_y.m_a == 2, although X.m_y is supposed to be const

由于可以将指向非const的指针分配给指向const的指针,因此您可以使用指针构建类似的示例。请记住,const仅保证您不会通过此变量修改变量本身,它不能保证变量内容不被修改。


3
我认为在C++中理解const的最佳方式是它保护了对象的物理位。但它对所引用的对象没有真正的保护。你可以使用const方法来控制对象定义,提供更深层次的值保护,但默认情况下,C++只保护物理对象本身。
C++允许您通过const值修改指针的内容之一的原因是它实际上无法阻止您这样做。想象一下,如果C++禁止了这种特定的构造方式,您可以轻松地通过以下方式违反它。
size_t t1 = (size_t)bar.a; // legal 
int* t2 = (int*)t1;        // legal 
*t2 = 3;                   // exactly the same as *bar.a = 3

@aschepler 正确,您可以使用技巧来赋予更深层次的const语义,但默认情况下它只是保护物理位。 - JaredPar
使用C风格的强制类型转换,无需经过size_t中间变量。您可以简单地说int *t = (int *)bar.a;,这将执行一个const_cast(当然,假设bar.a最初是指向常量的指针,这是假设)。消除常量性的能力并不是它一开始不存在的好理由。 - Adam H. Peterson
@AdamH.Peterson 我认为这代表了一个有趣的观点。指针实际上只是数字,在C/C++中允许指针和数字之间的简单转换(因为C允许了这样做)。鉴于两种格式之间的简单转换,通过const来保护指针似乎有点荒谬,因为很容易被违反。 - JaredPar
按照这个逻辑,@JaredPar,指针上的const修饰符甚至const本身都是荒谬的,因为你总是可以将其强制转换掉,并且你可以获得(几乎)任何对象的指针,然后取消const。我认为关于指针的const规则更好的理由是指向一个对象是一种关联而不是隐含的所有权,因此不应该是深层次的。 - Adam H. Peterson
@AdamH.Peterson 我同意逻辑也适用于原始指针。我知道这不是语言的本意。我更多地是试图为 OP 提供一个例子,展示由 C 继承的语义使得指针上的 const 有多脆弱。 - JaredPar
显示剩余2条评论

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