c++复制/拷贝构造函数
来源:互联网 发布:湖南省干部培训网络 编辑:程序博客网 时间:2024/05/17 01:55
在c++中,定义一个空类时,编译器会默认声明6个成员函数,它们分别是
class Empty {public: Empty(); //默认构造函数 Empty(const Empty&); //拷贝构造函数 ~Empty(); //析构函数 Empty& operator=(const Empty&); //赋值运算符 Empty* operator&(); //取地址运算符 const Empty* operator&() const; //取地址运算符const};注意 一下,编译器默认合成的析构函数不是虚函数。
首先,说一下什么是拷贝构造函数(也可以叫复制构造函数),它是一个特殊的构造函数,具有单个形参(此形参是对该类类型的引用,需要用const修饰,否则会无限循环调用复制构造函数)。一般这几种情况会调用它:1.根据另一个同类型的对象初始化一个对象 2.复制一个对象,将它作为实参传递给一个函数 3.从函数返回时复制一个对象。
只要我们不显示的定义拷贝构造函数,编译器都会自动合成一个,这时候编译器只是简单对对象中的数据成员进行赋值,也就是浅拷贝。大致的内部实现如代码所示
#include <iostream>class Test {public:Test(int tmp) {this->m1 = tmp;}//自定义拷贝构造函数Test(Test& c) {std::cout << "拷贝构造函数" << std::endl;m1 = c.m1;}int m1;};int main() {Test a(1);Test b = a;std::cout << b.m1 << std::endl;system("pause");return 0;}当然大多数情况下,浅拷贝是可以的,但是当类成员含有指针或者其它占有系统资源的变量时,由于浅拷贝只是简单的赋值,会将新对象的指针也指向以前对象已经分配的堆内存空间,如果这时候以前的对象已经析构,新对象的指针就成为野指针,出现运行错误。
先看下含有指针成员的的类对象浅拷贝代码
#include <iostream>class Test {public:Test(int* pi) {p = new int(100);p = pi;}~Test() {if (p != NULL) {delete p;p = NULL;}}int *p;};int main() {int i = 5;Test a(&i);Test b = a;std::cout << "对象a中的成员p的地址: " << a.p << std::endl;std::cout << "对象b中的成员p的地址: " << b.p << std::endl;std::cout << *(a.p) << std::endl;std::cout << *(b.p) << std::endl;system("pause");return 0;}输出结果:
从输出结果可以看出对象a和b中的指针成员p指向的是同一块堆内存地址,若a对象先把内存释放后在引用b中的p就会出现野指针的运行时错误。为了防止这种情况发生,我们需要自定义拷贝构造函数,对指针成员不是简单的赋值,需要重新开辟一块内存,将老对象指针指向的数据拷贝进去。这就是深拷贝。
看看深拷贝的实现:
#include <iostream>class Test {public:Test(int* pi) {p = new int(100);p = pi;}//自定义拷贝构造函数(深拷贝)Test(Test& c) {std::cout << "拷贝构造函数" << std::endl;p = new int;if (p != NULL) {//memcpy(p, c.p, sizeof(c.p));*p = *(c.p);}}//赋值运算符Test& operator=(const Test &rhs) {p = new int;*p = *(rhs.p);return *this;}~Test() {if (p != NULL) {delete p;p = NULL;}}int *p;};int main() {int i = 5;Test a(&i);Test b = a;std::cout << "对象a中的成员p的地址: " << a.p << std::endl;std::cout << "对象b中的成员p的地址: " << b.p << std::endl;std::cout << *(a.p) << std::endl;std::cout << *(b.p) << std::endl;system("pause");return 0;}
输出结果:
可以看出对象a和b中的指针保存的不是同一块堆内存地址,它们中的数据是一样的,这样就可以防止出现野指针的运行时错误。由于只是做个简单的测试,代码有点偏向伪码,类属性其实需要定义为private,为了测试方便,直接使用public了。
需要注意下,一般情况类需要重载赋值运算符,因为我们不一定是直接初始化对象Test b(a),我们可能会这样写Test b = a这种赋值初始化。所以好的建议是自定义了拷贝构造函数后顺便重载赋值运算符。
也就是说当类中含有指针这种需要占有系统资源的变量时,我们必须自定义拷贝构造函数(深拷贝)。浅拷贝说白了,就是简单赋值,有指针成员时,拷贝的是指针,而深拷贝就是拷贝指针指向的对象。
- C++:复制构造函数___浅拷贝
- c++-拷贝构造函数(复制构造函数)
- 复制(拷贝)构造函数
- 【C++】拷贝构造函数之浅复制与深复制
- 拷贝构造函数 与拷贝复制函数
- c++复制/拷贝构造函数
- c++复制/拷贝构造函数
- 拷贝(复制)构造函数
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数,拷贝构造函数
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- 复制构造函数(拷贝构造函数)
- JavaMail
- HtmlUnit抓取Ajax网页,例子是去哪儿机票
- Android Push消息推送
- 在eclipse中关联android源码
- Android IllegalArgumentException: Cannot draw recycled bitmaps解决方法
- c++复制/拷贝构造函数
- 工具UltraEdit破解使用方法!!!
- 正则表达式大全
- Java线程(篇外篇):线程本地变量ThreadLocal
- HTML文本框录入字母自动大写
- select判断选中,以及组装对象数据
- 为嘛子要慎用mysql的enum字段?(来自向东的原创博客)
- EXTjs 简单的表单学习
- 同步、异步