自初始化'A a = a;'是允许的吗?(涉及IT技术)

8

这段代码在复制构造函数中运行时失败。
但编译器(MSVS2008)没有发出警告。

您能解释一下(最好引用标准)这段代码是否非法或者是什么原因吗?

我知道A a = a;根本不应该被写入,但我正在寻找理论背景。

 class A
 {
 public: 

    A()
    :p(new int)
    {
    }

    A(const A& rv)
    {
        p = new int(*rv.p);
    }

    ~A()
    {
        delete p;
    }


 private:

    int *p;
 };

 int main()
 {
    A a = a;
 }

根据回答,我理解这段代码是合法的。 我应该加一个断言或者if(&rv == this) p = NULL;来确保安全吗? 有人在他们的代码中处理这个问题吗? - Yarik
4个回答

8
你的代码没有调用标准构造函数,而是调用了拷贝构造函数,因此你正在访问一个未初始化的指针。

有关这是否为编译时错误的标准引用吗? - Mehrdad Afshari
4
真的不能是编译时错误。这里有两个触发问题的条件:
  • 您的复制构造函数从源对象中取消引用指针。
  • 您正在使用自身初始化对象。
这两个问题可能在完全不同的编译单元中,编译器需要看到这两个条件才能标记出错。每个条件本身并不是问题,因此编译器没有理由抱怨。
- JohnMcG
1
@JohnMcG:我不同意......变量声明和初始化不能在不同的编译单元中。这显然毫无意义。不幸的是,C++委员会从未考虑过这一点(或者认为这不重要...无论如何)。你可以争辩说,编译器符合标准,从这个意义上来说你是正确的,但在我看来,这是标准的缺陷。 - Karoly Horvath

3
根据标准(12.6.1 [class.expl.init]),自我初始化是完全合法的。因此以下操作是合法的。
A a = a;

您只需要编写正确的复制构造函数即可处理它。

A(const A& rv)
{
    if(&rv == this) {
        p = new int(0);
        return;
    }

    p = new int(*rv.p);
}

编辑:根据评论更新代码。


嗯,“正确” - 这取决于你的意图是什么。这样做会导致未初始化的对象。 - Suma
2
请记住,当作为参数传递给自己的复制构造函数时,变量'a'并未被初始化。因此,'A::p'是未定义的,这个复制构造函数没有对其进行初始化。 - Martin York
补充一下:你回复的第一部分关于标准是正确的,但我对第二部分持怀疑态度。虽然你在这里避免了崩溃,但由于p未初始化,你很可能会在后面崩溃。 - Suma
更新了复制构造函数的代码,将p初始化为有用的内容。 - Glen
仅在赋值运算符( operator=() )中检查自我复制才有意义,而不是在复制构造函数中。请参见Fred Larson的答案。 - Dima

3

1

A a = a;绝对不应该写。但是a = a可以写。你的赋值运算符必须检查&rv == this,并在自我复制的情况下不执行任何操作。

哦,是的,你确实需要为类A编写一个赋值运算符。


抱歉,我现在已经修复了答案。 - Dima
不需要使用赋值运算符,可以通过复制构造函数进行优化替代。 - Martin York
Mark,如果你有一个A a; a = a;怎么办?默认构造函数A将被调用,然后是a::operator=()。我怀疑它不会被优化掉。 - Dima
Martin的观点是,在赋值运算符中检查自我赋值可能是必要的,以防止像这样的情况,但这并不足够。因为赋值可能被优化为复制构造函数,所以还需要考虑复制构造函数。 - JohnMcG

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