C++入门知识-拷贝构造函数-浅拷贝、深拷贝

简介: C++入门知识-拷贝构造函数-浅拷贝、深拷贝

一、概述

拷贝构造函数特征:第一个参数是自身类类型的引用,且任何额外参数都有默认值

class Foo
{
public:
  Foo(); // 默认构造函数
  Foo(const Foo &); // 拷贝构造函数
  //...
};

注意:

如果没有为一个类定义拷贝构造函数,编译器会生成一个默认的拷贝构造函数,默认的拷贝构造函会依次将非static成员拷贝到正在创建的对象中(对于基本类型的成员变量,按字节复制;对于类类型成员变量,调用其相应类型的拷贝构造函数);

但是,当你的类含有指针类型的私有数据成员时,默认拷贝构造函数是危险的,因为它使两个对象的指针都指向了同一块内存区域,这时便是浅拷贝了。


二、什么时候会触发拷贝构造函数

1.使用同类型的对象 去创建另外一个对象;

string nines = string(100,'9');

2.把对象传入函数的非引用参数时;

void Function(string s) //调用这个函数时,实参传到这个形参s时,会触发拷贝构造
{
  // ...
}

3.把对象作为函数非引用返回值时。

string s = "hello"
string Function()
{
  // ...
  return s; // 触发拷贝构造
}

三、浅拷贝

浅拷贝小故事

张三有 100 元现金和一个房产证,房产证上写着房子的地址“魔都1巷007号”;

某天,张三就想把自己的东西复制一份给李四;

于是就复制了 100 元和一个房产证给李四,房产证上的地址还是写着“魔都1巷007号”。

后来,张三把房子卖了,李四某天也去卖房子的时候就奔溃了。

浅拷贝

就是在拷贝对象时,只是按字节地拷贝了类成员的值,对于指针或引用类型,没有拷贝其指向地具体数据过去;

就像上面故事中,只拷贝了100元和房产证,而两份房产证都是指向同一份数据(“魔都1巷007号”)。

用代码的形式表示

#include <cstdio>
#include <cstring>
#define HOUSE_LEN 64
class Person
{
  int money;    // 钱
  char *house;  // 房子
public:
  Person(int _money, char *_house)
  {
    money = _money;
    house = _house;
  }
  void Show()
  {
    printf("I have %d Yuan,and house %s\n",money, house);
  }
  void SellHouse()// 卖房子
  {
    delete[] house;
  }
};
int main()
{
  char *pHouse = new char[HOUSE_LEN];
  strncpy(pHouse, "魔都1巷007号", HOUSE_LEN);
  Person ZhangSan(100, pHouse);
  printf("I am ZhangSan\n");
  ZhangSan.Show();
  Person LiSi = ZhangSan;
  printf("I am LiSi\n");
  LiSi.Show();
  ZhangSan.SellHouse();
  printf("ZhangSan SellHouse\n");
  //LiSi.SellHouse();  // 执行这一句会使程序报错,double free
  //printf("LiSi SellHouse\n");
  return 0;
}

浅拷贝的隐患

多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏。


四、深拷贝

深拷贝小故事

张三有 100 元现金和一个房产证,房产证上写着房子的地址“魔都1巷007号”;

某天,张三就想把自己的东西复制一份给李四;

于是就复制了 100 元和一个房产证给李四,并且新建了一座房子“魔都1巷007号-2”,把李四的房产证上地址改为“魔都1巷007号-2”。

深拷贝

就是在拷贝对象时,对于指针或引用类型的成员,需要申请新的内存并把源对象成员指向的数据一起拷贝到新的内存。

就像上面故事中,拷贝了100元和房产证,并且新建了“魔都1巷007号-2”,把李四的房产证地址也改为“魔都1巷007号-2”。

用代码的形式表示

#include <cstdio>
#include <cstring>
#define HOUSE_LEN 64
class Person
{
  int money;    // 钱
  char *house;  // 房子
public:
  Person(int _money, char *_house)
  {
    money = _money;
    house = _house;
  }
  Person(const Person &person)
  {
    money = person.money;
    house = new char[HOUSE_LEN];
    snprintf(house,HOUSE_LEN, "%s-2",person.house);
  }
  void Show()
  {
    printf("I have %d Yuan,and house %s\n",money, house);
  }
  void SellHouse()// 卖房子
  {
    delete[] house;
  }
};
int main()
{
  char *pHouse = new char[HOUSE_LEN];
  strncpy(pHouse, "魔都1巷007号", HOUSE_LEN);
  Person ZhangSan(100, pHouse);
  printf("I am ZhangSan\n");
  ZhangSan.Show();
  Person LiSi = ZhangSan;
  printf("I am LiSi\n");
  LiSi.Show();
  ZhangSan.SellHouse();
  printf("ZhangSan SellHouse\n");
  LiSi.SellHouse();  // 因为有了拷贝构造函数做了深拷贝,这里不会报错
  printf("LiSi SellHouse\n");
  return 0;
}

五、总结

在设计一个类的过程中,当这个类有指针或引用类型的成员时,且可能会触发调用拷贝构造函数,就需要自己定义一个拷贝构造函数,避免编译器的默认的拷贝构造函数引起浅拷贝而导致程序面临危险。

参考资料

《C++Primer》

C++细节 深拷贝和浅拷贝(位拷贝)详解

如果对你有帮助的话,记得点赞收藏,如果有什么遗漏的或者什么体会,请在评论告诉我,好东西记得分享 ^ _ ^

目录
相关文章
|
12天前
|
编译器 C语言 C++
【C++】学习笔记——C++入门_2
【C++】学习笔记——C++入门_2
19 6
|
12天前
|
安全 编译器 C语言
【C++】学习笔记——C++入门_3
【C++】学习笔记——C++入门_3
22 4
|
12天前
|
程序员 编译器 C语言
【C++】学习笔记——C++入门_1
【C++】学习笔记——C++入门_1
22 4
|
11天前
|
安全 编译器 程序员
【C++初阶】--- C++入门(上)
【C++初阶】--- C++入门(上)
12 1
|
13天前
|
存储 编译器 C++
【C++】:拷贝构造函数和赋值运算符重载
【C++】:拷贝构造函数和赋值运算符重载
12 1
|
13天前
|
安全 编译器 C语言
C++练级之路——C++入门
C++练级之路——C++入门
11 1
|
17天前
|
C++ 容器
【C++】拷贝构造函数、拷贝赋值函数与析构函数
【C++】拷贝构造函数、拷贝赋值函数与析构函数
67 6
|
9天前
|
存储 编译器 C++
【C++】类和对象③(类的默认成员函数:拷贝构造函数)
本文探讨了C++中拷贝构造函数和赋值运算符重载的重要性。拷贝构造函数用于创建与已有对象相同的新对象,尤其在类涉及资源管理时需谨慎处理,以防止浅拷贝导致的问题。默认拷贝构造函数进行字节级复制,可能导致资源重复释放。例子展示了未正确实现拷贝构造函数时可能导致的无限递归。此外,文章提到了拷贝构造函数的常见应用场景,如函数参数、返回值和对象初始化,并指出类对象在赋值或作为函数参数时会隐式调用拷贝构造。
|
11天前
|
存储 安全 编译器
【C++初阶】--- C++入门(下)
【C++初阶】--- C++入门(下)
9 0
|
11天前
|
存储 编译器 Linux
【C++初阶】--- C++入门(中)
【C++初阶】--- C++入门(中)
11 0