C++学习(4):拷贝构造函数

前言:

先来看C++对象的赋值

Student xiaoming = new Student(“一年级”,“一班”);
Student xiaohong = xiaoming;

C++支持对象的赋值,上面的代码是没有问题的,对象xiaohong拥有和对象xiaoming一样的属性(同样是“一年级一班”)。

问题1:以上代码对象的赋值是怎么实现的?
答:一个对象xiaohong的生成,一般我们会想到构造函数。但是构造函数往往伴随着类成员属性的初始化,而且对象xiaoming初始化后,属性还可以改变,所以构造函数这条路就行不通了。C++编译器提供了一种特殊的构造函数,即默认拷贝构造函数,默认拷贝构造函数对对象中的属性进行依次拷贝。

问题2:拷贝构造函数什么时候被调用?
答:1、对象在创建时使用其他的对象初始化时;2、对象作为函数的参数进行值传递时。

问题3:拷贝构造函数的使用会引入什么风险?
答:会引入“深拷贝”和“浅拷贝”问题带来的重复释放资源的错误。

问题4:如何规避风险?
答:可以自定义拷贝构造函数,增加分配资源操作。

正文

1、先看一段代码,这段代码展示了拷贝构造函数和赋值运算符的使用方法。

// copyConstructorTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>

class Student {
public:
    //构造函数 constructor
    Student() {
        std::cout << "constructor!" << std::endl;
    }

    //析构函数 destructor
    ~Student() {
        std::cout << "destructor!" << std::endl;
    }

    //拷贝构造函数 copy constructor
    Student(const Student& stu) {
        std::cout << "copy constructor!" << std::endl;
    }

    //赋值运算符 assignment operator
    Student& operator=(const Student&) {
        std::cout << "assignment operator!" << std::endl;
        return *this;
    }
private:
    std::string name;
    int age;
};

int main()
{
    Student s1;
    Student s2 = s1; //s2是新生成对象,调用拷贝构造函数
    
    Student s3;
    s3 = s1;  //s3是已存在对象,调用赋值运算符
    return 0;
}


2、深拷贝和浅拷贝
上述代码中,类的成员变量都是基本数据类型,在复制的时候,会新开辟一块内存来存值,这种拷贝方式叫做深拷贝。但是,如果类成员中有指针变量,则会出现浅拷贝,即同一块内存多了一个指针指向它,这样一来,在释放资源的时候,释放第一个指针时把内存释放,在释放第二个指针的时候,内存已经不存在了,所以报错。如下代码:

// copyConstructorTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>

class Student {
public:
    //构造函数 constructor
    Student() {
        std::cout << "constructor!" << std::endl;
    }

    //析构函数 destructor
    ~Student() {
        std::cout << "destructor!" << std::endl;
        delete address; // 浅拷贝会重复释放指向同一块内存的2个指针
    }

    //拷贝构造函数 copy constructor
    Student(const Student& stu) {
        std::cout << "copy constructor!" << std::endl;
    }

    //赋值运算符 assignment operator
    Student& operator=(const Student&) {
        std::cout << "assignment operator!" << std::endl;
        return *this;
    }
private:
    std::string name;
    int age;
    char* address; //如果类成员变量中有指针,则涉及到浅拷贝
};

int main()
{
    Student s1;
    Student s2 = s1; //s2是新生成对象,调用拷贝构造函数
    
    Student s3;
    s3 = s1;  //s3是已存在对象,调用赋值运算符
    return 0;
}


3、解决指针浅拷贝的问题
解决方法就是在拷贝构造函数中,为指针新开辟一块内存空间,如下代码;

// copyConstructorTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>
#include<string>

class Student {
public:
    //构造函数 constructor
    Student(std::string name, int age, char* address) {
        this->name = name;
        this->age = age;

        this->address = new char[strlen(address) + 1]; //指针在构造时要开辟内存空间
        strcpy(this->address, address);
        std::cout << "constructor!" << std::endl;
    }

    //析构函数 destructor
    ~Student() {
        std::cout << "destructor!" << std::endl;
        delete[] address; // "[]"大括号加不加要看new的时候有没有
    }

    //拷贝构造函数 copy constructor
    Student(const Student& stu) {
        this->name = stu.name; //拷贝构造函数也是构造函数,所有成员变量都要复制
        this->age = stu.age;
        std::cout << "copy constructor!" << std::endl;
        
        this->address = new char[strlen(stu.address) + 1]; //深拷贝,即内存也要复制一份
        strcpy(this->address, stu.address);
    }

    //赋值运算符 assignment operator
    Student& operator=(const Student&) {
        std::cout << "assignment operator!" << std::endl;
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, Student& stu) {
        os << stu.name << ":" << stu.age << ":" << stu.address << std::endl;
        return os;
    }
private:
    std::string name;
    int age;
    char* address; //如果类成员变量中有指针,则涉及到浅拷贝
};

int main()
{
    Student s1("XiaoMing", 20, "XXX");
    std::cout << s1 << std::endl;
    Student s2 = s1; //s2是新生成对象,调用拷贝构造函数
    std::cout << s2 << std::endl;

    Student s3("XiaoHong", 22, "YYY");
    std::cout << s3 << std::endl;
    s3 = s1;  //s3是已存在对象,调用赋值运算符
    std::cout << s3 << std::endl;

    return 0;
}


总结

如果你的类里面有指针变量,就要考虑深浅拷贝带来的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值