C++中复制构造函数与重载赋值操作符总结
前言
这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容:
1.复制构造函数和重载赋值操作符的定义;
2.复制构造函数和重载赋值操作符的调用时机;
3.复制构造函数和重载赋值操作符的实现要点
4.复制构造函数的一些细节。
复制构造函数和重载赋值操作符的定义
我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数、析构函数、复制构造函数和重载赋值操作;即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数。例如以下类:
class CTest
{
public:
CTest();
~CTest();
CTest(const CTest &);
void operator=(const CTest &);
};
对于构造函数和析构函数不是今天总结的重点,今天的重点是复制构造函数和重载赋值操作。类的复制构造函数原型如下:
class_name(const class_name &src);
一般来说,如果我们没有编写复制构造函数,那么编译器会自动地替每一个类创建一个复制构造函数(也叫隐式复制构造函数);相反的,如果我们编写了一个复制构造函数(显式的复制构造函数),那么编译器就不会创建它。
类的重载赋值操作符的原型如下:
void operator=(const class_name &);
重载赋值操作符是一个特别的赋值运算符,通常是用来把已存在的对象指定给其它相同类型的对象。它是一个特别的成员函数,如果我们没有定义这个成员函数,那么编译器会自动地产生这个成员函数。编译器产生的代码是以单一成员进行对象复制的动作。
总结了复制构造函数和重载赋值操作符的定义,只是让我们了解了它们,而没有真正的深入它们。接下来,再仔细的总结一下它们的调用时机。关于它们的调用时机,我一直都没有真正的明白过,所以这里一定要好好的总结明白了。
复制构造函数和重载赋值操作符的调用时机
对复制构造函数和重载赋值操作符的调用总是发生在不经意间,它们不是经过我们显式的去调用就被执行了。对于这种隐式调用的地方一定要多注意了,这也一般是有陷阱的地方。现在我就用实际的例子来进行验证;例子如下:
#include <iostream>
using namespace std;
class CTest
{
public:
CTest(){}
~CTest(){}
CTest(const CTest &test)
{
cout<<"copy constructor."<<endl;
}
void operator=(const CTest &test)
{
cout<<"operator="<<endl;
}
void Test(CTest test)
{}
CTest Test2()
{
CTest a;
return a;
}
void Test3(CTest &test)
{}
CTest &Test4()
{
CTest *pA = new CTest;
return *pA;
}
};
int main()
{
CTest obj;
CTest obj1(obj); // 调用复制构造函数
obj1 = obj; // 调用重载赋值操作符
/* 传参的过程中,要调用一次复制构造函数
* obj1入栈时会调用复制构造函数创建一个临时对象,与函数内的局部变量具有相同的作用域
*/
obj.Test(obj1);
/* 函数返回值时,调用复制构造函数;将返回值赋值给obj2时,调用重载赋值操作符
* 函数返回值时,也会构造一个临时对象;调用复制构造函数将返回值复制到临时对象上
*/
CTest obj2;
obj2 = obj.Test2();
obj2.Test3(obj); // 参数是引用,没有调用复制构造函数
CTest obj3;
obj2.Test4(); // 返回值是引用,没有调用复制构造函数
return 0;
}
在代码中都加入了注释,这里就不再做详细的说明了。再次总结一下,如果对象在声明的同时将另一个已存在的对象赋给它,就会调用复制构造函数;如果对象已经存在了,然后再将另一个已存在的对象赋给它,调用的就是重载赋值运算符了。这条规则很适用,希望大家能记住。
复制构造函数和重载赋值操作符的实现要点
在一般的情况下,编译器给我们生成的默认的复制构造函数和重载赋值操作符就已经够用了;但是在一些特别的时候,需要我们手动去实现自己的复制构造函数。
我们都知道,默认的复制构造函数和赋值运算符进行的都是"shallow copy",只是简单地复制字段,因此如果对象中含有动态分配的内存,就需要我们自己重写复制构造函数或者重载赋值运算符来实现"deep copy",确保数据的完整性和安全性。这也就是大家常常说的深拷贝与浅拷贝的问题。下面我就提供一个比较简单的例子来说明一下:
#include <iostream>
using namespace std;
const int MAXSIZE = 260;
class CTest
{
public:
CTest(wchar_t *pInitValue)
{
// Here, I malloc the memory
pValue = new wchar_t[MAXSIZE];
memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
wcscpy_s(pValue, MAXSIZE, pInitValue);
}
~CTest()
{
if (pValue)
{
delete pValue;
pValue = NULL;
}
}
CTest(const CTest &test)
{
// Malloc the new memory for the pValue
pValue = new wchar_t[MAXSIZE];
memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
wcscpy_s(pValue, MAXSIZE, test.pValue);
}
CTest &operator=(const CTest &test)
{
// This is very important, please remember
if (this == &test)
{
return *this;
}
// Please delete the memory, this maybe cause the memory leak
if (pValue)
{
delete pValue;
}
// Malloc the new memory for the pValue
pValue = new wchar_t[MAXSIZE];
memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
wcscpy_s(pValue, MAXSIZE, test.pValue);
return *this;
}
void Print()
{
wcout<<pValue<<endl;
}
private:
wchar_t *pValue; // The pointer points the memory
};
int main()
{
CTest obj(L"obj");
obj.Print();
CTest obj2(L"obj2");
obj2.Print();
obj2 = obj;
obj2.Print();
obj2 = obj2;
obj2.Print();
return 0;
}
特别是在实现重载赋值构造函数时需要多多的注意,在代码中我也添加了注释,大家可以认真的阅读一下代码,然后就懂了,如果不懂的就可以留言问我;当然了,如果我哪里理解错了,也希望大家能给我提出,我们共同进步。
复制构造函数的一些细节
1.以下哪些是复制构造函数
X::X(const X&);
X::X(X);
X::X(X&, int a=1);
X::X(X&, int a=1, int b=2);
这些细节问题在这里也说一说,我也是从别人的博客里看到的,这里自己也总结一下。对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数。
X::X(const X&); //是拷贝构造函数
X::X(X&, int=1); //是拷贝构造函数
X::X(X&, int a=1, int b=2); //当然也是拷贝构造函数
2.类中可以存在超过一个拷贝构造函数
class X {
public:
X(const X&); // const 的拷贝构造
X(X&); // 非const的拷贝构造
};
注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化。如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。这个默认的参数可能为 X::X(const X&)或 X::X(X&),由编译器根据上下文决定选择哪一个。在我的Visual Studio 2012中,当定义了多个复制构造函数以后,编译器就会有warning,但是程序还能正确运行。
总结
这篇文章对复制构造函数和重载赋值操作符进行了一些总结,重点是在复制构造函数与重载赋值操作符的调用时机上;对于大家喜欢总结的深拷贝与浅拷贝问题,我没有用过多的文字进行说明,我认为上面的代码就足以说明问题了。最后自己纠结已久的问题也就这样总结了,自己也彻底的明白了。
2014年2月21日 于大连,东软。
如果这篇文章对你有帮助,你可以请我喝杯咖啡。
» 本文链接:http://www.jellythink.com/archives/378
» 订阅本站:http://www.jellythink.com/feed
C++中复制构造函数与重载赋值操作符总结的更多相关文章
- C++中复制构造函数与重载赋值操作符
我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数.例如以下类: class CTe ...
- C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)
在C++中,有三大函数复制控制(复制构造函数,赋值操作符,析构函数),而在C++11中,加入了移动构造函数,移动赋值操作符.我就斗胆将他们命名为六大函数好了. 一.构造函数 c++primer中说过: ...
- C++ 之 重载赋值操作符
Widget 类中,定义了一个 Bitmap 类型的私有数据成员 -- pb 指针 class Bitmap { ... }; class Widget { private: Bitmap *pb; ...
- 5.5 C++重载赋值操作符
参考:http://www.weixueyuan.net/view/6383.html 总结: 重载赋值操作符同重载类的是拷贝构造函数的原因是一样,将一个对象拷贝给另一个对象,同时当类中存在指针类型的 ...
- C++重载赋值操作符
1.C++中重载赋值操作函数应该返回什么? 类重载赋值操作符一般都是作为成员函数而存在的,那函数应该返回什么类型呢?参考内置类型的赋值操作,例如 int x,y,z; x=y=z=15; 赋值行为相当 ...
- Effective C++(10) 重载赋值操作符时,返回该对象的引用(retrun *this)
问题聚焦: 这个准则比较简短,但是往往就是这种细节的地方,可以提高你的代码质量. 细节决定成败,让我们一起学习这条重载赋值操作符时需要遵守的准则吧. 还是以一个例子开始: Demo // 连锁赋值 x ...
- C++中复制构造函数和赋值操作符
先看一个例子: 定义了一个类:
- C++中复制构造函数
复制构造函数 复制构造函数用于: 根据另一个同类型的对象显示或隐式初始化一个对象 复制一个对象,将它作为实参传给一个函数 从函数返回时复制一个对象 初始化顺序容器中的元素 根据元素初始化式列表初始化数 ...
- Java中的构造函数和重载
一.Java中的构造函数 构造函数是对象被创建时初始化对象的成员方法,它具有和它所在的类完全一样的名字.构造函数只能有入口参数,没有返回类型,因为一个类的构造方法的返回类就是类本身.构造函数定义后,创 ...
随机推荐
- 图解Windows Server 2012 桌面图标
显示桌面图标.壁纸等 1 WIN键+R键,输入: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0 选择你需要的图标显示到桌面去,如下图所示 ...
- 【MVC】ASP.NET MVC中实现多个按钮提交的几种方法
有时候会遇到这种情况:在一个表单上需要多个按钮来完成不同的功能,比如一个简单的审批功能. 如果是用webform那不需要讨论,但asp.net mvc中一个表单只能提交到一个Action处理,相对比较 ...
- hbase常用运维命令
一,基本命令: 建表:create 'testtable','coulmn1','coulmn2' 也可以建表时加coulmn的属性如:create 'testtable',{NAME => ' ...
- .net分页控件webdiyer:AspNetPager
首先下载:AspNetPager.dll AspNetPager.xml 放到bin目录下 页面添加<%@ Register Assembly="AspNetPager" ...
- PUTTY使用Ctrl+s僵死的问题
算是分享个小经验吧! 一直都是使用VM+PUTTY的方式调试Linux程序,有时候在Vi中编辑了程序,Windowns下旧习难改,顺手就Ctrl+s了,尽管我知道Vi的保存是:w. 很不幸这时的PUT ...
- Abap 多线程
http://scn.sap.com/thread/18844 SAP ABAP 实现多线程 第一步:初始化server group ,server group 可以用RZ12进行维护,参数支 ...
- zend studio导入thinkphp的乱码问题
刚刚导入thinkphp有乱码还有错误怎么办? windows -> preference -> Work space -> text file encodeing设置为 UTF-8 ...
- 小鸟哥哥博客 For SAE
独立博客地址:http://www.zhujiawei.com.cn/ 辞职后出去玩了几个月,把积蓄都快花光了,打算熬到年底再找工作.最近闲来无聊,想起自己一年前趁着活动便宜,一口气买了10年的域名一 ...
- 国际制造执行系统(MES)应用与发展
某些专家认为,当今制造业的生存三要素是信息技术(IT).供应链管理(SCM)和成批制造技术.使用信息技术就是由依赖人工的作业方式转变为作业的快速化.高效化,大量减少人工介入,降低生产经营成本:供应链管 ...
- MySQL自用脚本(不定期更新)
查询指定SQL的trace信息 mysql -uUSER -pPWD -hHOST -e "\ use ins_tc_prd; \ set session optimizer_trace=' ...