为什么拷贝构造函数不需要检查输入对象是否指向自身?

3
如下面的代码所示,复制赋值操作符必须检查输入对象指向自身还是其他位置。我想知道为什么复制构造函数不需要进行相同的检查。
我是C++新手,希望能得到这个问题的帮助。
  class rule_of_three
    {
        char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block

        void init(const char* s)
        {
            std::size_t n = std::strlen(s) + 1;
            cstring = new char[n];
            std::memcpy(cstring, s, n); // populate
        }
     public:
        rule_of_three(const char* s = "") { init(s); }

        ~rule_of_three()
        {
            delete[] cstring;  // deallocate
        }

        rule_of_three(const rule_of_three& other) // copy constructor
        { 
            init(other.cstring);
        }

        rule_of_three& operator=(const rule_of_three& other) // copy assignment
        {
            if(this != &other) {
                delete[] cstring;  // deallocate
                init(other.cstring);
            }
            return *this;
        }
    };

1
因为当你通过复制构造时,被构造的对象不能是被复制的那个对象...你可以测试,但这种情况永远不会发生。 - Jean-Baptiste Yunès
@IgorR。但是存储对未初始化对象的引用是允许的,不是吗?我认为是的。 - HolyBlackCat
2
@user207421 简单,将其传递给自己的复制构造函数。 :P MyClass foo(foo);. - HolyBlackCat
@IgorR。我们可以称之为“未初始化存储”,但重点仍然存在。 - HolyBlackCat
顺便提一下 - 看起来你正在这个 cstring 类中处理所有指针管理。如果你用 unique_ptr(或者根据你的需求使用 shared_ptr),很多内存管理的“工作”都可以省去。因为 unique ptr 可以重新分配(删除旧实例)并在销毁时也会整理好。 - code_fodder
显示剩余3条评论
2个回答

3

自我赋值有时会发生,这是类的正常使用的一部分。

将一个未构造完成的对象作为参数传递给它自己的复制(或移动)构造函数并不正常。虽然本质上不是未定义的行为1,但没有什么好的理由去这样做,通常也不会这样发生。这可能是出于意外,或者如果有人故意尝试破坏你的类。

因此,传统上的复制(和移动)构造函数不会检查 &other != this

但如果您想要一些额外的安全保障,没有任何阻止您这样做:

rule_of_three(const rule_of_three& other) // copy constructor
{ 
    assert(&other != this);
    init(other.cstring);
}

1 看起来[basic.life]/7允许这样做,只要不访问尚未构建的对象本身即可。使用&获取其地址是被允许的。


0

假设你有类似于Stormtroopers(冲锋队员)的简单对象:

class Stormtrooper
    {
        char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block

        void clone(const char* s)
        {
            std::size_t n = std::strlen(s) + 1;
            cstring = new char[n];
            std::memcpy(cstring, s, n); // populate
        }
     public:
        Stormtrooper(const char* s = "I am a Stormtrooper clone") { clone(s); }

        ~Stormtrooper()
        {
            delete[] cstring;  // deallocate
        }

        Stormtrooper(const Stormtrooper& other) // copy constructor
        { 
            clone(other.cstring);
        }

        Stormtrooper& operator=(const Stormtrooper& other) // copy assignment
        {
            if(this != &other) {
                delete[] cstring;  // deallocate
                clone(other.cstring);
            }
            return *this;
        }
    };

如果您想将一个Stormtrooper分配给另一个人,检查这两个Stormtrooper是否已经相同(通常是这样)非常有用。通过这种方式,您可以避免clone()操作,因为Stormtrooper已经相同。
但是,如果您想创建一个新的Stormtrooper,并且希望他与另一个Stormtrooper相同(通常情况下),则可以进行复制构造,并且在这种情况下,clone()操作将被正确执行。
通过这种方式,您可以轻松创建整个Stormtrooper军队。

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