1

我想将我的复制运算符重定向到我的复制构造函数。在后者中,我实现了基于旧可用类复制/构造新类的正确逻辑。

但是,正确的方法是如何做到这一点?我“认为”这可能是内存泄漏,但如果不传递指针,我不知道该怎么做:

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

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

谢谢你。

4

6 回答 6

2

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

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

    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.
        }
    };
于 2013-10-31T17:43:53.940 回答
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它是一个无抛出函数。如果您的复制构造函数抛出异常,那么您分配给的对象仍处于有效状态。

于 2013-10-31T17:38:01.390 回答
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;
};

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

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

于 2013-10-31T17:44:34.320 回答
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;
}
于 2013-10-31T18:43:51.350 回答
0

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

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

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

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

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

但我不会推荐它。

于 2013-10-31T17:40:30.870 回答
0

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

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

我想不出这将是最好的任何情况(其他答案描述了在某些情况下更好的实施方式)。

MyClass如果包含数百个 int/float 字段和几个动态分配的指针,也许(只是想在这里弥补) ?

但是,不鼓励在您的类中使用裸(非智能)指针。如果你有这样一个类,那么你遇到的问题比不工作的赋值运算符要糟糕得多——你必须先重构,问题就会连同所有其他错误一起消失。

于 2013-10-31T19:15:55.770 回答