在上一节的copy构造函数中,就涉及到了C++中的浅拷贝问题,当我们没有自定义拷贝构造函数的时候,C++编译器会自动帮我们生成一个拷贝构造函数,这个拷贝构造函数只能拷贝对象的值或者引用,而不能拷贝引用本身,也就是说如果对象中存在一个动态分配的内存变量的时候,它只会拷贝指针本身,而不会拷贝指针指向的内存。浅拷贝会引发某些问题,如下代码。
#include "iostream" using namespace std; class String { private: char * p; int length; public: String(const char * p) { this->length = strlen(p); this->p = (char*)malloc(this->length + 1); strcpy_s(this->p,this->length + 1, p); } ~String() { if (this->p) { free(this->p); this->p = NULL; this->length = 0; } } char * toString() { return this->p; } }; void test() { String s = "hello"; String s2 = s; } int main() { test(); system("pause"); return 0; }以上代码出现的错误主要是,test函数运行的时候,会把s中指向的地址复制一份给s2,这时候,并没有复制一份内存给s2,所以当test函数退出的时候,就会先析构s2,当析构s2的时候,我们把里面的动态内存分配的部分给析构了,再次当析构s的时候,指针指向的内容以及被析构完毕,而这时候,free函数就会出错。 解决办法是换成深拷贝。
String s1("zxc"); String s2("asd"); s2 = s1;这里的s2=s1操作事实上也是一个浅拷贝,当系统析构对象的时候,也会发生一系列问题,这里的解决方案是重载=符号,至于如何重载操作符,在后面的课程中会讲到
深拷贝指的是将引用的内存也拷贝一份,这样就能保证在析构的时候,每个引用都有自己所指向的内存可以释放
#include "iostream" using namespace std; class String { private: char * p; int length; public: String(const char * p) { this->length = strlen(p); this->p = (char*)malloc(this->length + 1); strcpy_s(this->p,this->length + 1, p); } ~String() { if (this->p) { free(this->p); this->p = NULL; this->length = 0; } } String(const String & s){ this->length = s.length; this->p = (char *)malloc(this->length+1); strcpy_s(this->p,this->length+1,p); } char * toString() { return this->p; } }; void test() { String s = "hello"; String s2 = s; } int main() { test(); system("pause"); return 0; }这里自定义拷贝构造函数,当我们直接使用String s1 = s2;的时候,这时候会调用拷贝构造函数,这里重写拷贝构造函数即可。也就是我们构造了一个深拷贝。
当A类嵌套B类的时候,构造与析构顺序是怎样的?当B类的构造函数带参数的时候,这时候在A中又如何初始化B对象的时候,又是如何初始化的呢?
class T { private: int a; public: T() { cout << "我是T的构造函数" << endl; } T(const T & t) { cout << "我是拷贝构造函数" << endl; } ~T() { cout << "我是T的析构函数" << endl; } }; class A { private: T t; public: A() { cout << "我是A的构造函数" << endl; } ~A() { cout << "我是A的析构函数" << endl; } A(const A & a) { cout << "我是A的拷贝构造函数" << endl; } }; int main() { A a; system("pause"); return 0; }先构造A中的T,然后再构造A,而析构方向完全相反 所以上面最终打印的是 我是t的构造函数 我是a的构造函数 我是a的析构函数 我是t的析构函数
class T { private: int a; public: T(int a) { this->a = a; cout << "我是T的构造函数" <<this->a<<endl; } T(const T & t) { cout << "我是拷贝构造函数" <<this->a<< endl; } ~T() { cout << "我是T的析构函数" <<this->a<<endl; } }; class A { private: T t; T t2; public: A(int a1,int a2) : t(a1),t2(a2){ cout << "我是A的构造函数" << endl; } ~A() { cout << "我是A的析构函数" << endl; } A(const A & a,int a1,int a2) :t(a1),t2(a2){ cout << "我是A的拷贝构造函数" << endl; } }; int main() { A a; system("pause"); return 0; }这里T类中必须使用参数才能初始化,所以这里在A的构造函数中使用:t(),值得注意的是,这里初始化的顺序为t,t1