C ++,正确的复制操作符重载

时间:2013-10-31 17:34:05

标签: c++ copy operators overloading

我想将我的复制操作符重定向到我的复制构造函数。在后者的位置,我实现了基于旧的avaialble类复制/构造新类的正确逻辑。

然而,如何正确地做到这一点?我“想”这个可能会泄漏内存,但我不知道怎么做而不通过指针:

MyClass& MyClass::operator=(const MyClass& a) {
    MyClass* b = new MyClass(a); 
    return *b; 
}

这样好吗?如果不是,那么正确的方法是什么?我应该更改方法的主体还是原型?

谢谢。

6 个答案:

答案 0 :(得分:2)

不,operator=应将当前对象属性设置为与分配的对象相同。您的方法在堆上分配一个新对象,将其作为引用返回(基本上将其泄漏),并使调用操作符的对象完全保持不变。

您应该实现一个名为CopyFrom()的方法,该方法分配所有对象的属性以匹配传入对象的属性(深度复制任何堆分配的指针,其生命周期由MyClass管理)然后从您的复制构造函数和您的operator=调用THAT。

    class MyClass
    {
    public:
        MyClass( const MyClass& in )
        {
            CopyFrom( in );
        }

        MyClass& operator=( const MyClass& in )
        {
            CopyFrom( in );
            return *this;
        }

    private:
        void CopyFrom( const MyClass& in )
        {
            ... copies in's attributes into self.
        }
    };

答案 1 :(得分:1)

除非您在MyClass中存储指针,否则正确的复制赋值运算符是默认生成的运算符。但是,如果你需要实现一个,你可以通过copy-swap惯用语来复制构造函数:

MyClass& MyClass::operator = (MyClass const& a) {
    MyClass temp(a); // Call the copy constructor
    using std::swap;
    swap(temp, *this);
    return *this;
}

using std::swap的原因是启用依赖于参数的查找。如果为MyClass定义自己的交换函数,则会调用它。否则std::swap将用作后备。 (编辑:在这种情况下,你确实需要实现自定义交换,否则你将获得无限递归。std::swap将使用赋值运算符,它将调用std::swap,它将调用。 ..)

这个成语很受欢迎的原因是因为std::swap是一个无投掷功能。如果你的拷贝构造函数抛出异常,那么你分配的对象仍处于有效状态。

答案 2 :(得分:1)

通常,复制赋值运算符永远不应创建副本。相反,它应该将数据复制到它所调用的现有对象(赋值的左侧)。例如:

class MyClass
{
public:
    MyClass & operator = (const MyClass & RHS)
    {
        // Copy data from RHS into 'this'
        m_value = RHS.m_value;
        return *this;
    }

private:
    int m_value;
};

在这种情况下,不需要定义自己的复制构造函数,因为默认(编译器提供的)工作正常。这只是一个例子。

不幸的是,您无法在现有对象上调用复制构造函数。复制交换模式是一种替代方案,但效率可能较低。

答案 3 :(得分:0)

“正确的方法”是像赋值运算符一样实现赋值运算符:修改调用运算符的对象的内容并返回对它的引用。

您当前的实现将导致内存泄漏,并且不执行任何赋值(这是赋值运算符的主要点)。

如果你只想编写一次赋值代码,并且你的类没有在构造函数中分配内存,你可以这样做:

    MyClass::MyClass(const MyClass& a) {
        *this = a;
    }

    MyClass& MyClass::operator=(const MyClass& a) {
        if (&a == this)
            return *this;
        // Do assignment
        return *this;
    }

但我不推荐它。

答案 4 :(得分:0)

您的代码完全错误(抱歉)!赋值运算符 不分配任何东西,但分配一个指向MyClass对象的指针, 造成内存泄漏。我的建议:避免指针或使用一些 智能指针(shared_ptr,unique_ptr),但这只是一个旁注。

也许这很有帮助:

#include <iostream>
#include <limits>

class X
{
    public:
    X(std::size_t n)
    :   m_size(n), m_data(new int[n])
    {
        std::cout << "Construct" << std::endl;
    }

    ~X()
    {
        std::cout << "Destruct" << std::endl;
        delete [] m_data;
    }

    // Exception safe assignment.
    // Note: I am passing by value to enable copy elision and
    //       move semantics.
    X& operator = (X x) {
        std::cout << "Assign" << std::endl;
        x.swap(*this);
        return *this;
    }

    void swap(X& x) {
        std::swap(m_size, x.m_size);
        std::swap(m_data, x.m_data);
    }

    std::size_t size() const { return m_size; }

    private:
    std::size_t m_size;
    int* m_data;
};

int main()
{
    X x(1);
    try {
        x = X(2);
        // To provoke an exception:
        std::size_t n = std::numeric_limits<std::size_t>::max();
        x = X(n);
    }
    catch(...) {
        std::cout << "Exception" << std::endl;
    }
    std::cout << "Size: " << x.size() << std::endl;
    return 0;
}

答案 5 :(得分:-1)

如果您绝对想要通过复制构造函数实现赋值运算符,请使用以下命令:

MyClass& MyClass::operator=(const MyClass& o)
{
    this->~MyClass(); // destroy current object
    new(this) MyClass(o); // use the copy constructor
    return *this;
}

我无法想到这是最好的事情(其他答案描述了在某些情况下更好的实施方式)。

如果MyClass包含数百个int / float字段,并且有几个动态分配的指针,那么可能(只是试图在这里做一些事情)?

  • 在构造函数和赋值运算符中复制它们太繁琐且容易出错
  • 构造函数和赋值运算符调用的
  • Having a copying function - 不理想,因为指针必须先设置为NULL
  • 上面的代码 - 无需额外工作即可使用!

然而,不鼓励在你的班级中使用裸(非智能)指针。如果你有这样一个类,那么你有比非工作赋值运算符更糟糕的问题 - 你必须先重构,问题就会消失,再加上所有其他错误。