浅拷贝
在上一节的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