一、在以下几种情况下会调用对象的拷贝构造函数(对象被创建时):
1.一个对象以值传递的方式作为函数的形参时;
void func(CTest t) // CTest为clas类型,见下文 { cout << t.a << " " << t.b << endl; }
2.一个对象以值传递的方式作为函数的返回值时;
CTest func() { CTest tt; tt.a = 2; tt.b = 3; return tt; }
3.一个对象使用另外一个对象初始化时;
{ CTest c; c.a = 10; c.b = 20; CTest t(c);//初始化 cout << t.a << " " << t.b << endl; }
二、默认拷贝构造函数
在对象拷贝过程中,如果没有自定义拷贝构造函数,编译器会提供一个缺省的默认拷贝构造函数,默认拷贝构造函数对于基本类型的成员变量,按字节复制;对于类类型成员变量,调用其相应类型的拷贝构造函数。
浅拷贝:
缺省拷贝构造函数在拷贝过程中是按字节复制的;对于指针型成员变量只复制指针本身,而不复制指针所指向的目标,拷贝后两个指针指向同一个内存空间。
1.浅拷贝构造函数
#include <iostream> using namespace std; class CTest { public: CTest() { buf = new char[10]; printf("CTest create buf:0x%x ", buf); } ~CTest() { printf("~CTest destory 0x%x ", buf); delete[] buf; } public: int a; char * buf; }; int main(int argc, char** argv) {
{ CTest tmp0; tmp0.a = 10; printf("tmp0 buf:0x%x ", tmp0.buf); CTest tmp(tmp0); printf("tmp1 buf:0x%x ", tmp.buf); }
system("pause"); return 0; }
此代码中使用了系统的默认拷贝构造函数;
程序运行后,编译器报错,以及构造函数只是执行了一次,而析构了两次,指针tmp0.buf和tmp.buf指向了同一块内存。这就是默认拷贝构造函数仅仅按照字节复制导致指针指向同一块内存;
而报错的原因是对同一块内存进行了两次释放;
此时对于有指针的对象进行拷贝时就需要使用深拷贝:
#include <iostream> using namespace std; class CTest { public: CTest() { buf = new char[10]; printf("CTest create buf:0x%x ", buf); } CTest(const CTest& other) { buf = new char[10]; memcpy(buf, other.buf, sizeof(other.buf )); printf("CTest create buf:0x%x, other.buf:%p ", buf, other.buf); } ~CTest() { printf("~CTest destory 0x%x ", buf); delete[] buf; } public: int a; char * buf; }; int main(int argc, char** argv) { { CTest tmp0; tmp0.a = 10; printf("tmp0 buf:0x%x ", tmp0.buf); CTest tmp(tmp0); printf("tmp1 buf:0x%x ", tmp.buf); } system("pause"); return 0; }
执行结果:
执行后,编译器没有报错,且生成的buf指向了两个不同的内存;
因此,关于对于存在指针成员变量的类进行拷贝操作时,必须实现深拷贝构造函数;
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
另外,对于派生类在实现拷贝构造函数时,必须对基类同样进行拷贝构造,具体区别如下:
#include <iostream> using namespace std; class CBase { public: int x; }; class CTest : public CBase { public: CTest() { buf = new char[10]; printf("CTest create buf:0x%x ", buf); } CTest(const CTest& other) { buf = new char[10]; memcpy(buf, other.buf, sizeof(other.buf )); printf("CTest create buf:0x%x, other.buf:%p ", buf, other.buf); a = other.a; } ~CTest() { printf("~CTest destory 0x%x ", buf); delete[] buf; } public: int a; char * buf; }; int main(int argc, char** argv) { { CTest tmp0; tmp0.a = 10; tmp0.x = 20; printf("tmp0 buf:0x%x ", tmp0.buf); CTest tmp(tmp0); printf("tmp1 buf:0x%x ", tmp.buf); printf("---tmp.a:%d, tmp.x:%d ", tmp.a, tmp.x); } system("pause"); return 0; }
执行结果中可以看出,派生类中继承来的变量x值不正确;
修改代码如下
#include <iostream> using namespace std; class CBase { public: int x; }; class CTest : public CBase { public: CTest() { buf = new char[10]; printf("CTest create buf:0x%x ", buf); } CTest(const CTest& other) : CBase(other) //这就是区别 { buf = new char[10]; memcpy(buf, other.buf, sizeof(other.buf )); printf("CTest create buf:0x%x, other.buf:%p ", buf, other.buf); a = other.a; } ~CTest() { printf("~CTest destory 0x%x ", buf); delete[] buf; } public: int a; char * buf; }; int main(int argc, char** argv) { { CTest tmp0; tmp0.a = 10; tmp0.x = 20; printf("tmp0 buf:0x%x ", tmp0.buf); CTest tmp(tmp0); printf("tmp1 buf:0x%x ", tmp.buf); printf("---tmp.a:%d, tmp.x:%d ", tmp.a, tmp.x); } system("pause"); return 0; }
浅拷贝构造函数:1.浅拷贝构造函数中必须对每个成员变量进行赋值拷贝;
2.继承的类,必须对父类进行拷贝;
3.浅拷贝构造函数可省略,系统会有默认拷贝构造函数,就是对类的成员变量进行一一拷贝 就是传说中的浅拷贝;
补充:深拷贝
2.深拷贝构造函数-必须实现构造函数
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Object { public: Object(char * str) { m_size = strlen(str)+1; m_buf = new char[m_size]; strcpy(m_buf,str); printf("%s ",m_buf); } ~Object() { delete[] m_buf; } private: char *m_buf; int m_size; }; int main(int argc, char * argv[]) { Object obja("hello world"); Object objb(obja); system("pause"); return 0; }
上述代码在析构时,出现错误:
主要原因是因为使用了系统的默认拷贝构造函数,相当于执行了以下代码:
objb.m_buf = obja.m_buf;
objb.m_size = obja.m_buf;
这样两个对象指针,指向了同一块内存;
析构时,先析构了obja,指向的内存被释放,再析构objb时,发现内存已经被释放,导致程序崩溃;
因此,此时需要手动写一个拷贝构造函数,目的是拷贝数据,而不是拷贝指针;见如下代码:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Object { public: Object(char * str) { m_size = strlen(str)+1; m_buf = new char[m_size]; strcpy(m_buf,str); printf("%s ",m_buf); } ~Object() { delete[] m_buf; } Object(const Object &other) { m_size = other.m_size; m_buf = new char[m_size]; strcpy(m_buf,other.m_buf); } private: char *m_buf; int m_size; }; int main(int argc, char * argv[]) { Object obja("hello world"); Object objb(obja); system("pause"); return 0; }
上述代码中的红色部分,手动实现的拷贝构造函数中申请一块新的内存,然后对数据进行拷贝。
#include <iostream>using namespace std;
class CBase{public:int x;};
class CTest : public CBase{public:CTest(){buf = new char[10];printf("CTest create buf:0x%x
", buf);}
CTest(const CTest& other){buf = new char[10];memcpy(buf, other.buf, sizeof(other.buf));printf("CTest create buf:0x%x, other.buf:%p
", buf, other.buf);a = other.a;}
~CTest(){printf("~CTest destory 0x%x
", buf);delete[] buf;}public:int a;char * buf;};
int main(int argc, char** argv){{CTest tmp0;tmp0.a = 10;tmp0.x = 20;printf("tmp0 buf:0x%x
", tmp0.buf);CTest tmp(tmp0);printf("tmp1 buf:0x%x
", tmp.buf);printf("---tmp.a:%d, tmp.x:%d
", tmp.a, tmp.x);}system("pause");return 0;}