我最近重新访问了复制构造函数,赋值运算符,复制交换idom,如下所示:
What is the copy-and-swap idiom?
和许多其他地方-

上面的链接是一个很好的帖子-但我还有其他问题-
在stackoverflow和许多其他站点上的很多地方都回答了这些问题,但是我还没有看到很多一致性-

1-在我们为拷贝构造函数中的深层拷贝分配新内存的区域周围,是否应该有try-catch? (我已经双向看过了)

2-关于复制构造函数和赋值运算符的继承,何时应调用基类函数,何时应将这些函数虚拟化?

3-std::copy是在复制构造函数中复制内存的最佳方法吗?我已经用memcpy看到过它,并且看到其他人说memcpy是地球上最糟糕的事情。

考虑下面的示例(感谢所有反馈),它提示了一些其他问题:

4-我们应该检查自我分配吗?如果是这样的话

5-非主题问题,但我看到swaped用作:std::copy(Other.Data,Other.Data + size,Data);应该是:std::copy(Other.Data,Other.Data + (size-1),Data);如果交换是从“第一个到最后一个”,第0个元素是Other.Data?

6-为什么注释掉的构造函数不起作用(我必须将size更改为mysize)-假设这意味着无论我编写它们的顺序如何,构造函数始终会首先调用分配元素?

7-对我的实现方案还有其他意见吗?我知道代码没有用,但我只是想说明一点。

class TBar
{

    public:

    //Swap Function
    void swap(TBar &One, TBar &Two)
    {
            std::swap(One.b,Two.b);
            std::swap(One.a,Two.a);
    }

    int a;
    int *b;


    TBar& operator=(TBar Other)
    {
            swap(Other,*this);
            return (*this);
    }

    TBar() : a(0), b(new int) {}                //We Always Allocate the int

    TBar(TBar const &Other) : a(Other.a), b(new int)
    {
            std::copy(Other.b,Other.b,b);
            *b = 22;                                                //Just to have something
    }

    virtual ~TBar() { delete b;}
};

class TSuperFoo : public TBar
{
    public:

    int* Data;
    int size;

    //Swap Function for copy swap
    void swap (TSuperFoo &One, TSuperFoo &Two)
    {
            std::swap(static_cast<TBar&>(One),static_cast<TBar&>(Two));
            std::swap(One.Data,Two.Data);
            std::swap(One.size,Two.size);
    }

    //Default Constructor
    TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[mysize]) {}
    //TSuperFoo(int mysize = 5) : TBar(), size(mysize), Data(new int[size]) {}                *1

    //Copy Constructor
    TSuperFoo(TSuperFoo const &Other) : TBar(Other), size(Other.size), Data(new int[Other.size])        // I need [Other.size]! not sizw
    {
            std::copy(Other.Data,Other.Data + size,Data);        // Should this be (size-1) if std::copy is First -> Last? *2
    }

    //Assignment Operator
    TSuperFoo& operator=(TSuperFoo Other)
    {
            swap(Other,(*this));
            return (*this);
    }

    ~TSuperFoo() { delete[] Data;}

};

最佳答案

  • 如果分配内存,则需要确保在引发异常的情况下将其释放。您可以使用显式的try/catch来执行此操作,也可以使用诸如std::unique_ptr之类的智能指针来保存内存,当智能指针被堆栈解卷破坏时,该内存将被自动删除。
  • 您很少需要virtual赋值运算符。在成员初始化列表中调用基类拷贝构造函数,如果要进行成员分配,则首先在派生的赋值运算符中调用基类赋值运算符-如果您要进行复制/交换,则无需调用您的派生赋值运算符中的基类赋值,前提是正确实现了 copy-and-swap 。
  • std::copy适用于对象,并将正确调用拷贝构造函数。如果您有普通的POD对象,则memcpy也可以正常工作。尽管在大多数情况下,我还是会选择std::copy的---无论如何它都应该针对POD在后台进行优化,使其成为memcpy,并且避免了以后再添加复制构造函数时可能出现的错误。

  • [更新更新的问题]
  • 编写了copy/swap之后,就不需要检查自我分配了,确实没有办法---到您输入赋值运算符other时,它就是拷贝,您无法知道源对象是什么。这仅意味着自我分配仍将执行复制/交换。
  • std::copy将一对迭代器(first,first + size)作为输入。这允许空范围,并且与标准库中的每个基于范围的算法相同。
  • 注释掉的构造函数不起作用,因为无论成员初始化程序列表中的顺序如何,都按照声明的顺序初始化成员。因此,Data总是首先被初始化。如果初始化依赖于size,则它将获得duff值,因为size尚未初始化。如果您交换sizedata的声明,则此构造函数将正常工作。好的编译器会警告成员初始化的顺序与声明的顺序不匹配。
  • 关于c++ - 复制构造函数和赋值运算符的实现选择-,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11208772/

    10-11 22:57
    查看更多