避免在复制构造函数和operator =中重复相同的代码 [英] Avoid repeating the same code in copy constructor and operator=

查看:76
本文介绍了避免在复制构造函数和operator =中重复相同的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在c ++中,当类包含动态分配的数据时,通常合理的是显式定义副本构造函数,operator =和destructor.但是这些特殊方法的作用是重叠的.更具体地说,operator =通常首先进行一些销毁,然后进行处理,类似于复制构造函数中的操作.

In c++ when classes contains dynamically allocated data it is usually reasonable to explicitly define copy constructor, operator= and destructor. But the activity of these special methods overlaps. More specifically operator= usually first does some destruction and then it does coping similar to the one in copy constructor.

我的问题是如何以最佳方式编写这种代码,而不必重复相同的代码行,也不需要处理器进行不必要的工作(例如不必要的复制).

My question is how to write this the best way without repeating the same lines of code and without the need for processor to do unnecessary work (like unnecessary copying).

我通常会得到两种帮助方法.一种用于建筑,另一种用于破坏.第一个从复制构造函数和operator =中调用.第二个由析构函数和operator =使用.

I usually end up with two helping methods. One for construction and one for destruction. The first is called from both copy constructor and operator=. The second is used by destructor and operator=.

这是示例代码:

    template <class T>
    class MyClass
    {
        private:
        // Data members
        int count;
        T* data; // Some of them are dynamicly allocated
        void construct(const MyClass& myClass)
        {
            // Code which does deep copy
            this->count = myClass.count;
            data = new T[count];
            try
            {
                for (int i = 0; i < count; i++)
                    data[i] = myClass.data[i];
            }
            catch (...)
            {
                delete[] data;
                throw;
            }
        }
        void destruct()
        {
            // Dealocate all dynamicly allocated data members
            delete[] data;
        }
        public: MyClass(int count) : count(count)
        {
            data = new T[count];
        }
        MyClass(const MyClass& myClass)
        {
            construct(myClass);
        }
        MyClass& operator = (const MyClass& myClass)
        {
            if (this != &myClass)
            {
                destruct();
                construct(myClass);
            }
            return *this;
        }
        ~MyClass()
        {
            destruct();
        }
    };

这是否正确?以这种方式拆分代码是一个好习惯吗?

Is this even correct? And is it a good habit to split the code this way?

推荐答案

一个初步的评论: operator = 不会不是开始于破坏,但通过构造.否则,它将离开如果构造通过终止,则对象处于无效状态例外.因此,您的代码不正确.(注意测试自我分配的必要性通常表明赋值运算符是正确的.)

One initial comment: the operator= does not start by destructing, but by constructing. Otherwise, it will leave the object in an invalid state if the construction terminates via an exception. Your code is incorrect because of this. (Note that the necessity to test for self assignment is usually a sign that the assignment operator is not correct.)

用于处理此问题的经典解决方案是交换习惯用法:您添加成员函数交换:

The classical solution for handling this is the swap idiom: you add a member function swap:

void MyClass:swap( MyClass& other )
{
    std::swap( count, other.count );
    std::swap( data, other.data );
}

保证不会抛出.(在这里,它只是交换一个int和一个指针,两者都不能抛出.)然后,您将赋值运算符实现为:

which is guaranteed not to throw. (Here, it just swaps an int and a pointer, neither of which can throw.) Then you implement the assignment operator as:

MyClass& MyClass<T>::operator=( MyClass const& other )
{
    MyClass tmp( other );
    swap( tmp );
    return *this;
}

这是简单明了的方法,但是任何解决方案都可以在开始之前,所有可能失败的操作都已完成更改数据是可以接受的.对于像您这样的简单情况代码,例如:

This is simple and straight forward, but any solution in which all operations which may fail are finished before you start changing the data is acceptable. For a simple case like your code, for example:

MyClass& MyClass<T>::operator=( MyClass const& other )
{
    T* newData = cloneData( other.data, other.count );
    delete data;
    count = other.count;
    data = newData;
    return *this;
}

(其中 cloneData 是一个成员函数,可完成大部分操作您的 construct 可以,但是返回指针,但不修改 this 中的任何内容.)

(where cloneData is a member function which does most of what your construct does, but returns the pointer, and doesn't modify anything in this).

与您最初提出的问题没有直接关系,但通常在这种情况下,您不是要在其中执行新的T [count] cloneData (或 construct ,或其他).这构成了所有 T 的默认构造函数,然后分配它们.惯用的方式是这样的:

Not directly related to your initial question, but generally, in such cases, you do not want to do a new T[count] in cloneData (or construct, or whatever). This constructs all of the T with the default constructor, and then assigns them. The idiomatic way of doing this is something like:

T*
MyClass<T>::cloneData( T const* other, int count )
{
    //  ATTENTION! the type is a lie, at least for the moment!
    T* results = static_cast<T*>( operator new( count * sizeof(T) ) );
    int i = 0;
    try {
        while ( i != count ) {
            new (results + i) T( other[i] );
            ++ i;
        }
    } catch (...) {
        while ( i != 0 ) {
            -- i;
            results[i].~T();
        }
        throw;
    }
    return results;
}

通常,这将使用单独的(私人)经理来完成课:

Most often, this will be done using a separate (private) manager class:

//  Inside MyClass, private:
struct Data
{
    T* data;
    int count;
    Data( int count )
        : data( static_cast<T*>( operator new( count * sizeof(T) ) )
        , count( 0 )
    {
    }
    ~Data()
    {
        while ( count != 0 ) {
            -- count;
            (data + count)->~T();
        }
    }
    void swap( Data& other )
    {
        std::swap( data, other.data );
        std::swap( count, other.count );
    }
};
Data data;

//  Copy constructor
MyClass( MyClass const& other )
    : data( other.data.count )
{
    while ( data.count != other.data.count ) {
        new (data.data + data.count) T( other.date[data.count] );
        ++ data.count;
    }
}

(当然还有分配的交换习惯).这允许多个计数/数据对,没有丢失异常的风险安全.

(and of course, the swap idiom for assignment). This allows multiple count/data pairs without any risk of loosing exception safety.

这篇关于避免在复制构造函数和operator =中重复相同的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆