我们都知道面向对象的对象是一个宏观的概念, 万事万物都可以当成一个对象。而现实中我们的对象是可以复制的,那么我们在编程中创建的对象如何进行复制呢?
在C++中祖师爷规定了:当我们想把一个对象赋值给另一个对象的时候
如图所见拷贝构造函数是我们的六大成员默认函数之一,构造函数的作用是初始化,析构函数是复制清理工作,而我们的构造拷贝函数是用来同类对象进行赋值给另一个对象时的工作:
讲了怎么长时间拷贝构造是干什么的,下面就来到拷贝构造的创建把: 其实构造的前几个特征是需要先了解才能去书写的所以博主这里把他都给整合到前面了,后面的其他特征单独介绍:
这里第二个特征就特别强调了,我们在书写拷贝构造函数的时候一定要使用传引用
🍸 代码演示:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date d) // 错误写法:编译报错,会引发无穷递归
Date(const Date& d) // 正确写法
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
构造拷贝函数既然是六个默认成员函数之一的话,那么肯定也是符合默认成员函数的特点如果我们没有显示定义的话自动生成:
🍸 代码演示:
这里我们就可以看到就算我们不写默认成员函数那么编译器也会自动生成
默认拷贝构造函数
去拷贝和赋值,这是可能就有人要问了既然默认生成的拷贝构造函数
可以完成复制那么为什么要我们手动创建呢?
🔥 这是因为默认生成的拷贝构造函数完成的只是浅拷贝,只是把值复制过去了
说到浅拷贝和深拷贝很多铁汁们不太明白,什么是浅拷贝?深拷贝拷贝了那些内容?
🍸 代码演示:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (int*)malloc(capacity * sizeof(int));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const int& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
int* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}
这里就是我们说的浅拷贝了,为什么程序回出现崩溃呢?这里刚开始创建了一个S1 对象,又创建了一个S2 对象去进行调用拷贝构造函数进行拷贝:
这里就是我们说的浅拷贝,S2 和 S1 指向用一块空间而当程序结束的时候 S2 调用它的析构函数去吧 S1 所指向的空间给释放了, 那么当 S1 在释放的时候就重复释放了原来释放的空间导致程序崩溃。
🔥 所以在这些去动态申请资源的函数的类去,一定要显示定义拷贝构造函数进行深拷贝
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (int*)malloc(capacity * sizeof(int));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
Stack(const Stack& p1)
{
int* tmp = (int*)malloc(sizeof(int) * p1._capacity);
if (tmp == nullptr)
{
perror("file malloc");
exit(-1);
}
memcpy(tmp, p1._array, sizeof(int) * p1._size);
_array = tmp;
_capacity = p1._capacity;
_size = p1._size;
}
void Push(const int& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
int* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}
如果我们一个类里面并不会去申请资源那么它的默认生成的拷贝构造函数 ,也就够用了。默认生成的拷贝构造函数只会进行值拷贝而我们在不申请资源的话,值拷贝就是我们需要的功能。
到了这里我相信大家一定对靠北构造函数有一定认知了那么大家知道拷贝函数在哪些场景会自动调用呢?
这个就是最常见的场景了,使用已存在的对象去创建新对象。
🍸 代码演示:
class Date
{
public:
Date(int year, int minute, int day)
{
cout << "Date(int,int,int):" << this << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d):" << this << endl;
}
~Date()
{
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022,1,13);
Test(d1);
return 0;
}
在以前学习函数的时候我们知道,形参是实参的一份临时拷贝所以当函数参数类型为 类 的类型对象的话也会自动调用 拷贝构造函数
🍸 代码演示:
Date Test(Date d)
{
Date temp(d);
return temp;
}
函数的返回值返回给我们调用的对象时候,返回的是 temp 的一份临时拷贝并不是对象本身
🍸 代码演示:
Date Test(Date d)
{
Date temp(d);
return temp;
}