• C++拷贝构造函数


    拷贝构造函数

    每个类都有多个构造函数和一个析构函数。此外,类还可以有一个拷贝构造函数(copy constructor)。

    拷贝构造函数用于拷贝对象。也就是说,这个构造函数的参数是这个类的对象,执行这个构造函数之后,会产生一个与原来对象一样的对象(实际上是用原来对象的数据初始化了一个对象)。

    构造函数的签名如下:

    ClassName (const ClassName&)

    下面给出一个栗子:

    Circle.h

    class Circle {
    public:
        //无参构造函数
        Circle();
        //有参构造函数
        Circle(double);
        //析构函数
        ~Circle();
        //获取圆的面积
        double getArea();
        //获取圆的半径
        double getRadius();
    
    private:
        //圆的半径
        double radius;
        //定义常量圆周率
        const double PI = 3.14159;
        //对象个数
        static int objectNumber;
    };

    Circle.cpp

    #include "Circle.h"
    #include <iostream>
    
    using namespace std;
    
    int Circle::objectNumber = 0;
    
    //无参构造函数
    Circle::Circle() {
        this->radius = 1;
        objectNumber++;
        cout << "当前对象的个数是" << objectNumber << endl;
    }
    
    //有参构造函数
    Circle::Circle(double radius) {
        this->radius = radius;
        objectNumber++;
        cout << "当前对象的个数是" << objectNumber << endl;
    }
    
    //析构函数
    Circle::~Circle(){
        objectNumber--;
        cout << "当前对象的个数是" << objectNumber << endl;
    }
    
    //获取面积
    double Circle::getArea() {
        return radius*radius*PI;
    }
    
    //获取半径
    double Circle::getRadius(){
        return this->radius;
    }

    main.cpp

    #include <iostream>
    #include "Circle.h"
    
    using namespace std;
    
    int main()
    {
        //调用无参构造函数
        cout << "创建对象p1" << endl;
        Circle* p1 = new Circle(2);
    
        //调用拷贝构造函数
        cout << "创建对象p2" << endl;
        Circle* p2 = new Circle(*p1);
    
        //调用有参构造函数,参数为3
        cout << "创建对象p3" << endl;
        Circle* p3 = new Circle(3);
    
        cout << "-------------------------------"<< endl;
    
        cout << "p1半径为" << p1->getRadius() << endl;
        cout << "p2半径为" << p2->getRadius() << endl;
        cout << "p3半径为" << p3->getRadius() << endl;
    
        cout << "-------------------------------"<< endl;
        //销毁p1
        cout << "销毁对象p1" << endl;
        delete p1;
    
        //销毁p2
        cout << "销毁对象p2" << endl;
        delete p2;
    
        //销毁p3
        cout << "销毁对象p3" << endl;
        delete p3;
    
    
        return 0;
    }

    运行结果:

    由于我们调用的是缺省的拷贝构造函数,所以创建p2时,对象的个数没有加1。

    还有一个问题是,我们调用的拷贝构造函数,是将p1的地址给了p2,还是新开辟了内存?

    更改一下main.cpp

    #include <iostream>
    #include "Circle.h"
    
    using namespace std;
    
    int main()
    {
        //调用无参构造函数
        cout << "创建对象p1" << endl;
        Circle p1(2);
    
        //调用拷贝构造函数
        cout << "创建对象p2" << endl;
        Circle p2(p1);
    
        //调用有参构造函数,参数为3
        cout << "创建对象p3" << endl;
        Circle p3(3);
    
        cout << "-------------------------------"<< endl;
    
        cout << "p1半径为" << p1.getRadius() << endl;
        cout << "p1地址为" << &p1 << endl;
        //销毁p1
        cout << "销毁对象p1" << endl;
        delete &p1;
         cout << "-------------------------------"<< endl;
        cout << "p2半径为" << p2.getRadius() << endl;
        cout << "p2地址为" << &p2 << endl;
        //销毁p2
        cout << "销毁对象p2" << endl;
        delete &p2;
         cout << "-------------------------------"<< endl;
        cout << "p3半径为" << p3.getRadius() << endl;
        //销毁p3
        cout << "销毁对象p3" << endl;
        delete &p3;
    
        return 0;
    }

    运行结果:

     

    可以看到,p1和p2有着两个不同的地址,而且p1被delete之后,p2的值还在。所以,拷贝构造函数会新开辟内存空间。

    自定义拷贝构造函数

    接下来解决之前对象个数不一致的问题。为了能让我们的对象个数在拷贝构造时也变化,我们就要自定义一个拷贝构造函数。

    首先在Circle.h的public下添加:

    //拷贝构造函数
    Circle (const Circle&);

    然后在Circle.cpp中添加函数体:

    //拷贝构造函数
    Circle::Circle(const Circle& c) {
        this->radius = c.radius;
        objectNumber++;
        cout << "拷贝成功" << endl;
        cout << "当前对象的个数是" << objectNumber << endl;
    }

    然后再次运行main.cpp(main.cpp不变)

    OK,现在就符合实际情况了。

    深拷贝和浅拷贝

    之前我们说到的都是浅拷贝。也就是说,如果类中有一个指针变量,在拷贝时,指针变量的值不会改变。这时,两个虽然两个对象具有不同的地址,但是两个对象的指针却指向同一个地址。当一个对象被销毁时,它的指针指向的内存也会销毁。因此,另一个对象销毁时,就会产生错误。

    由此引出深拷贝。即让指针不指向同一个地址,这时就一定要自定义拷贝构造函数,给新对象的指针分配内存了。

     (顺便一提,浅拷贝构造函数相当于用=,也就是说Circle p2(p1)也可以写为Circle p2 = p1)

    假设我的类中有一个指针p,下面是浅拷贝构造函数:

    //浅拷贝构造函数
    Circle::Circle(const Circle& c) {
        this->radius = c.radius;
        this->p = c.p;
        objectNumber++;
        cout << "拷贝成功" << endl;
        cout << "当前对象的个数是" << objectNumber << endl;
    }

    此时,没有为新对象的指针分配内存

    main.cpp如下:

    #include <iostream>
    #include "Circle.h"
    
    using namespace std;
    
    int main()
    {
        //调用无参构造函数
        cout << "创建对象p1" << endl;
        Circle p1(2);
    
        //调用有参构造函数,参数为1
        cout << "创建对象p2" << endl;
        Circle p2(p1);
    
    
        cout << "-------------------------------"<< endl;
    
    
        cout << "p1的指针p的值为" << p1.p << endl;
    
         cout << "-------------------------------"<< endl;
    
        cout << "p2的指针p的值为" << p2.p << endl;
    
    
        return 0;
    }

    运行结果:

    可以看到,两个指针的值都是0x82a978

    再看深拷贝的拷贝构造函数:

    //深拷贝构造函数
    Circle::Circle(const Circle& c) {
        this->radius = c.radius;
        //先开辟内存
        this->p = new int;
        //再赋值
        *p = *(c.p);
        objectNumber++;
        cout << "拷贝成功" << endl;
        cout << "当前对象的个数是" << objectNumber << endl;
    }

    main.cpp同上

    运行结果:

     此时两个指针指向的就是不同的地址了。

  • 相关阅读:
    this指向详解
    领域驱动设计-1-概述
    算法 表达式求值
    进制转换
    IDEA Junit FileNotFoundException: class path resource [spring/spring.xml] cannot be opened because it does not exist
    aes加密示例
    create an oauth app
    搭建docusaurus博客
    Vue项目整体架构记要
    vue+element 获取验证码
  • 原文地址:https://www.cnblogs.com/bwjblogs/p/12740812.html
Copyright © 2020-2023  润新知