C++ 拷贝构造函数

拷贝构造函数(Copy Constructor)是一个特殊的构造函数,用于创建一个类的对象,并用另一个同类型的对象来初始化它。如果类包含动态分配的资源,通常需要实现 深拷贝,以避免共享资源引发的问题。编译器会为类生成一个默认的 浅拷贝 拷贝构造函数,但在有动态资源时,手动实现深拷贝是必要的。本文主要介绍C++ 拷贝构造函数。

1、拷贝构造函数

拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。

格式
拷贝构造函数的声明:
class 类名
{
public:
类名(形参参数)//构造函数的声明/原型
类名(类名& 对象名)//拷贝构造函数的声明/原型
...
};
拷贝构造函数的实现:
类名::类名(类名& 对象名)//拷贝构造函数的实现/定义
{函数体}

例如,

#include <iostream>
using namespace std;
class Point
{
    public:
       Point(int xx,int yy){X=xx;Y=yy;}  
       Point(const Point& p); 
       int getX(){return X;}
       int getY(){return Y;} 
   private:
       int X,Y;
};
Point::Point(const Point& p)
{
    X=p.X;
    Y=p.Y;
    std::cout<<"拷贝构造函数调用"<<std::endl;
}
int main()
{
    Point point(1,1);
    Point p=point;
    return 0;
}

 调用拷贝构造函数有以下三种情况:

1)用类的一个对象去初始化另一个对象时。

2)对象作为函数参数传递时,调用拷贝构造函数

3)如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数。

2、隐式拷贝构造函数

如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会自动生成一个来进行对象之间非static成员的位拷贝(Bitwise Copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数,除非另外一个构造函数在类初始化或构造列表的时候被调用。

#include <iostream>
using namespace std;

class Test {
private:
    int* data;  // 动态分配的资源

public:
    // 构造函数
    Test(int val) {
        data = new int(val);  // 动态分配内存
        cout << "构造函数:为 " << *data << " 分配内存" << endl;
    }

    // 析构函数
    ~Test() {
        delete data;  // 释放内存
        cout << "析构函数:销毁对象" << endl;
    }

    // 显示数据
    void showData() const {
        cout << "数据: " << *data << endl;
    }
};

int main() {
    Test obj1(10);    // 调用构造函数,创建 obj1
    Test obj2 = obj1; // 隐式调用拷贝构造函数(浅拷贝)

    obj2.showData();  // 显示 obj2 数据

    return 0;  // 当 obj1 和 obj2 离开作用域时,析构函数会被调用
}

3、深拷贝和浅拷贝

由于C++提供的默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。一般情况下,只需要使用系统提供的浅拷贝构造函数即可,但是,如果对象的数据成员包括指向堆空间的指针,就不能使用这种拷贝方式。因为两个对象都拥有同一个资源,对象析构时,该资源将进行两次资源返还,此时必须自定义深拷贝构造函数,为创建的对象分配堆空间,否则会出现动态分配的指针变量悬空的情况。深拷贝需要同时复制对象空间和资源。 深拷贝就是如果一个类拥有资源,在它的对象之间进行发生复制的过程时,采用资源重新分配这个过程就是深拷贝。否则就是浅拷贝。

1)浅拷贝

#include <iostream>
using namespace std;

class Test {
private:
    int* data;  // 动态分配的资源

public:
    Test(int val) {
        data = new int(val);  // 动态分配内存
    }

    // 浅拷贝构造函数
    Test(const Test& other) : data(other.data) {}

    ~Test() {
        delete data;  // 释放内存
    }

    void showData() const {
        cout << "数据: " << *data << endl;
    }
};

int main() {
    Test obj1(10);    // 创建 obj1
    Test obj2 = obj1; // 调用浅拷贝构造函数

    obj2.showData();  // 显示 obj2 数据

    return 0;
}

2)深拷贝

#include <iostream>
using namespace std;

class Test {
private:
    int* data;

public:
    // 构造函数,直接初始化成员
    Test(int val) : data(new int(val)) {}  
    // 深拷贝构造函数
    Test(const Test& other) : data(new int(*other.data)) {}  

    ~Test() { delete data; }  // 析构函数

    void showData() const {
        cout << "数据: " << *data << endl;
    }
};

int main() {
    Test obj1(10);
    Test obj2 = obj1;

    obj2.showData();

    return 0;
}

4、编译器自动生成拷贝构造函数

如没有提供自定义的拷贝构造函数,C++ 编译器会自动生成一个 浅拷贝 的拷贝构造函数,即简单地将对象的每个成员复制到新对象中。如果类中有指针类型的成员,且不进行深拷贝,就可能出现资源共享问题。

#include<iostream>
using namespace std;

class Test {
private:
    int* data;

public:
    Test(int val) {
        data = new int(val);
    }

    // 编译器自动生成的拷贝构造函数(浅拷贝)
    // Test(const Test& other) = default;

    ~Test() {
        delete data;
    }
};

int main() {
  cout << "Hello, World!";
  return 0;
}

推荐阅读
cjavapy编程之路首页