C++快速入门--5

浅拷贝

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值