提问人:John Bumper 提问时间:7/10/2013 最后编辑:David GJohn Bumper 更新时间:7/13/2013 访问量:2374
避免在复制构造函数和运算符中重复相同的代码=
Avoid repeating the same code in copy constructor and operator=
问:
在 c++ 中,当类包含动态分配的数据时,显式定义复制构造函数、operator= 和析构函数通常是合理的。但是这些特殊方法的活动是重叠的。更具体地说,operator= 通常首先进行一些破坏,然后进行类似于复制构造函数中的处理。
我的问题是,如何在不重复相同代码行的情况下以最佳方式编写此代码,并且不需要处理器执行不必要的工作(例如不必要的复制)。
我通常最终会得到两种帮助方法。一个用于建设,一个用于破坏。第一个是从 copy constructor 和 operator= 调用的。第二个由 destructor 和 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();
}
};
这甚至正确吗? 以这种方式拆分代码是一个好习惯吗?
答:
通过首先复制右侧,然后与之交换来实现分配。这样,您还可以获得异常安全性,这是上面的代码所不提供的。否则,当 construct() 失败时,如果 construct() 失败,则容器可能会损坏,因为成员指针引用了一些已释放的数据,并且在销毁时将再次释放,从而导致未定义的行为。
foo&
foo::operator=(foo const& rhs)
{
using std::swap;
foo tmp(rhs);
swap(*this, tmp);
return *this;
}
评论
data = nullptr
我看不出这有什么固有的问题,只要你确保不声明构造或破坏虚拟。
您可能对 Effective C++ (Scott Meyers) 中的第 2 章感兴趣,该章完全致力于构造函数、复制运算符和析构函数。
至于代码未按应有的方式处理的异常,请考虑更有效的 C++ (Scott Meyers) 中的第 10 和 11 项。
评论
new
construct
一个初步评论:不是以
破坏,但通过建造。否则,它将离开
如果构造通过
例外。因此,您的代码不正确。(请注意,
测试自我分配的必要性通常表明
赋值运算符不正确。operator=
处理这个问题的经典解决方案是交换成语:你 添加成员函数交换:
void MyClass:swap( MyClass& other )
{
std::swap( count, other.count );
std::swap( data, other.data );
}
保证不会扔。(在这里,它只是交换一个 int 和一个指针,两者都不能抛出。然后你 将赋值运算符实现为:
MyClass& MyClass<T>::operator=( MyClass const& other )
{
MyClass tmp( other );
swap( tmp );
return *this;
}
这简单明了,但任何解决方案 所有可能失败的操作在开始之前都已完成 更改数据是可以接受的。对于像您这样的简单案例 代码,例如:
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
编辑:
与您最初的问题没有直接关系,但通常,在
在这种情况下,你不想做一个 in(或 ,或其他什么)。这将构造所有
,然后分配它们。
这样做的惯用方式是这样的:new T[count]
cloneData
construct
T
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;
}
大多数情况下,这将使用单独的(私人)管理器来完成 类:
// 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;
}
}
(当然,还有 Assignment 的交换成语)。这允许 多个计数/数据对,没有任何丢失异常风险 安全。
评论
memmove
std::copy
memcpy
memcpy
上一个:这样的写作业有什么问题?
评论
new
assign
clear
swap