文章目录

  • ​​1.复制构造函数​​
  • ​​2.合成复制构造函数​​
  • ​​3.使用复制构造函数的3种情况​​
  • ​​3.深复制与浅复制​​

1.复制构造函数

  • 复制构造函数又称为拷贝构造函数, 它是一种特殊的构造函数。
    它的作用就是用一个已经生成的对象来初始化另一个同类的对象。
  • 变量的初始化:
    int a=10; int b=a;
  • 对象的初始化:
    Point pt1(10,20); Point pt2=pt1;
  • 复制构造函数定义的一般形式为:
类名(const 类名& obj)
{
函数体
}
  • eg:默认构造函数,复制构造函数,带参数的构造函数的对比
class Point 
{
public:
Point():x(0), y(0){}//默认构造函数
Point(const Point& r): x(r.x), y(r.y){}//复制构造函数
Point(int a, int b): x(a), y(b){}//带参数的构造函数
private:
int x,y;
};
  • 复制构造函数的特点:
    (1)复制构造函数有且只有一个本类类型对象的引用形参, 通常使用const限定。
    因为复制构造函数只是复制对象, 没有必要改变传递来的对象的值。
    (2)复制构造函数的功能是利用一个已知的对象来初始化一个被创建的同类的对象。
    (3)与复制构造函数对应的对象的定义形式为:
类名 对象名1(类对象1), 对象名2(类对象2),......;
  • 对象赋值和对象复制的区别:
    (1)对象的赋值是对一个已经存在的对象赋值, 因此必须先定义被赋值的对象, 才能进行赋值。
    (2)对象的复制则是从无到有地建立一个新对象, 并使它与一个已有的对象完全相同(包括对象的结构和成员的值) 。

2.合成复制构造函数

  • 每个类必须有一个复制构造函数。
    如果类没有定义复制构造函数,编译器就会自动合成一个, 称为合成复制构造函数(synthesized copy constructor) 。
  • 与合成默认构造函数不同, 即使定义了其他构造函数, 编译器也会合成复制构造函数。
  • 合成复制构造函数的操作是: 执行逐个成员初始化, 将新对象初始化为原对象的副本
    (1)所谓“逐个成员” , 指的是编译器将现对象的每个非静态数据成员,依次复制到正创建的对象中。 每个成员的类型决定了复制该成员的含义:
    ①内置类型成员直接复制其值,eg:内置类型:int, char。。。。;
    ②类类型成员使用该类的复制构造函数进行复制;
    ③如果一个类具有数组成员, 则合成复制构造函数将复制数组, 即复制数组的每一个元素到新对象中
    (2)逐个成员初始化可以这样理解: 将合成复制构造函数看作是每个数据成员在构造函数初始化列表中进行初始化的构造函数

3.使用复制构造函数的3种情况

(1)用一个对象显式或隐式初始化另一个对象。

  • C++支持两种初始化形式: 复制初始化和直接初始化。 复制初始化使用等号(=) , 而直接初始化将初始化式放在圆括号中。
  • 复制初始化和直接初始化是有区别的:
    直接初始化会调用与实参匹配的构造函数;
    而复制初始化总是调用复制构造函数。
Point pt1(10,20);/定义了Point类型的对象pt1
Point pt2=pt1;//复制初始化
Point pt3(pt1);//直接初始化

(2)函数参数按值传递对象时或函数返回对象时。

  • 当函数形参为对象类型, 而非指针和引用类型时, 函数调用按值传递对象, 即编译器调用复制构造函数产生一个实参对象副本传递到函数中。
  • 类似地, 以对象类型作为返回值时, 编译器调用复制构造函数产生一个return语句中的值的副本返回到调用函数。

(3)根据元素初始化式列表初始化数组元素时

  • 如果没有为类类型数组提供元素初始化式, 则将用默认构造函数初始化每个元素。 然而, 如果使用常规的大括号的数组初值列表形式来初始化数组时, 则使用复制初始化来初始化每个元素。
  • 总的来说, 正是有了复制构造函数, 函数才可以传递对象和返回对象, 对象数组才能用初值列表的形式初始化

3.深复制与浅复制

(1)如果一个拥有资源(如用new得到的动态内存) 的类对象发生复制的时候,
若对象数据与资源内容一起复制, 称为深复制, 如图所示:

(28.2)复制构造函数和合成复制构造函数_构造函数

(2)若复制对象但未复制资源内容称为浅复制, 如图所示

  • 说明:将a对象的数据成员p拷贝到b对象的数据成员p,指针存放的是地址,表面两者指向同一块的内存区域;
    若先撤销a,再撤销b,会造成对同一块内存free两次。
  • 系统自动生成的复制构造函数都是浅复制,类成员有指针,就会出现问题。

    (3)eg:
#include <iostream>
#include <string.h>
using namespace std;
class CA
{
public:
CA(int b, char *cstr)//构造函数
{
a=b;
str=new char[b];
strcpy(str, cste);
}

CA(const CA &C)//复制构造函数
{
a=C.a;
str=new char[a];//这里是深复制,浅复制则写成:str=C.str;
if (str!=0)
strcpy(str, C.str);
}
void show()
{
cout<<str<<endl;
}

/*
将各个对象中str所指向的字符数组进行撤销,
若是浅拷贝,会将对象a和对象b的str所指向的字符数组撤销两次,
这是错误的!!!
*/
~CA()//析构函数
{
delete str;
}
privat:
int a;
char *str;
};

int main()
{
CA a(10,"hello");
CA b=a;
b.show();
return 0;
}