为什么允许在初始化列表中从const指向const转换为const指向非const

时间:2013-09-10 07:48:59

标签: c++ constructor const implicit-conversion

我读了发布的问题 Why does C++ not have a const constructor?

我仍然感到困惑,为什么该程序可以编译。我试图就这个问题提出我的看法,我不知道为什么它被删除了。所以我不得不再问这个问题。

这是程序

class Cheater
{
public:
    Cheater(int avalue) :
    value(avalue),
    cheaterPtr(this) //conceptually odd legality in const Cheater ctor
    {}

    Cheater& getCheaterPtr() const {return *cheaterPtr;}
    int value;
private:
    Cheater * cheaterPtr;
};
int main()
{
    const Cheater cheater(7); //Initialize the value to 7

 //   cheater.value                 = 4;    //good, illegal
    cheater.getCheaterPtr().value = 4;    //oops, legal
    return 0;
}

我的困惑是:

const Cheater cheater(7) 在构造函数

中创建一个const对象作弊者
  Cheater(int avalue) :
    value(avalue),
    cheaterPtr(this) //conceptually odd legality in const Cheater ctor
{}

'this'指针用于初始化cheaterPtr

我认为这不应该是正确的。 cheater是一个const对象,它的指针应该是这样的:const Cheater* const this;这意味着它自己的指针和指针所指向的对象都应该是const,我们既不能改变指针的值,也不能改变指针的值。修改指针指向的对象。

但是对象cheater的{​​{1}}成员类似于cheaterPtr。这意味着指针是const,但它指向的对象可能是非对象的。

我们知道,不允许指向const到指针转换为非指针转换:

Cheater* const cheaterPtr

如何在初始化列表中允许从指针到const到指针到非对象的转换?真的发生了什么?

以下是关于我试图提供给原始帖子的构造函数中“constness”的描述:

“与其他成员函数不同,构造函数可能不会被声明为const。当我们创建类类型的const对象时,在构造函数完成对象的初始化之后,对象才会假定它的'constness'。因此,构造函数可以在构造期间写入const对象。“

- C ++入门(第5版)P262 7.1.4构造函数

2 个答案:

答案 0 :(得分:3)

如果构造函数是const,则它们无法构造它们的对象 - 它们无法写入其数据!

您引用的代码为“合法:”

cheater.getCheaterPtr().value = 4;    //oops, legal

实际上并不合法。在编译时,它的行为是未定义的,因为它通过非const左值修改const对象。它与此完全相同:

const int value = 0;
const int * p = &value;
*const_cast<int*>(p) = 4;

这也会编译,但它仍然是非法的(有UB)。

答案 1 :(得分:1)

您的假设不正确。一次取一个,首先是代码注释。

class Cheater
{
public:
    Cheater(int avalue) :
        value(avalue),
        cheaterPtr(this) // NOTE: perfectly legal, as *this is non-const
                         // in the construction context.
    {}

    // NOTE: method is viable as const. it makes no modifications
    //  to any members, invokes no non-const member functions, and
    //  makes no attempt to pass *this as a non-const parameter. the
    //  code neither knows, nor cares whether `cheaterPtr`points to
    //  *this or not.
    Cheater& getCheaterPtr() const {return *cheaterPtr;}

    int value;

private:
    Cheater * cheaterPtr; // NOTE: member pointer is non-const.
};


int main()
{
    // NOTE: perfectly ok. we're creating a const Cheater object
    //  which means we cannot fire non-const members or pass it
    //  by reference or address as a non-const parameter to anything.
    const Cheater cheater(7);

    // NOTE: completely lega. Invoking a const-method on a const
    // object. That it returns a non-const reference is irrelevant
    // to the const-ness of the object and member function.    
    cheater.getCheaterPtr().value = 4;

    return 0;
}

你说:

  

我认为这不应该是正确的。 cheater是一个const对象,其指针应该类似于:const Cheater* const this

施工后

cheater const 。在构造期间它必须是非const 。此外,构造函数不会(也不能)知道调用者已指示对象将为const。所有它知道的是它构建一个对象的时间,这就是它的作用。此外,构建后{em> &cheaterconst Cheater *。实际指针变量本身const在这种情况下根本不适用。

然后......

  

...对象骗子的cheaterPtr成员类似于Cheater* const cheaterPtr;

这实际上是一种非常准确的描述方式。由于cheaterconst,其成员也是如此,这意味着cheaterPtr成员是const; 指向的内容。您无法更改指针值,但因为它不是指向const对象的指针,您可以自由地使用该指针来修改它指向的内容,在这种情况下恰好是this

如果您希望两者指针及其指向对象为const,您应该在成员列表中将其声明为const Cheater *cheaterPtr;。 (并且这样做,顺便说一句,只通过getCheaterPointer()的可变行为无效。它也必须返回const Cheater*,这当然意味着分配失败。

简而言之,此代码完全有效。您想要看到的内容(调用者 const-ness的构建意识)不是该语言的一部分,事实上,如果您希望构造函数具有...的自由度,那么事实并非如此。 ,构造