从复制构造函数调用默认赋值运算符是不好的形式吗? [英] Is it bad form to call the default assignment operator from the copy constructor?

查看:226
本文介绍了从复制构造函数调用默认赋值运算符是不好的形式吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个需要制作副本的类。副本中的绝大多数数据元素必须严格反映原始数据元素,然而,有几个元素的状态不被保留并需要重新初始化



从复制构造函数调用默认赋值运算符是不好的形式吗?



默认赋值运算符在普通数据,double,char,short)以及每个赋值运算符的用户定义类。指针需要单独处理。



一个缺点是,由于不执行额外的重新初始化,此方法会导致赋值操作符crippled。也不可能禁用赋值操作符的使用,因此打开用户的选项以通过使用不完全的默认赋值运算符 A obj1,obj2; obj2 = obj1; / *可能的结果是一个不正确初始化的obj2 * /



这将是很好放松对除了 a(0),b(0)... 之外,还包括a(orig.a),b(orig.b)... >必须写入。需要写所有的初始化两次创建两个地方的错误,如果新的变量(如 double x,y,z )被添加到类,初始化代码需要正确添加至少2个位置,而不是1个。



有更好的方法吗? b $ b

C ++ 0x有更好的方法吗?

 一个{
public:
A():a(0),b(0),c(0),d(0)
A(const A& orig){
* this = orig; / * <-----这是坏吗? * /
c = int();
}
public:
int a,b,c,d;
};

A X;
X.a = 123;
X.b = 456;
X.c = 789;
X.d = 987;

A Y(X);

printf(X:%d%d%d%d \\\
,X.a,X.b,X.c,X.d);
printf(Y:%d%d%d%d \\\
,Y.a,Y.b,Y.c,Y.d);

输出:

 code> X:123 456 789 987 
Y:123 456 0 987

替代复制构造函数:

  A(const A& orig):a(orig.a),b ,c(0),d(orig.d){} / * < - 是更好吗? * / 


解决方案

As brone指出,在复制结构方面实现分配。我更喜欢他的另一种成语:

  T :: operator =(T t){
swap(* this,t);
return * this;
}

有点短,可以利用一些深奥的语言特性来提高性能。像任何好的C ++代码一样,它也有一些细微之处。



首先, t 是有意通过值传递的,以便调用复制构造函数(大部分时间),我们可以修改是我们的心的内容,而不影响原有的价值。使用 const T& 将无法编译,并且 T& 会通过修改assign-from 。



此技术还需要 swap 专用于类型,不使用类型赋值运算符( std :: swap ),否则会导致无限递归。使用std :: swap 或使用namespace std 小心任何stray ,因为它们会拉 for T > std :: swap c>。过载解析和 ADL 将确保使用正确的交换版本(如果已定义)。



有两种方法可以为类型定义 swap 。第一种方法使用 swap 成员函数进行实际工作,并具有委派给它的 swap 专门化,例如所以:

  class T {
public:
// ...
swap(T&){...}
};

void swap(T& a,T& b){a.swap(b); }

这在标准库中很常见; std :: vector ,例如,以这种方式实现交换。如果你有一个 swap 成员函数,你可以直接从赋值运算符调用它,并避免任何问题的函数查找。



另一种方法是将 swap 声明为朋友函数,并完成所有工作:

  class T {
// ...
friend void swap(T& a,T& b);
};

void swap(T& a,T& b){...}

我喜欢第二个,因为 swap()通常不是类的接口的组成部分;它似乎更适合作为一个自由功能。



对于类型,优化的 swap 是一种常见的方法,的C ++ 0x中的右值引用的好处,所以这是一个好主意,如果该类可以利用它,你真的需要性能。


Consider a class of which copies need to be made. The vast majority of the data elements in the copy must strictly reflect the original, however there are select few elements whose state is not to be preserved and need to be reinitialized.

Is it bad form to call a default assignment operator from the copy constructor?

The default assignment operator will behave well with Plain Old Data( int,double,char,short) as well user defined classes per their assignment operators. Pointers would need to be treated separately.

One drawback is that this method renders the assignment operator crippled since the extra reinitialization is not performed. It is also not possible to disable the use of the assignment operator thus opening up the option of the user to create a broken class by using the incomplete default assignment operator A obj1,obj2; obj2=obj1; /* Could result is an incorrectly initialized obj2 */ .

It would be good to relax the requirement that to a(orig.a),b(orig.b)... in addition to a(0),b(0) ... must be written. Needing to write all of the initialization twice creates two places for errors and if new variables (say double x,y,z) were to be added to the class, initialization code would need to correctly added in at least 2 places instead of 1.

Is there a better way?

Is there be a better way in C++0x?

class A {
  public:
    A(): a(0),b(0),c(0),d(0)
    A(const A & orig){
      *this = orig;       /* <----- is this "bad"? */
      c = int();
    }
  public:
    int a,b,c,d;
};

A X;
X.a = 123;
X.b = 456;
X.c = 789;
X.d = 987;

A Y(X);

printf("X: %d %d %d %d\n",X.a,X.b,X.c,X.d);
printf("Y: %d %d %d %d\n",Y.a,Y.b,Y.c,Y.d);

Output:

X: 123 456 789 987
Y: 123 456 0 987

Alternative Copy Constructor:

A(const A & orig):a(orig.a),b(orig.b),c(0),d(orig.d){}  /* <-- is this "better"? */

解决方案

As brone points out, you're better off implementing assignment in terms of copy construction. I prefer an alternative idiom to his:

T& T::operator=(T t) {
    swap(*this, t);
    return *this;
}

It's a bit shorter, and can take advantage of some esoteric language features to improve performance. Like any good piece of C++ code, it also has some subtleties to watch for.

First, the t parameter is intentionally passed by value, so that the copy constructor will be called (most of the time) and we can modify is to our heart's content without affecting the original value. Using const T& would fail to compile, and T& would trigger some surprising behaviour by modifying the assigned-from value.

This technique also requires swap to be specialized for the type in a way that doesn't use the type's assignment operator (as std::swap does), or it will cause an infinite recursion. Be careful of any stray using std::swap or using namespace std, as they will pull std::swap into scope and cause problems if you didn't specialize swap for T. Overload resolution and ADL will ensure the correct version of swap is used if you have defined it.

There are a couple of ways to define swap for a type. The first method uses a swap member function to do the actual work and has a swap specialization that delegates to it, like so:

class T {
public:
    // ....
    void swap(T&) { ... }
};

void swap(T& a, T& b) { a.swap(b); }

This is pretty common in the standard library; std::vector, for example, has swapping implemented this way. If you have a swap member function you can just call it directly from the assignment operator and avoid any issues with function lookup.

Another way is to declare swap as a friend function and have it do all of the work:

class T {
    // ....
    friend void swap(T& a, T& b);
};

void swap(T& a, T& b) { ... }

I prefer the second one, as swap() usually isn't an integral part of the class' interface; it seems more appropriate as a free function. It's a matter of taste, however.

Having an optimized swap for a type is a common method of achieving some of the benefits of rvalue references in C++0x, so it's a good idea in general if the class can take advantage of it and you really need the performance.

这篇关于从复制构造函数调用默认赋值运算符是不好的形式吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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