类和对象中篇——默认成员函数总结

目录
默认成员函数
1. 构造函数
2. 析构函数
3. 拷贝构造函数
拷贝构造的无限递归问题
深拷贝与浅拷贝
运算符重载函数
前置运算符与后置运算符的重载
4. 赋值运算符重载
const成员
5/6. 取地址重载函数
默认成员函数
在类中有一些特殊的成员函数,称为默认成员函数它们用来执行常见的操作,例如对象的初始化、清理、拷贝和赋值等 , 默认成员函数区别于其他成员函数的地方在于就算我们不写编译器也会默认生成 。
默认成员函数有6个:
注:默认成员函数只能是类成员,你猜它们为啥叫默认成员函数
1. 构造函数
介绍
构造函数是默认成员函数的一种 , 用于在创建对象时对其进行初始化操作 。
特性
Date() //构造函数{_day = 0;_month = 0;_year = 0;}
【类和对象中篇——默认成员函数总结】void TestDate(){Date d1(2023,6,1); //调用带参的构造函数Date d2; //调用无参构造函数//不能写成Date d2()否则编译器无法区分是函数声明还是变量定义}
Date(int year = 0, int month = 0, int day = 0) //全缺省{_day = year;_month = month;_year = day;}Date() //无参{_year = 0;_month = 0;_day = 0;}
class Date{public: int _year = 0; //在声明时给一个缺省值int _month = 0;int _day = 0;};
2. 析构函数
介绍
构造函数是默认成员函数的一种 , 用于执行对象清理和资源释放的操作 。
特性
~Date() //析构函数{_year = 0;_month = 0;_day = 0;}
3. 拷贝构造函数
概念
拷贝构造函数是构造函数的一种特殊重载形式 , 用于在创建对象时用已经存在的对象来初始化一个新对象
特性
Date(Date& d) //必须是引用类型 , 普通对象编译器会直接报错{_year = d._year;_month = d._month;_day = d._year;}
拷贝构造的无限递归问题
首先要明确区分拷贝与赋值概念,拷贝指的是用一个已经存在的变量来初始化一个新变量,赋值指的是将一个已经存在的变量的值赋给另一个已存在的变量 。
其次c++规定自定义类型的拷贝必须调用拷贝构造函数来完成,因为会有深浅拷贝的问题 。
清楚了这两点之后,如下图假设拷贝构造函数的形参不是引用会发生什么呢?
我们想用d1来初始化d2,于是调用拷贝构造函数 , 再函数调过程中需要将实参d1拷贝给形参date,但自定义类型的拷贝只能调用拷贝构造函数完成,于是又要调用拷贝构造函数将d1拷贝给date,再调用过程中又需要将实参d1拷贝给形参date,但是自定义类型的拷贝只能调用拷贝构造函数.....子子孙孙无穷匮也
深拷贝与浅拷贝
浅拷贝
浅拷贝只是简单地将一个对象的值复制给另一个对象 。
这种拷贝有一个问题,如果拷贝对象是指针那么拷贝指针和源指针就会指向同一块空间 。但往往我们需要的是两块独立的空间存放着相同的数据,而且拷贝指针和源指针都指向同一块可能会导致一系列的内存问题 。
深拷贝
如果拷贝对象是指针 , 就先为目标指针开辟与源指针相同的空间,在依次将数据依次拷贝到目标指针 。
如下图,将一个栈对象s1将拷贝给s2
浅拷贝
只简单的复制s1对象中的数据,但这样会导致s1对象中的指针和s2对象中的指针指向同一块空间,进而引发一系列的内存问题 。
深拷贝
会开辟与开辟与源指针相同的空间,在依次将数据依次拷贝到目标指针 。
注:一旦涉及资源申请,就必须要使用深拷贝 。
运算符重载函数
介绍
运算符重载是具有特殊函数名的函数,它的引入是为了解决自定义类型不能使用运算符的问题,具体方法是通过函数来模拟运算符功能,在使用运算符的时候就自动调用该函数 。
特性
//+=运算符重载函数Date& operator+=(Date& x, Date& y) //函数参数必须与运算符操作数对应{x.a = x.a + y.a;x.b = x.b + y.b;}
//+=运算符重载函数Date& operator+=(Date& y) //类成员{a = x.a + y.a;b = x.b + y.b;}
void TestDate(){Date s1;Date s2;operator+(s1, s2);//全局函数调用方式//s1.operator+(s2);成员函数调用方式s1 + s2;//运算符操作, 编译器自动转换成operator+(s1, s2)}
1. .* 2. :: 3. sizeof4. ?: 5. .
前置运算符与后置运算符的重载
为了在运算符重载时区分前置运算符和后置运算符 , C++规定在定义后置运算符时多定义一个int类型的参数以构成函数重载来区分,这个int类型的参数仅作为占位符并不使用它
// 前置++Date& operator++(){(*this) += 1;return *this;}//后置++Date operator++(int){Date d1(*this);(*this) += 1;return d1;}
4. 赋值运算符重载
介绍
赋值重载是运算符重载的一种形式,同时也属于默认成员函数的一种 , 用于相同类型的赋值
特性
Date& operator=(Date& x) //赋值重载函数{if (this != &x){a = x.a;b = x.b;}return *(this);}
const成员
const成员在调用成员函数时会报错,原因是权限的放大,const成员地址的实际类型是const 类名* this,但接受的形参却是类名*const this,这就造成了权限的放大 。
解决方法是将形参的类型修饰成const 类名* this,但this不能显示传递所以只能在()括号后面加上const这样就表示const 类名* this 。
注:()括号后面加上const , 形参的实际类型是const 类名* const this
5/6. 取地址重载函数
介绍
取地址重载是运算符重载的一种形式,同时也属于默认成员函数的一种,用于对象的取地址
特性
//普通对象&地址重载Date* operator&(){return this;}//const对象&地址const Date* operator&() const{return this;}