简体   繁体   English

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

[英]C++ : Implementing copy constructor and copy assignment operator

After reading about copy constructors and copy assignment operators in C++, I tried to create a simple example. 在阅读了C ++中的复制构造函数和复制赋值运算符之后,我尝试创建一个简单的例子。 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. 能不能指出是否有任何错误/改进或更好的例子来理解相关概念。

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;
}

Your copy assignment operator is implemented incorrectly. 您的副本分配运算符未正确实现。 The object being assigned to leaks the object its base points to. 被分配的对象泄漏其base指向的对象。

Your default constructor is also incorrect: it leaves both base and var uninitialized, so there is no way to know whether either is valid and in the destructor, when you call delete base; 你的默认构造函数也是不正确的:它使basevar都没有初始化,因此当你调用delete base;时,无法知道是否有效以及在析构函数中delete base; , Bad Things Happen. ,糟糕的事情发生。

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 . 实现复制构造函数和复制赋值运算符以及知道您已正确完成的最简单方法是使用复制和交换习惯用法

Only Foobar needs a custom copy constructor, assignment operator and destructor. 只有Foobar需要自定义复制构造函数,赋值运算符和析构函数。 Foobase doesn't need one because the default behaviour the compiler gives is good enough. Foobase不需要一个,因为编译器提供的默认行为足够好。

In the case of Foobar you have a leak in the assignment operator. Foobar的情况下,您在赋值运算符中有泄漏。 You can easily fix it by freeing the object before allocating it, and that should be good enough. 您可以通过在分配对象之前释放对象来轻松修复它,这应该足够好了。 But if you ever add a second pointer member to Foobar you will see that that's when things get complicated. 但是如果你曾经向Foobar添加第二个指针成员,那么当事情变得复杂时你就会看到它。 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. 当你添加更多的指针成员时,事情变得比多项式更复杂。

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 Foobar doesn't default-initialize the members. 此外, Foobar的默认构造函数不会默认初始化成员。 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. 成员指针指向任意地址,int具有任意值。 Now if you use the object the constructor created you are very near Undefined Behaviour Land. 现在,如果您使用构造函数创建的对象,则非常接近Undefined Behavior Land。

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 delete in your code (Experts see point 2) 不要在代码中调用delete (专家见第2点)
  2. Don't call delete in your code (you know better...) 不要在你的代码中调用delete (你知道的更好......)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM