💛 前情提要💛
本章节是C++ 的六个默认成员函数 的相关知识~
接下来我们即将进入一个全新的空间,对代码有一个全新的视角~
以下的内容一定会让你对C++ 有一个颠覆性的认识哦!!!
以下内容干货满满,跟上步伐吧~
作者介绍:
🎓 作者: 热爱编程不起眼的小人物🐐 🔎作者的Gitee:代码仓库 📌系列文章&专栏推荐: 《刷题特辑》、 《C语言学习专栏》、《数据结构_初阶》 、《C++轻松学_深度剖析_由0至1》
📒我和大家一样都是初次踏入这个美妙的“元”宇宙🌏 希望在输出知识的同时,也能与大家共同进步、无限进步🌟 🌐这里为大家推荐一款很好用的刷题网站呀👉点击跳转
💡本章重点
-
认识并实现C++ 中类的默认成员函数:
-
🔥了解C++ 的魅力:运算符重载
🍞一.类的默认成员函数
💡默认成员函数:
👉接下来我们就深入分析默认成员函数 究竟是什么吧~
🥐Ⅰ.构造函数
💡构造函数:
-
函数形式:类名() {}; -
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次 -
本质: 辅助完成对象的成员变量的初始化,将对象的成员变量初始化函数内嵌化【相当于顶替了我们以前实现数据结构的Init 函数】
??构造函数的特征:
-
函数名字与类名相同 -
函数没有返回值 -
在对象进行实例化的时候,编译器自动调用其构造函数 -
构造函数支持重载
👉那我们就以日期类 为例子,来剖析构造函数 吧:
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
- 上述的日期类中,我们虽然看不见
默认构造函数 ,但其实它是存在的,只是没有显示的调用,函数为:Date() {} 【无参构造函数】
1??在上述的了解中,我们得知默认构造函数 会帮助我们初始化成员变量,那初始化后的值是多少呢?
-
上述的检验便可以得知,编译器自动生成的默认构造函数,对成员变量初始化的值为随机值 ,这样一来我们就可以知道: -
编译器生成的默认构造函数对成员变量进行初始化 了,但并没有处理 【即初始化成随机值 了,但并没有真正像Init 函数一样处理成我们想要的默认值】 -
正是因为C++ 认为没有初始化就使用对象会存在一些使用风险,于是引入了构造函数 ,这样就不会忘记调用对应的函数对成员变量初始化了 -
👆综上: “默认构造函数初始化了,但没完全初始化”【因为并没有将初始化后的值处理成0 等其它数值】
2??此时为了达到能将初始化后的值处理成我们想要的值,可以有如下四 种操作:
- 无参构造函数:即在对象实例化的过程中,编译器自动调用此构造函数进行成员变量的初始化
Date(int year, int month, int day)
{
_year = 0;
_month = 1;
_day = 1;
}
- 带参构造函数: 即在实例化的过程中,传参实例化对象,显示定义构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
- ?特别注意: 在我们显示定义构造函数后,编译器便不会再生成默认的构造函数,而是采用我们自己的构造函数进行使用
- 全缺省构造函数:在第一步的基础上,我们可以进一步优化,写成
全缺省 的构造函数【这样就可以在不带参的情况下,也处理成我们想要的值】
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
- ?特别注意: 在这种情况下,若
无参 进行构造函数的初始化时,是不需要带上括号的【Date d(); 】,否则就会被看成是关于d 的函数声明
- 构造函数的函数重载:也可以用于构造函数的多种初始化方式中,但容易与
缺省函数 构成歧义,程序在执行的时候不知道应该调用哪个,所以不推荐
🔥重点:
class Date
{
private:
int _year;
int _month;
int _day;
Time _t;
};
-
默认构造函数不仅会对成员变量中的内置类型 (int、char……类型)进行初始化,也会对自定义类型 (class、struct……类型)的成员变量去调用 它们自己的构造函数去初始化自己(Eg:Time为自定义类型),从而达到这个类(eg:Date)中的成员变量被全部初始化 -
简单来说:默认构造函数仅仅对自定义类型 成员的初始化执行了调用 的操作,即调用 自定义类型成员自己的默认构造函数 去初始化【调用 的默认构造函数为哪一种,具体看用户自己怎么样实现】
?综上:
🥐 Ⅱ.析构函数
💡析构函数:
-
函数形式:~类名() {}; -
析构函数是指:对象在销毁时,自动调用析构函数,完成类中的资源清理工作 -
其中这里的资源 ,并不是指对象的销毁,因为对象为局部对象,是存在栈区的,其销毁工作是由编译器去完成的 -
而是指一些类中(Eg:vector、string……),有向堆区 申请空间的,在对象被销毁时,编译器会自动调用其析构函数区释放申请的空间,避免内存泄漏【替代了以往自己实现数据结构的destroy 函数】
?特别注意:
??析构函数的特征:
?那如果同时存在多个对象,析构的顺序是怎么样的呢
?综上:
🥐 Ⅲ.拷贝构造函数
💡拷贝构造函数:
??拷贝构造函数的特征:
?为什么特别注意要用引用传参 呢
-
因为是传值传参,所以会在形参接收的时候,形参是实参的临时拷贝 -
即如上:传值传参后,需要构建一个对象d 去接收对象d1 ,而此操作不正相当于对象d 调用拷贝构造函数去拷贝构造一个与对象d1 相同值的对象吗 -
而此时的拷贝构造函数都没还进入到实现部分,就在传参上陷入了无限的递归调用了 -
所以只有引用传参 可以解决这个问题
?特别注意:
- 如果不显示定义拷贝构造函数的函数实现,系统自己也会默认生成一个:按照按内存存储按字节序完成拷贝,即实现了
浅拷贝 (值拷贝)【如下所示】
- 但这里会涉及
深 、浅 拷贝的问题【后续会提到】,假如有两个堆类,一个是通过另外一个而拷贝构造出来的,但又因为是值拷贝 ,所以仅仅单纯的将值拷贝过去,这就会产生如下的问题:它们都指向同一块空间了
?综上:
🔥二.运算符重载
💡运算符重载:
-
函数形式: 返回类型 ?operator ?需要重载的运算符符号 ?(参数列表){}; -
在C++ 中为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似 -
简单来说:因为类和对象 的引入,C++ 中专门引入运算符重载解决原本在语言层面上运算符就支持内置类型,而不支持自定义类型的情况 -
即支持我们自己针对自定义类型【Eg:Date d1 + Date d2 ,此时程序是不支持两个类相加的】的运算符操作,进行赋予新的意思,去告诉编译器针对这种某种类型下的运算符是如何操作的,自己给自定义类型写一个对应运算符的使用规则
?特别注意:
-
不能通过连接其他符号来创建新的操作符:比如operator@ 【需要在编译器原有的操作符上进行重载】 -
重载操作符必须有一个类类型或者枚举类型的操作数【如果操作数都是内置类型的话,重载就没意义了】 -
有五个运算符是不支持重载的:* 、:: 、sizeof 、?: 、.
??运算符重载的两种场景:
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year;
&& d1._month == d2._month
&& d1._day == d2._day;
}
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
bool operator==(const Date& d2)
{
return this->_year == d2._year;
&& this->_month == d2._month
&& this->_day == d2._day;
}
?综上:
🥐Ⅰ.赋值运算符重载
💡赋值运算符重载:
?特别注意:
-
与拷贝构造不同的是:拷贝构造是在对象实例化的时候,拿另外一个对象初始化自己【在对象实例化阶段】 -
但赋值运算符重载是:在已创建的对象上,拿另外一个对象的数据对自我进行拷贝【在对象已存在阶段】 -
对于Date d1; Date d2 = d1; 这种情况,d2此时还没有被实例化出来,处于对象实例化 的阶段,所以这种情况是属于拷贝构造 而非赋值运算符重载
👉示例:
👆不难发现:
🔥重点: 我们应该如何实现一个赋值运算符重载呢?
void operator=(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;
}
?综上:
-
就是运算符重载 的概念啦~ -
有了以上的了解,想必大家都感悟到C++的精华了吧
🍞三.总结
?综上:
-
对于内置类型 来说:
-
对于自定义类型 来说:
-
编译器会自动调用其默认成员函数实现相关的初始化、拷贝赋值、析构等操作 -
若自定义类型 的成员涉及深拷贝 等操作,我们则需要对拷贝构造函数和赋值运算符重载显示实现深拷贝 -
若涉及到内存的释放,析构函数需要显示定义去释放空间
🍞四.const成员
💡const修饰类的成员函数:
👉该如何操作呢?
bool operator==(const Date& d);
bool operator==(const Date& d) const;
?特别注意:
-
此处如果是普通对象调用const成员函数,是可以调用且类成员是收到保护的,因为这属于权限缩小 -
但如果是const对象调用const成员函数,则调不动,因为这属于权限放大 ,不符合规则 -
【对于权限 的放大or缩小,同学们可点击>跳转<食用呀】
?综上:
-
const 在* 之前修饰的是指针指向的对象 -
const 在* 之后修饰的是指针本身
🫓结尾
综上,我们基本了解了C++中的 “类和对象 - 默认成员函数” 🍭 的知识啦~
恭喜你的内功又双叒叕得到了提高!!!
感谢你们的阅读😆
后续还会继续更新💓,欢迎持续关注📌哟~
💫如果有错误?,欢迎指正呀💫
?如果觉得收获满满,可以点点赞👍支持一下哟~?
|