C++基础精讲篇第6讲:类中构造函数和析构函数特性详解_King_lm_Guard的博客-CSDN博客
https://blog.csdn.net/King_lm_Guard/article/details/126043552C++基础精讲篇第7讲:类中拷贝构造函数特性详解_King_lm_Guard的博客-CSDN博客
https://blog.csdn.net/King_lm_Guard/article/details/126049876 本讲内容基于上一讲中分析的C++类中6个默认的成员函数,在前两讲中详细分析了构造函数、析构函数与拷贝构造函数,这一讲博主接着分析赋值运算符重载函数相关特性。
目录
2.3 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝
1、运算符重载基本概念
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)。详解:参数列表分为(左操作数、右操作数)。返回类型是由操作符号决定的,比如:两个同类型的变量相减,返回的差值是整型则返回类型为int ,如果为字符型则返回char,如果返回值就是该类的类型,那就以该类名作为返回对象接收。
//判断日期类变量d1和d2是否相等,对运算符“==”进行重载 bool operator==(const Date& d1, const Date& d2) { return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day; }
运算符重载运用格式如上例所示,对符号“==”进行运算符重载,比较两个日期类是否相等。其中左操作数为d1,右操作数为d2,返回类型用bool接收。
2、赋值运算符重载
2.1 赋值运算符重载格式
1、参数类型:const T&,传递引用可以提高传参效率;
2、返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值;
3、检测是否自己给自己赋值;
4、返回*this :要复合连续赋值的含义。
示例:以如下代码为例说明:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; //运算符重载 class Date { public: //构造函数 Date(int year =1 , 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; } //赋值运算符重载 Date& operator=(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } private: int _year; int _month; int _day; }; void Test() { Date d1(2022, 7, 30); //书写方式1 //Date d2(d1); //书写方式2 Date d2; d2.operator=(d1); //书写方式3 Date d3 = d2; } int main() { Test(); return 0; }
程序执行结果:可以发现三种不同的写法同样可以实现对类变量d2的赋值运算符重载。
2.2 赋值运算符只能重载成类的成员函数不能重载成全局函数
示例:将赋值运算符重载成全局函数时演示情况
注意:由于此时是全局函数,而在类Date中,原则上成员变量_year、_month、_day三者应该在类中设置为private,而不是public,但为了演示全局函数,所以将成员函数设置为public,此时可以在该类外直接访问成员变量,如下程序所示:
此时程序运行结果显示编译出错,如上所示,这是因为:
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
2.3 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
示例:
class Time { public: //构造函数 Time(int hour=1,int minute=1,int second=1) { _hour = hour; _minute = minute; _second = second; } //赋值运算符重载 Time& operator=(const Time& t) { if (this != &t) { _hour = t._hour; _minute = t._minute; _second = t._second; } return *this; } private: int _hour; int _minute; int _second; }; class Date { public: //构造函数 Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //得到私有的自定义类型Time类的成员变量 Time& Get_Time() { return this->_t; } private: // 基本类型(内置类型) int _year ; int _month; int _day ; // 自定义类型 Time _t; }; int main() { Date d1(2022, 7, 30); Time t(12, 24, 36); d1.Get_Time().operator=(t); Date d2; d2 = d1; return 0; }
程序执行结果:
补充:
同拷贝构造函数一样,编译器生成的默认赋值运算符重载函数可以完成字节序的值拷贝,诸如像日期类这种类类型的对象,因为其内部的成员变量如:_year、_month、_day等都是内置类型,虽然成员变量_t是Time类类型的,但在拷贝的时候编译器会去调用自定义类型的赋值运算符重载函数,如程序中所示,所以可以实现拷贝。
但对于栈、队列等需要额外开辟空间的类来说(如下所示),如果只是简单的采用值拷贝,在销毁空间的时候会存在对同一块空间多次释放的情况,为此,需要采用深拷贝相关知识进行解决该类问题,这在后面的学习中我会为大家带来讲解。
3、前置++和后置++重载
因为前置++和后置++的运算符相同,为了在使用过程中加以区分,在C++中,使用函数重载进行区分,也就是说在后置++重载中增加一个int类型参数和前置++构成函数重载。但调用函数时该参数用户不用传递,编译器自动传递。
3.1 前置++详细分析
前置++是返回+1之后的结果,因为在类中this指针指向的对象函数结束后不会销毁,故以引用方式返回提高效率,这种返回方式在上一讲中讲解拷贝构造函数时进行过详细分析,如果不用引用,而是用传值返回,则会形成临时拷贝,相比传引用返回效率较低。(还是提供上一讲分析的传引用返回和传值返回的区别)。
![]()
前置++用法示例说明
3.2 后置++详细分析
后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1。因为在使用后置++时,需要保存旧值,所以需要创建临时变量temp保存旧值*this,然后再讲该临时变量返回,因此此时只能以传值的方式返回,不能传引用返回。
![]()
后置++用法示例说明
4、const成员函数
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
示例:下面的程序执行结果会是什么呢?
class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << "Print()" << endl; cout << "year:" << _year << endl; cout << "month:" << _month << endl; cout << "day:" << _day << endl << endl; } void Print() const { cout << "Print()const" << endl; cout << "year:" << _year << endl; cout << "month:" << _month << endl; cout << "day:" << _day << endl << endl; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; void Test() { Date d1(2022, 1, 13); d1.Print(); const Date d2(2022, 1, 13); d2.Print(); } int main() { Test(); return 0; }
在解答之前,博主请读者们思考以下几个问题:
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?
解答上面的这几个问题就涉及到权限的概念(注意:在函数参数传递以及返回过程中,权限的概念只是在引用和指针之间进行讨论),为了让读者们能再次巩固这一块的知识,大家可以阅读我以前写的这一讲的知识:1、不可以,权限被放大了;
2、可以,相当于权限缩小了,权限缩小是被允许的;
3、不能,权限被放大了;
4、可以,权限缩小了。
上述程序结果原理示意:
知识补充:const修饰指针有分为两种方式,组合有三种形式
1、指针变量不能被修改;
2、指针变量指向的内容不能被修改
*第一种场景:这里的const表示this指针变量不能被修改;
*第二种场景:这里的const表示this指针变量指向的内容不能被修改;
*第三种场景:this指针变量不能被修改的同时,this指针指向的内容也不能被修改。
建议大家:如果this指针及指针指向得内容均不想被修改时,加上const修饰。
5、取地址及const取地址操作符重载
注意:这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
用法示例
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容!
6、运算符重载注意事项
1、不能通过连接其他符号来创建新的操作符:比如operator@;
2、重载操作符必须有一个类类型参数;
3、用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义;
4、作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;
5、.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
7、结语
今天这一讲主要将类中6个默认的成员函数种剩下的三个成员函数进行讲解,其中,赋值运算符重载函数是非常重要的,对于深入学习C++而言,这是必须掌握的,为此在下一讲中,博主将以日期类为例,将各个函数的运用用实例说话,让读者们能理解得更透彻。欢迎点赞、关注、支持!!!