为啥复制构造函数不需要检查输入对象是不是指向自身?

Posted

技术标签:

【中文标题】为啥复制构造函数不需要检查输入对象是不是指向自身?【英文标题】:Why does copy constructor not need to check whether the input object pointers to itself or not?为什么复制构造函数不需要检查输入对象是否指向自身? 【发布时间】:2020-09-17 21:17:12 【问题描述】:

正如下面的代码,复制赋值运算符必须检查输入对象是否指向自身。我想知道为什么复制构造函数不需要做同样的检查。

我是 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;
        
    ;

【问题讨论】:

因为复制构造的时候,构造出来的对象不可能是被复制的那个……可以测试,但是这种情况永远不会发生。 @IgorR。但是存储对未初始化对象的引用是允许的,不是吗?我认为是。 @HolyBlackCat 如何获得对未初始化对象的引用? @user207421 很简单,将它传递给它自己的复制ctor。 :PMyClass foo(foo);. @IgorR。我们可以称其为“未初始化的存储”,但重点仍然存在。 【参考方案1】:

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

将尚未构造的对象作为参数传递给它自己的复制(或移动)构造函数是不正常的。虽然本身不​​是未定义的行为1,但没有充分的理由这样做,而且通常不会发生。这可能是偶然发生的,或者如果有人故意试图破坏您的课程。

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

但如果您想要一些额外的安全性,没有什么能阻止您这样做:

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


1[basic.life]/7 似乎允许这样做,只要您不访问尚未构建的对象本身。允许使用& 获取地址。

【讨论】:

【参考方案2】:

假设您有像 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,检查这两个 Stormtrooper 是否已经相同(通常是)很有用。这样可以避免 clone() 操作,因为 Stormtroopers 已经是相同的了。

但是,如果您想创建一个新的 Stormtrooper,并且希望他与另一个 Stormtrooper 相同(像往常一样),您可以复制构造它,在这种情况下,clone() 操作将正确执行。

通过这种方式,您可以很容易地创建一整支冲锋队。

【讨论】:

以上是关于为啥复制构造函数不需要检查输入对象是不是指向自身?的主要内容,如果未能解决你的问题,请参考以下文章

不检查复制赋值运算符是不是将对象分配给自身真的安全吗?

c++的复制构造函数

为啥我们需要复制构造函数以及何时应该在 java 中使用复制构造函数

复制构造函数2——深入理解

为啥 C++ 复制构造函数必须使用 const 对象?

Java拷贝构造函数初尝试