C++中类的6个默认成员函数 【拷贝构造函数】

文章目录

    • 拷贝构造函数的使用
      • 拷贝构造对于自定义类型【浅拷贝】
      • 深拷贝
      • 拷贝构造函数典型调用场景

拷贝构造函数的使用

  • 在前几章学习对象的时候,我们有的时候需要一个与已存在对象一某一样的新对象

  • 那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

  • 拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用
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(2024,2,1);
	Date d2(d1);
	return 0;
}

C++中类的6个默认成员函数 【拷贝构造函数】_第1张图片

  • 下面我们解释一下:
  • 这里的const可加可不加,但是建议加上
  • 有些人就写这个写昏头了,写反了,不加const的话不会提示

C++中类的6个默认成员函数 【拷贝构造函数】_第2张图片

  • 加上了就知道哪里错了,所以还是加上较好

C++中类的6个默认成员函数 【拷贝构造函数】_第3张图片

  • 这里的拷贝构造必须传引用,要不然会引发无穷递归【反正编译会报错~】

C++中类的6个默认成员函数 【拷贝构造函数】_第4张图片

  • 再看下面代码,我是没有写拷贝构造的,但是这里自动拷贝了,毕竟这个是默认成员函数,这里生成的还和之前几个的默认成员函数还不一样,之前的对默认成员函数对内置类型不处理,而这个拷贝构造对内置类型处理了,如果没有处理,这里就拷贝不出来
  • 说明这里会自动生成一个拷贝构造函数,将值拷贝回去
  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝
class Date
{
public:
	// 构造函数
	Date(int year = 2024, int month = 2, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024, 2, 1);
	Date d2(d1);
	
	d1.Print();
	d2.Print();

	return 0;
}

C++中类的6个默认成员函数 【拷贝构造函数】_第5张图片

拷贝构造对于自定义类型【浅拷贝】

  • 那么对于自定义类型呢?
class Time
{
public:
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int year = 2024, int month = 2, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;

	// 自定义类型
	Time _t;
};
int main()
{
	Date d1(2024,2,1);
	Date d2(d1);
	
	d1.Print();
	d2.Print();
	return 0;
}

C++中类的6个默认成员函数 【拷贝构造函数】_第6张图片

  • 我们有一个方法就是强制让编译器生成

  • 加上这一条:

Time() = default;

C++中类的6个默认成员函数 【拷贝构造函数】_第7张图片

深拷贝

  • 刚刚上面的一种场景叫做浅拷贝,还有一个场景就是深拷贝

  • 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

  • 下面我这个代码没有写拷贝构造,它会自动生成一个默认的值拷贝,我们来运行一下

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _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;
}
  • 我们看到程序崩溃了~~

C++中类的6个默认成员函数 【拷贝构造函数】_第8张图片

  • 也就是在一些场景下,默认生成的拷贝构造是会出事的

  • 再调试看一下:

C++中类的6个默认成员函数 【拷贝构造函数】_第9张图片

  • 这会导致两个对象指向同一块空间

C++中类的6个默认成员函数 【拷贝构造函数】_第10张图片

  • 在结束的时候会调用析构函数,会把那块空间给释放了

C++中类的6个默认成员函数 【拷贝构造函数】_第11张图片

  • 再次释放的时候会出现错误
  • 同一块空间被释放了两次【所以是绝对不能的】

C++中类的6个默认成员函数 【拷贝构造函数】_第12张图片

  • 正确的我们这样写
  • 需要引入一个叫做深拷贝
Stack(const Stack& s)
{
	// 深拷贝
	DataType* tmp = (DataType*)malloc(s._capacity * sizeof(DataType));
	if (nullptr == tmp)
	{
		perror("malloc fail\n");
		exit(-1);
	}
	memcpy(tmp, s._array, sizeof(DataType) * s._size);
	_array = tmp;
	// 浅拷贝
	_size = s._size;
	_capacity = s._capacity;
}

C++中类的6个默认成员函数 【拷贝构造函数】_第13张图片

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝

拷贝构造函数典型调用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象
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;
};
Date Test(Date d)
{
	Date temp(d);
	return temp;
}
int main()
{
	Date d1(2022, 1, 13);
	Test(d1);
	return 0;
}

C++中类的6个默认成员函数 【拷贝构造函数】_第14张图片

  • 为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

最后本文就到这里结束了,感谢大家的收看,请多多指点~

你可能感兴趣的:(C++,c++,c)