当前位置:   article > 正文

C++中的深拷贝和浅拷贝(详解)_c++ set拷贝

c++ set拷贝

2020-07-13

拷贝构造函数是一种特殊的构造函数,在创建对象时,它是使用同一类中之前创建过的对象来初始化新创建的对象。如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。

我们在编写程序的过程中,如果不主动编写拷贝构造函数和赋值函数,编译器将会调用默认的函数,如果类中含有指针变量,那么如果使用的默认的函数就会有错误,下面首先我们先进行简单的介绍,之后再用具体的例子来加以说明。

1.拷贝构造函数和赋值函数

拷贝构造函数,顾名思义,它是一个构造函数,所以它是在对象创建的时候被主动调用的函数,可以将
另外一个对象的变量拷贝给当前对象。赋值函数,是在对象已经存在的情况下才会进行调用。
  • 1
  • 2
#include <iostream>
#include <set>
using namespace std;

class Test {

public:
    Test() { // 默认构造函数
        cout << "Test()" << endl;
    };
    Test(int v) :value(v) { // 带参数的构造函数
        cout << "Test(int v)" << endl;
    }
    Test(const Test& obj){ //拷贝构造函数
        cout << "Test(const Test& obj)" << endl;
    }
    Test& operator=(const Test& obj)
    {
        cout << "Test& operator=(const Test& obj)" << endl;
        return *this;
    }
    ~Test() { // 析构函数
    }

private:
    int value;
};

int main() {
    Test a(1);
    Test c = a;
    Test d;
    d = a;
    getchar();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

在这里插入图片描述
(1)拷贝构造函数和赋值函数

Test c = a;
d = a;
  • 1

这里第一个"=“时对象c还没有存在,所以这里调用的是拷贝构造函数,第二个”="时对象d已经存在了,所以调用的是赋值函数

(2)拷贝构造函数
拷贝构造函数是其它构造函数的重载函数,它的参数是const对象的引用,const比较容易理解,我们将一个对象拷贝给另外一个对象,那该对象的值我们是不希望被改变了的,另外一个原因是,添加 const 限制后,就可以将 const 对象和非 const 对象传递给形参了,因为非 const 类型可以转换为 const 类型,如果没有 const 限制,就不能将 const 对象传递给形参,因为 const 类型不能转换为非 const 类型,这就意味着,不能使用 const 对象来初始化当前对象了;那么这里我们为什么选择传递对象的引用作为函数的参数呢?一个原因是引用传参的时候,形参是实参的一个别名,也就是说它们俩其实是相同的,但是如果我们不传递引用的话,在进入函数的时候,会另外分配一块存储空间给形参使用,形参将会初始化实参的值,在函数调用结束的时候,这块存储空间将会被释放掉,如果说对象比较大的话,在这个初始化的过程中将会比较的耗时;还有一个十分重要的原因是我们在调用拷贝构造函数的时候,如果传递的是对象的话,由于要将实参的值赋给形参,将会调用拷贝构造函数进行赋值,那么就会形成一个死循环,如果您将上述拷贝构造函数中的"&"删除掉,程序将会报错。

(3)赋值函数
赋值函数用到的是运算符的重载,它的返回值是对象的引用,参数是const对象的引用。对于返回值而言,我们希望保留运算符原有的特性,考虑到a=b=c,这个赋值语句的顺序应该是a=(b=c),所以赋值函数重载函数的返回值应该是类的对象,返回对象的引用是因为如果返回对象的话,将会把原先对象的值赋值给临时的对象,这样还会调用一次拷贝构造函数。赋值函数的参数是对象的引用,一个原因是节省时间,另外一个原因是使函数可以传递const的对象。

#include <iostream>
using namespace std;

class Test {
public:
    Test() { // 默认构造函数
        cout << "Test()" << endl;
    };
    Test(int v) :value(v) { // 带参数的构造函数
        cout << "Test(int v)" << endl;
    }
    Test(const Test& obj){ //拷贝构造函数
        cout << "Test(const Test& obj)" << endl;
    }
    Test operator=(const Test& obj)
    {
        cout << "Test& operator=(const Test& obj)" << endl;
        return *this;
    }

    ~Test() { // 析构函数
    }

private:
    int value;
};

int main() {
    Test a(1);
    Test c = a;
    Test d;
    d = c = a;
    getchar();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这里插入图片描述
由于赋值函数的返回值是对象,这里在函数返回时就调用了拷贝构造函数。

2.何时调用拷贝构造函数

(1)定义一个对象时,以本类另一个对象作为初始值,发生拷贝构造。
(2)如果函数的形参是类的对象,调用函数时,将使用实参初始化形参,发生拷贝构造。
(3)如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化
一个临时的无名对象,传递给主调函数,发生拷贝构造。
  • 1
  • 2
  • 3
  • 4

3.浅拷贝和深拷贝

浅拷贝:如果用默认的拷贝构造函数(赋值函数)去赋值有指针类型的成员变量的对象,将会使两个对象
的指针地址也是一样的,也就是说这两个对象的指针成员变量指向的是相同的地址。
深拷贝:每个对象拥有自己的资源,此时需要显示提供拷贝构造函数和赋值函数。
  • 1
  • 2
  • 3

在这里插入图片描述
如果调用了默认的拷贝构造函数(赋值函数),在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标,这将会使同一指针指向相同的区域,同时也会导致同一块资源被释放多次,从而造成错误。
下面的例子是显示定义的赋值函数。

#include <iostream>
#include <string.h>
using namespace std;

class Test {
public:
    Test():ptr(new char[1]) { // 带参数的构造函数
        cout << "Test():ptr(new char[1])" << endl;
    }
    Test operator=(const Test& obj)
    {
        if (this == &obj) { // s=s
            return *this;
        }
        delete[] ptr;
        ptr = new char[strlen(obj.ptr) + 1];
        strcpy(ptr, obj.ptr);
        cout << "Test& operator=(const Test& obj)" << endl;
        return *this;
    }
    Test& operator=(const char *s)
    {
        delete[] ptr;
        ptr = new char[strlen(s) + 1];
        strcpy(ptr, s);
        cout << "Test& operator=(const Test& obj)" << endl;
        return *this;
    }
    ~Test() { // 析构函数
        delete[] ptr;
    }
private:
    char* ptr;
};

int main() {
    Test a;
    a= "test";
    Test b;
    b= a;
    Test d;
    getchar();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/501768
推荐阅读
相关标签
  

闽ICP备14008679号