C++:实现复制构造函数和复制赋值运算符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4700991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:10:43  来源:igfitidea点击:

C++ : Implementing copy constructor and copy assignment operator

c++copy-constructor

提问by blitzkriegz

After reading about copy constructors and copy assignment operators in C++, I tried to create a simple example. Though the below snippet apparently works, I am not sure whether I am implementing the copy constructor and copy assignment operator the right way. Could you please point out if there are any mistakes/improvements or a better example to understand the relevant concepts.

在阅读了 C++ 中的复制构造函数和复制赋值运算符之后,我尝试创建一个简单的示例。尽管下面的代码片段显然有效,但我不确定我是否以正确的方式实现了复制构造函数和复制赋值运算符。您能否指出是否有任何错误/改进或更好的示例来理解相关概念。

class Foobase
{
    int bInt;

public:
    Foobase() {}

    Foobase(int b) { bInt = b;}

    int GetValue() { return bInt;}

    int SetValue(const int& val) { bInt = val; }
};


class Foobar
{
    int var;    
    Foobase *base;      

public:
    Foobar(){}

    Foobar(int v)
    {
        var = v;        
        base = new Foobase(v * -1);

    }

    //Copy constructor
    Foobar(const Foobar& foo)
    {       
        var = foo.var;
        base = new Foobase(foo.GetBaseValue());
    }

    //Copy assignemnt operator
    Foobar& operator= (const Foobar& other)
    {
        if (this != &other) // prevent self-assignment
        {
            var = other.var;
            base = new Foobase(other.GetBaseValue());

        }
        return *this;
    }

    ~Foobar()
    {
        delete base;
    }

    void SetValue(int val)
    {
        var = val;
    }

    void SetBaseValue(const int& val)
    {
        base->SetValue(val);
    }

    int GetBaseValue() const
    {
        return(base->GetValue());
    }

    void Print()
    {
        cout<<"Foobar Value: "<<var<<endl;
        cout<<"Foobase Value: "<<base->GetValue()<<endl;

    }   

};

int main()
{
    Foobar f(10);       
    Foobar g(f);  //calls copy constructor
    Foobar h = f; //calls copy constructor

    Foobar i;
    i = f;

    f.SetBaseValue(12);
    f.SetValue(2);    

    Foobar j = f = z; //copy constructor for j but assignment operator for f

    z.SetBaseValue(777);
    z.SetValue(77);

    return 1;
}

采纳答案by James McNellis

Your copy assignment operator is implemented incorrectly. The object being assigned to leaks the object its basepoints to.

您的复制赋值运算符未正确实现。分配给的对象会泄漏其base指向的对象。

Your default constructor is also incorrect: it leaves both baseand varuninitialized, so there is no way to know whether either is valid and in the destructor, when you call delete base;, Bad Things Happen.

您的默认构造函数也是不正确的:它同时保留basevar未初始化,因此无法知道其中一个是否有效并且在析构函数中,当您调用时delete base;,坏事发生了。

The easiest way to implement the copy constructor and copy assignment operator and to know that you have done so correctly is to use the Copy-and-Swap idiom.

实现复制构造函数和复制赋值运算符并知道您是否正确执行的最简单方法是使用Copy-and-Swap idiom

回答by wilhelmtell

Only Foobarneeds a custom copy constructor, assignment operator and destructor. Foobasedoesn't need one because the default behaviour the compiler gives is good enough.

Foobar需要一个自定义的复制构造函数、赋值运算符和析构函数。Foobase不需要,因为编译器给出的默认行为已经足够好了。

In the case of Foobaryou have a leak in the assignment operator. You can easily fix it by freeing the object before allocating it, and that should be good enough. But if you ever add a secondpointer member to Foobaryou will see that that's when things get complicated. Now, if you have an exception while allocating the second pointer you need to clean up properly the first pointer you allocated, to avoid corruption or leaks. And things get more complicated than that in a polynomial manner as you add more pointer members.

如果Foobar您在赋值运算符中有泄漏。您可以通过在分配对象之前释放对象来轻松修复它,这应该足够了。但是,如果您向其中添加第二个指针成员,Foobar您会发现那是事情变得复杂的时候。现在,如果您在分配第二个指针时遇到异常,您需要正确清理您分配的第一个指针,以避免损坏或泄漏。随着您添加更多指针成员,事情变得比多项式方式更复杂。

Instead, what you want to do is implement the assignment operator in terms of the copy constructor. Then, you should implement the copy-constructor in terms of a non-throwing swap function. Read about the Copy & Swap idiom for details.

相反,您想要做的是根据复制构造函数实现赋值运算符。然后,您应该根据非抛出交换函数来实现复制构造函数。有关详细信息,请阅读复制和交换习语。

Also, the default constructor of Foobardoesn't default-initialize the members. That's bad, because it's not what the user would expect. The member pointer points at an arbitrary address and the int has an arbitrary value. Now if you use the object the constructor created you are very near Undefined Behaviour Land.

此外, 的默认构造函数Foobar不会默认初始化成员。这很糟糕,因为这不是用户所期望的。成员指针指向任意地址,int 具有任意值。现在,如果您使用构造函数创建的对象,您将非常接近 Undefined Behavior Land。

回答by Matthieu M.

I have a very simple patch for you:

我有一个非常简单的补丁给你:

class Foobar
{
  int var;    
  std::unique_ptr<FooBase> base;

...

That should get you started.

这应该让你开始。

The bottom line is:

底线是:

  1. Don't call deletein your code (Experts see point 2)
  2. Don't call deletein your code (you know better...)
  1. 不要调用delete你的代码(专家见第 2 点)
  2. 不要调用delete你的代码(你更清楚......)