📌 博主名称:池茗

欢迎来到池茗的博客☆*: .. o(≧∇≦)o..:*☆
⭐ 数据结构系列个人专栏:
⭐ C++基础个人专栏:
⭐ 不会因为虚度年华而悔恨,也不会因为碌碌无为而羞愧
📖 点击展开/收起 文章目录
文章目录
- [📌 博主名称:池茗](#📌 博主名称:池茗)
- 默认成员函数
下面是C++文档地址,方便大家查阅C++有关使用及语法知识,里面也有论坛,大家自行取用
欢迎各位来到池茗的创意工坊:
今天我们要讲的是 --->类与对象核心篇
- 默认成员函数
- 构造函数
- 析构函数
- 拷贝构造
- 赋值重载
默认成员函数
默认成员函数就是我们不用自己写编译器自动生成在类内部的函数,下面我们一个一个介绍一下他们
构造函数
会隐式传入this指针
构造函数,特殊的成员函数,要求函数名与类名相同,且返回值为void时要省略掉在函数中写void
对象实例化时会自动调用构造函数
cpp
class date
{
public:
//系统自动生成的默认构造函数
date()
{
}
//全缺省默认构造函数
date(int year = 0,int month = 0,int day = 0)
:_year(year),
_month(month),
_day(day)
{
}
//传参构造函数,传参构造函数和全缺省构造函数不可同时出现
date(int year,int month,int day)
:_year(year),
_month(month),
_day(day)
{
}
void print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
初始化列表
cpp
class Date
{
Date(int m,int& x,int year=1900,int month=1,int day=1)
://初始化列表成员定义的地方 ()中间是一个式子,每个成员只可初始化一次
_year(year),
_month(month),
_day(day),
//有些成员必须用初始化列表初始化
_ptr((int*)malloc(sizeof(int)*4)),//可以是表达式
//引用初始化
_x(x),
//const只能在初始化列表中初始化
_m(m)
{}
private:
在成员声明可以给缺省值如果初始化列表没有给值,就按缺省值给,自定义成员自动调用他的默认构造函数
int _year = 1 ;
int _month =1;
int _day=1;
int* _ptr=(int*)malloc(sizeof(int) * 4);
不可给缺省值只可在初始化列表初始化
int& _x;
const int _m;
}
上面展示的是一个日期类的构造过程,冒号及其后面就是初始化列表 ,初始化列表就是每个成员变量初始化及定义的地方,

这里继续强调一遍,上期我就说过,在一个类里,这里只能算成员变量的声明 ,在实例化一个对象就是调用它的构造函数通过初始化列表把一个个成员给定义并初始化,如果没有给值,那调用的就是默认构造函数(下面就会讲解),要是你自己在成员变量声明时给了缺省值,这里系统生成的默认构造里面的初始化列表就会调用你这个初始值
有些成员必须用初始化列表初始化(这些变量释义看上面代码)不可再声明地方给缺省值- 需要开辟资源的
_ptr((int*)malloc(sizeof(int)*4)),//可以是表达式 - 引用初始化
_x(x), - const只能在初始化列表中初始化
_m(m)
- 需要开辟资源的
构造成员的顺序>: 按照变量声明顺序来,而不是按照初始化列表中的顺序来
小题检验 >:判断_a1,_a2的值
class a {
public:
a(int a)
//在这里初始化顺序就是类里面成员声明的顺序
//所以先初始化_a2后初始化_a1
:_a1(a),
_a2(_a1)
{}
void print() {
cout << _a1 << _a2 << endl;
}
private:
int _a2 = 2;
int _a1 = 2;
};
int main()
{
a a(1);
a.print();
}
默认构造
你没写构造函数,系统会自动生成默认构造,你写了构造函数就不会生成
构造函数可以重载,但默认构造函数只能有一个 ,简单来说默认构造函数就是不用传参的构造函数
默认构造函数只能同时出现一个,不能同时出现多个,但可以和构造函数同时出现
构造函数的调用如果不传参,就不加括号 ,如果有参数,就在后面直接写参数,再调用构造函数的同时也定义对象
Date a;
若类里面全是自定义类型,会自动调用他的默认构造函数 ,像MyQueue这样的类,他的默认构造函数将自动调用stack的构造函数,
简要介绍一下这里Myqueue就是我们前面在栈部分讲的两个栈实现一个队列,他的两个成员就是栈
Myqueue()
:s1(),s2(){}//在这里把两个栈构造好由于这段代码是在类里面,s1,s2已经存在了,所以调用构造函数不用加类名
Myqueue在这里非常特别,下面讲完成员函数会讲解Myqueue的特别之处
- 系统自动生成的默认构造函数
date()
{
} - 全缺省默认构造函数(自己写的)
date(int year = 0,int month = 0,int day = 0)
:_year(year),
_month(month),
_day(day)
{
}
去掉缺省值或者变成半缺省就是普通构造
析构函数
1.析构函数的定义,在类名前面加个"~"
2.无参无返回值与构造函数类似
3.一个类只能有一个析构函数,要么自己写,要么系统会自动生成
4.与构造函数类似,析构函数不对内置类型做处理,有自定义成员一定会调用自定义成员自己的析构函数,
不要自己再画蛇添足显式调用自定义类型的析构
对于MyQueue调用析构函数,就算你写了析构函数,系统也会自动调用stack的析构函数
其他与构造函数类似
注意:在这里最重要的一点区别,与构造函数,无论你自己写没写都会调用~MyQueue()
eg:
自己写的
cpp
~MyQueue(){
cout<<"~MyQueue"<<endl;
}
当然在这里我们不在有自定义类型的类里面调用自定义类型的析构函数,这样会产生二次释放同一片空间
系统自己生成的
MyQueue不管你写没写调用类类型的对象的析构函数,系统也会自己调用
cpp
~MyQueue(){
// 编译器在内内部会这样调用一次
//s1.~stack();//会产生二次调用
//s2.~stack();
}
析构顺序
先构造的成员后析构,也就是按照声明顺序倒着来
最后再提一点,但你在构造函数中有自己malloc或者new的空间一定要写析构函数,不然会内存泄漏
拷贝构造
cpp
Date(Date& d)
:_year(d._year),
_month(d._month),
_day(d._day)
{
}
拷贝构造,顾名思义,就是用来拷贝对象的假如一个函数传值func(Date d1)要求传入一个对象值,这里是形参,首先要拷贝实参,就会调用拷贝构造 ,这时就会出现经典问题拷贝构造函数参数必须是传引用
这里必须传引用如果不传引用传值,又会像func(a)一样,调用新的拷贝构造函数,就会一直递归下去

拷贝构造的调用
cpp
int main()
{
Date b(2025,4,7);
//拷贝构造有两种写法
//拷贝的同时定义了类,先定义的类,后拷贝,只是在一句中实现了
//1.Date a(b);
//2.Date a = b;
Date a = b;
//C++规定函数在传值(自定义类型)时会调用该自定义类型的拷贝构造
func(a);
}
注意一下,这里的2.Date a = b;要与后面的赋值重载做区分不要搞混了,这里不是赋值,是构造函数
函数中return返回的都是临时对象的拷贝 ,在返回时都会调用返回对象的拷贝构造,构造一个临时对象,也就是const引用的对象
拷贝构造对效率的消耗是很大的我们有意识地要减少没必要的拷贝构造
cpp
Date func(Date a1){
}//在这里func传值调用a1,a1在这里会首先调用a1的拷贝构造函数生成一个a1//拷贝原来的a
Date func2(const Date& a2) {
}//由于上面那种写法需要拷贝a要调用a2的拷贝构造函数需要多建立一层栈帧为了提高效率我们可以直接传引用
//这里传const引用是为了不改变a的值
Date func3(const Date& a3) {
Date st;
return st;//前面说过,返回自定义类型和内置类型,都返回的是st的值的拷贝,会拷贝构造一个临时返回值,如果这样写返回后,st已经被销毁了
}
Date func4(const Date& a3) {
static Date st;
return st;//所以我们要这样写,但是这样写也有弊端,要拷贝构造一个st,就又要建立栈帧,效率低下
}
Date& func5(const Date& a3) {
static Date st;
return st;//所以我们改进了第五种写法,返回st的引用
}

浅拷贝
就是系统生成的就够了
深拷贝
*当在你的初始化列表中有_ptr((int )malloc(sizeof(int)*4)),**你就需要自己写拷贝构造进行深拷贝,也就是在开辟一块空间来存放相同的数据
原因:因为你有这种自己申请的空间,会产生二次析构

运算符重载
在内置类型中有他的运算符
在自定义内省中我们也想用运算符该怎么用呢?
在C++中我们允许使用operator这个关键字来重载运算符
eg:bool operator==(const Date& d1)
但是有五个不能被重载的运算符:
| 运算符 | 说明 |
|---|---|
| .* | 成员指针访问运算符 |
| :: | 类作用域解析运算符 |
| ?: | 条件运算符(三目运算符 |
| . | 成员访问运算符(点运算符) |
| sizeof | 计算对象/类型大小的运算符 |
| 对于成员指针运算符大家可能比较陌生下面展示一下用法 |
cpp
class Date {
public:
int _year;
void print() { cout << _year << endl; }
};
int main() {
Date d;
// 定义一个指向 Date 类成员变量的指针
int Date::*ptr = &Date::_year;
// 使用 .* 通过对象和成员指针来访问成员
d.*ptr = 2026;
cout << d._year << endl; // 输出 2026
return 0;
}
运算符重载及其相关问题代码演示加注解
cpp
class Date
{
public:
bool operator==(const Date& d1)//注意我这儿传的的是const引用 而不是传值,避免了拷贝构造,加const是为了不改变值
{
return _year ==d1._year
&&_month == d1._month&&
_day == d1._day;
}
Date& operator=(const Date& d1)
{
_year = d1._year;
_month = d1._month;
_day = d1._day;
return (*this);//
}
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
//这种定义在类外的运算符重载要么在类里面写getyear()函数来获取_year的值,要么就把所有全部设为公有
//bool operator==(const Date& d1,const Date& d2)
//{
// return d1._year == d2._year
// && d1._month == d2._month &&
// d1._day == d2._day;
//}
int main()
{ //C++规定类类型对象要使用运算符时,必须调用对应的运算符重载
//运算符重载是一种特殊的函数,operator后面加上运算符,eg:operator==()这种,他也可以具有返回值
//重载运算符的参数要和调用对象一样多
Date d1(2025, 4, 9);
Date d2(2025, 4, 7);
//if(d1==d2)与下面的写法意思一样
if (d1.operator==(d2)){//这种写法其实第一个参数都是this指针//这是定义在类里面的
cout << "ture" << endl;
}
else
cout << "false" << endl;
Date d3;
d3 = d2 = d1;
return 0;
}
赋值重载
- C++中的一个默认成员函数,用于已存在的两个对象的拷贝(浅拷贝),如果遇到stack那种还是要自己写(深拷贝),
与拷贝构造函数一样,遇到类类型成员,还是会自动调用他的赋值重载 - 有返回值,返回值是类的引用,作用是可以写成d1=d2=d3
- 传入参数类型最好写为引用
- 没有显示实现系统会自动调用
