拷贝构造函数和赋值操作符

假设有一个如下的MyClass类:

class MyClass
{
public:
	//构造函数
	//拷贝构造函数
	MyClass(const MyClass& that)
		: int_data_(that.int_data_),
		  dbl_data_(that.dbl_data_),
		  str_data_(that.str_data_)
	{
	}
	//赋值操作符
	MyClass& operator = (const MyClass& that)
	{
		if(this != that)
		{
			int_data_ = that.int_data_;
			dbl_data_ = that.dbl_data_;
			str_data_ = that.str_data_;
		}
		return *this;
	}

	//一些其他方法
private:
	Int int_data_;
	Double dbl_data_;
	string str_data_;
	//每次在这里添加一个新的数据成员时,不要忘了在拷贝构造函数和赋值操作中添加对应的代码
};

对于这个类,存在什么问题呢?真正的问题是,在私有部分后面的注释指出了问题的所在。如果像注释中所说的,这样的操作特别麻烦而且极易犯错误。事实上,如果我们没有编写拷贝构造函数和赋值操作符,C++会为我们编写一个“默认版本”。

拷贝构造函数的默认版本为所有的数据成员调用拷贝构造函数(或简单的复制内置类型),赋值操作符的默认版本将调用每个数据成员的赋值操作符或者简单的复制内置类型。

因此,对于上面的例子,拷贝构造函数和赋值操作符完全不是必需的。更糟的是,它们是潜在的错误之源,因为它们使代码变得脆弱。如果有人试图改变它们,就可能对代码产生破坏。

因此,对于上述的例子,比较好的思路是彻底避免编写拷贝构造函数和赋值操作符。

一般而言,有几种方式可供选择:

  • 依赖编译器自动创建的默认版本
  • 把拷贝构造函数和赋值操作符声明为私有,并且不提供实现,禁止任何类型的复制
  • 编写自己的版本

对于上述三种方式,应该尽量避免使用第三种。如果发现自己为某个类编写了拷贝构造函数或赋值操作符,思考是否有必要。也许可以避免这种做法并转为第一种方式(使用编译器所创建的默认版本)或使用其他方法(例如智能指针)。如果仍然不确定,可以使用第二种方式,只要不存在任何类型的复制,就不会出错。但是,需要注意类的有些用法(例如在vector<MyClass>中)需要拷贝构造函数和赋值操作符,因此禁止任何类型的复制也需要谨慎,必须理解它限制了类的使用方面的某些选项。

总结:

  • 只要有可能,避免编写拷贝构造函数和赋值操作符
  • 如果默认版本并不适用,可以考虑把拷贝构造函数和赋值操作符声明为私有,禁止类实例的复制
时间: 2024-10-07 08:25:33

拷贝构造函数和赋值操作符的相关文章

构造函数、拷贝构造函数、赋值操作符

对于这样一种类与类之间的关系,我们希望为其编写“深拷贝”.两个类的定义如下: class Point { int x; int y; }; class Polygon : public Shape { Point *points; }; 1. 构造函数 //构造函数 Polygon(const Point &p) : _point(new Point) { this->_point->x = p.x; this->_point->y = p.y; } 2. 拷贝构造函数 /

拷贝构造函数 和 赋值操作符重载

什么时候需要定义自己的拷贝构造函数: 当类中包含有,动态分配成员 或者 指针 的时候. 如果使用默认构造函数,则新构造出来的 新类 和 旧类 里面的指针成员 指向同一个空间, 而当其中一个类 清空掉那个空间 .另一个类的指针就会变成野指针(因为空间已经被清空) , 也就是说默认构造函数是复制值(地址也是值) ps.基本数据类型的数组可以直接使用默认复制构造函数.也就是说有涉及到 自行开辟额外空间 的数据类型 和 指针 的类就需要 什么时候调用拷贝构造函数: 1.一个对象以 值传递 的方式 传入

深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结

转自 http://www.jb51.net/article/37527.htm,感谢作者 #include "stdafx.h"      #include <iostream>      using namespace std;      class B      {      public:          B():data(0)    //默认构造函数          {               cout << "Default con

赋值构造函数(重载赋值操作符)(c++常问问题二)

*什么是赋值构造函数(重载赋值操作符) 下面的代码演示了什么是赋值构造函数,如果不人为定义赋值构造函数,系统将默认给你分配一个浅拷贝的赋值构造函数(下面例子为深拷贝的赋值操作) class cat { public: //构造函数 cat():m_pMyName(NULL),m_unAge(0) { cout<<"cat defult ctor"<<endl; } //子类赋值构造函数(重载赋值操作符) cat& operator=(cat& o

拷贝构造函数和赋值函数

在 C++ 中,赋值和拷贝是不同的, 1)拷贝构造函数是对未初始化的内存进行初始化操作 2)而赋值是对现有的已经初始化的对象进行操作.(这里我对"已经初始化"的理解是已经调用了构造函数,并且构造函数体可以未执行,只要调用到即可),赋值函数应该给所有数据成员都初始化. 3)重点:包含动态分配成员的类 应提供拷贝构造函数,并重载"="赋值操作符. 4)可以说,C++中什么时候有临时对象产生,此时刻c++一定要调用拷贝构造函数.(临时对象产生时有一个特例,此时不需要调用拷

拷贝构造函数和赋值函数的一些知识

/*******************拷贝构造函数和赋值运算符重载有以下两个不同之处***************************/ 1.拷贝构造函数生成新的类对象,而赋值运算符不能. 2.由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新对象相同,而复制操作符需要这个操作,另外赋值运算符中如果原来对象中有内存分配,要先把内存释放掉. 下面是String类的一个实现的部分函数,可以看出二者的区别. 1 class String{ 2 public:

拷贝构造函数与赋值函数的区别

1.从概念上区分:复制构造函数是构造函数,而赋值操作符属于操作符重载范畴,它通常是类的成员函数 2.从原型上来区分:复制构造函数原型ClassType(const ClassType &);无返回值赋值操作符原型ClassType& operator=(const ClassType &);返回值为ClassType的引用,便于连续赋值操作 3.从使用的场合来区分:复制构造函数用于产生对象,它用于以下几个地方:函数参数为类的值类型时.函数返回值为类类型时以及初始化语句,例如(示例了

C++ 拷贝构造函数与赋值函数的区别(很严谨和全面)

这里我们用类String 来介绍这两个函数: 拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用拷贝构造函数.为啥形参必须是对该类型的引用呢?试想一下,假如形参是该类的一个实例,由于是传值参数,我们把形参复制到实参会调用拷贝构造函数,如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,从而形成无休止的递归调用导致栈溢出. 赋值函数,也是赋值操作符重载,因为赋值必须作为类成员,那

C++之复制构造函数和赋值操作符

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象.拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象. 复制对象把它作为参数传递给函数. 复制对象,并从函数返回这个对象. 如果在类中没有定义拷贝构造函数,编译器会自行定义一个.如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数.拷贝构造函数的最常见形式如下: classname (const classname &obj) { // 构造函数的主体 } 在这里,o