一、拷贝构造
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
在创建对象时,也可以创建一个与一个对象一模一样的新对象。
1.概念
构造函数:只有单个形参,该形参是对本类 类型对象的引用(一般常用const修饰),再用已存在的类类型对象创建新对象时由编译器自动调用。
2.特性
拷贝构造函数也是特殊的成员函数,其特性如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参。(使用传值方式会引发无穷递归调用)
代码示例:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
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; }
此时,d2对d1完成拷贝构造
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
代码示例:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
// 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的
Date d2(d1);
return 0; }
二、赋值重载
具体用法通过日期类进行练习
1.运算符重载
引入:
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名:关键字operator + 需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
———————————————————————
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
- . 、* 、:: 、sizeof 、?: 以上5个运算符不能重载。
2.赋值运算符
- 参数类型
- 返回值
- 检测是否自己给自己赋值
- 返回*this
- 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
补充
在C++中,赋值重载(或称为赋值操作符重载)是指为自定义类型(如类)重载赋值操作符(=)的行为。默认情况下,C++中的赋值操作符对于内置类型(如int、float、char等)是有效的,但当创建自定义类型时,可能需要定义自己的赋值行为。
赋值操作符重载函数通常具有以下形式:
ClassName& operator=(const ClassName& other);
这里有几个关键点:
- 返回类型:通常返回对调用对象的引用(ClassName&),这允许连续赋值(链式赋值)。
- 参数:通常接受一个常量引用参数,该参数是待赋值对象的副本。
- 自赋值检查:在赋值操作符的实现中,通常需要检查调用对象(*this)和参数对象(other)是否是同一个对象,以避免自赋值导致的潜在问题。
- 资源释放:如果类管理资源(如动态分配的内存),则在赋值之前可能需要释放这些资源。
- 资源分配:将参数对象的值复制到调用对象中,可能需要分配新的资源。
- 返回*this:最后,返回调用对象的引用,以支持链式赋值。
以下是一个简单的示例,展示了如何为一个管理动态数组的类重载赋值操作符:
#include <iostream>
class MyArray {
private:
int* data;
size_t size;
public:
// 构造函数、析构函数、拷贝构造函数等...
// 赋值操作符重载
MyArray& operator=(const MyArray& other) {
// 自赋值检查
if (this == &other) {
return *this;
}
// 释放当前对象的资源
delete[] data;
// 分配新资源并复制数据
size = other.size;
data = new int[size];
for (size_t i = 0; i < size; ++i) {
data[i] = other.data[i];
}
// 返回调用对象的引用
return *this;
}
// 其他成员函数...
};
int main() {
MyArray arr1{/* 初始化arr1 */};
MyArray arr2{/* 初始化arr2 */};
// 使用重载的赋值操作符
arr1 = arr2;
// 链式赋值(如果其他类也重载了赋值操作符)
MyArray arr3;
arr3 = arr1 = arr2; // 先执行arr1 = arr2,然后执行arr3 = arr1的结果
return 0;
}
请注意,在实际应用中,你可能还需要考虑异常安全性、性能优化和深拷贝与浅拷贝的问题。此外,如果类管理资源,通常还需要实现拷贝构造函数、移动构造函数、拷贝赋值操作符和移动赋值操作符,以提供完整的资源管理语义。这通常被称为“规则五”(Rule of Five)或“规则零”(Rule of Zero),后者推荐使用智能指针(如std::unique_ptr或std::shared_ptr)来自动管理资源。**