cpp对象拷贝与堆中的对象实例拷贝

⌚Time: 2024-01-12 23:10:51

👨‍💻Author: Jack Ge

拷贝构造函数

拷贝构造函数是C++中的一种特殊的构造函数,用于创建一个新对象,该对象是已存在的对象的副本。

拷贝构造函数的语法如下:


class ClassName {

    // ...

public:

    ClassName(const ClassName& obj); // 拷贝构造函数声明

    // ...

};

其中,类名表示要创建的对象类型,obj是已存在的对象。

拷贝构造函数的作用是创建一个新对象,并将已存在的对象的值复制给新对象。它通常用于以下情况:

  1. 初始化一个新对象,该对象的值与已存在的对象相同。

  2. 通过值传递方式使用对象作为参数。

  3. 以值返回方式从函数中返回对象。

下面是一个示例,演示了如何定义和使用拷贝构造函数:


#include <iostream>



class MyClass {

public:

  int value;



  // 默认构造函数

  MyClass() {

    std::cout << "默认构造函数被调用" << std::endl;

    value = 0;

  }



  // 拷贝构造函数

  MyClass(const MyClass& obj) {

    std::cout << "拷贝构造函数被调用" << std::endl;

    value = obj.value;

  }

};



int main() {

  MyClass obj1;      // 调用默认构造函数

  obj1.value = 10;



  MyClass obj2(obj1);  // 调用拷贝构造函数

  std::cout << obj2.value << std::endl;



  return 0;

}

输出结果为:


默认构造函数被调用

拷贝构造函数被调用

10

在上述示例中,我们定义了一个名为MyClass的类,并在其中定义了默认构造函数和拷贝构造函数。我们首先调用默认构造函数创建了一个对象obj1,然后通过将obj1传递给拷贝构造函数来创建了一个新对象obj2,最后输出了obj2的值。可以看到,obj2的值与obj1的值相同。

如果不为类定义拷贝构造函数,C++会提供一个默认的拷贝构造函数,其作用是将对象的每个成员变量进行逐一的拷贝。

触发条件:拷贝构造函数在以下情况下被自动调用:

等于号运算符

等于号赋值(Assignment Operator)是一个重载运算符,用于将一个对象的值赋给另一个同类型的对象。

等于号赋值运算符的参数为一个同类型的对象的引用,并返回一个指向当前对象的引用。在等于号赋值运算符的实现中,可以通过引用对象的成员变量或者调用对象的成员函数,将数据从源对象赋值给目标对象。例如:


ClassName& ClassName::operator=(const ClassName& other) {

    // 等于号赋值逻辑

    // ...

    return *this;

}

总结

==拷贝构造函数和等于号赋值在对象赋值和对象初始化过程中起着重要的作用,可以保证对象的正确复制和赋值。在自定义类中,如果没有手动定义拷贝构造函数和等于号赋值运算符,编译器会自动生成默认的拷贝构造函数和等于号赋值运算符。==

虽然拷贝构造函数和赋值运算符重载函数都可以用来复制对象,但它们的主要区别在于其使用场景:

深拷贝

对于含有动态分配的资源(如堆内存)的类对象,需要手动实现拷贝构造函数和等于号赋值运算符,以避免资源泄漏或访问错误。

下面是一个示例,展示如何手动实现类的拷贝构造函数和等于号赋值运算符:


#include <iostream>



class DynamicResource {

public:

    DynamicResource(int size) {

        data = new int[size];

        this->size = size;

    }



    ~DynamicResource() {

        delete[] data;

    }



    // 拷贝构造函数

    DynamicResource(const DynamicResource& other) {

        size = other.size;

        data = new int[size];

        std::copy(other.data, other.data + size, data);

    }



    // 等于号赋值运算符

    DynamicResource& operator=(const DynamicResource& other) {

        if (this == &other) {

            return *this;

        }



        delete[] data;

        size = other.size;

        data = new int[size];

        std::copy(other.data, other.data + size, data);



        return *this;

    }



    void setData(int index, int value) {

        data[index] = value;

    }



    int getData(int index) const {

        return data[index];

    }



private:

    int* data;

    int size;

};



int main() {

    DynamicResource resource1(5);

    resource1.setData(0, 10);

    resource1.setData(1, 20);

    

    DynamicResource resource2 = resource1; // 使用拷贝构造函数

    std::cout << resource2.getData(0) << ", " << resource2.getData(1) << std::endl;



    DynamicResource resource3(3);

    resource3 = resource1; // 使用等于号赋值运算符

    std::cout << resource3.getData(0) << ", " << resource3.getData(1) << std::endl;



    return 0;

}

在这个示例中,DynamicResource 类拥有一个动态分配的整型数组 data,数组的大小由构造函数参数传入。拷贝构造函数和等于号赋值运算符都会复制 data 数组内容,并创建一个相同大小的数组。析构函数用于释放 data 数组的内存。

main 函数中,我们创建了一个 resource1 对象,并为其数组元素赋值。然后,我们使用拷贝构造函数和等于号赋值运算符将 resource1 的内容复制给 resource2resource3。最后,我们打印了 resource2resource3 中数组的元素,以验证拷贝和赋值的正确性。

这个示例展示了手动实现拷贝构造函数和等于号赋值运算符的基本原则:复制动态资源,并避免资源泄漏和重复释放。

堆中对象实例的拷贝


class Base{

    ...

};

class A:public Base{

    ...

};

class B:public Base{

    ...

};

class C ...

如果有一个堆中的对象实例,我想得到他的一份拷贝,如果是

错误的方法


A * pa = new A;

...

Base *p;

p  = pa;

你得不到拷贝,两个指针指向的是同一个实例

如果你想要


Base *p = new A;

*p = *pa;

你不会得到正确的结果,因为他只会拷贝基类的数据部分而忽略了子类的数据部分

正确的拷贝方法


A *p = new A;

*p = *pa;

或者


Base *p = new A;

memcpy(p,pa,sizeof(A));

你会得到正确的拷贝,但是如果你不确定要拷贝的是哪一个子类?

使用多态技术拷贝实例

用下面的办法,在基类中定义一个拷贝函数作为纯虚函数,放到各个子类中实现




#include <iostream>

class Base{



public:

    Base(){}

    virtual ~Base(){}

public:

    virtual Base* get_copy() = 0;

};

class A:public Base{

public:

    A(){}

    virtual ~A(){}

public:

    void set_data(int value){

        data = value;

    }

    void print_data(){

        std::cout<<data<<std::endl;

    }

    virtual Base* get_copy(){

        Base* pb = new A;

        memcpy(pb,this,sizeof(A));

        //*pb = *this;//不好用

        //如果类里有动态分配的空间,也要进行分配和拷贝

        //...

        return pb;

    }

protected:

    int data;

};

int main(){

    A *pa = new A;

    pa->set_data(9993);

    Base *pb;

    pb = pa->get_copy();

    dynamic_cast<A*>(pb)->print_data();

    return 0;

}


编译运行测试


g++ t.cpp

a.exe

9993

得到堆中实例了一样的拷贝