C++从零到满绩——类和对象(中)
目录
1>>前言
2>>构造函数(我称之为初始化函数)
3>>析构函数(我称之为销毁函数)
4>>拷贝构造函数(我称之为复制函数)
5>>运算符重载
5.2>>赋值运算符重载
编辑
6>>结语
1>>前言
上节课学习了基础篇(包括函数重载、引用、inline、nullptr)and类和对象(上)(包括类的定义、实例化、this指针),忘记的宝子们可以去复习一下~~~
枫の大一-CSDN博客C++从零到满绩——入门基础and类和对象(上)-CSDN博客 跳转上篇
枫の大一-CSDN博客 跳转个人主页查看
今天我们来类和对象(中)(包括:构造函数、析构函数、拷贝构造函数、赋值运算符重载)。需要注意的是今天我们学习的四个内容 始终 带着两个疑问:
第一:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求?
第二:编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现?
ps:如果有觉得小编哪里需要改进的欢迎指出,做出完美的C++篇章肯定少不了各位精神股东的支持啦,谢谢大家~
2>>构造函数(我称之为初始化函数)
构造函数虽然有构造二字,但它并非开空间造对象,而是在对象实例化时初始化对象。它的本质就是替代之前写的init初始化函数,并且它还能自动调用,简直perfect!
构造函数的特点:
1.函数名域类名一样。
2.无返回值。没有void!!!
3.对象实例化时会自动调用。
4.构造函数可重载。
5.如果类中没有显式定义(我们自己写的)构造函数,编译器会自动生成无参默认构造函数。
6.默认构造包括:无参构造函数、全缺省构造函数、编译器默认生成的构造函数。简单说就是不需要我们传实参就是默认构造。
7.编译器默认生成的构造函数,对于内置类型初始化不确定,看编译器;对于自定义类型成员变量,要求调用这个成员变量的默认构造函数进行初始化。类似于高数的链式求导,y不能对x求导,可以对t求导,t在对x求导。
#include<iostream>
using namespace std;
class Date {
public://公共
void print() {//打印函数
cout << _year << "/" << _month << "/" << _day << endl;
}
private://私有
int _year;
int _month;
int _day;
};
int main() {
Date d1;
d1.print();
return 0;
}
不写构造函数运行结果为随机值
#include<iostream>
using namespace std;
class Date {
public://公共
Date(int year = 1, int month = 1, int day = 1) {//构造函数
_year = year;
_month = month;
_day = day;
}
void print() {//打印函数
cout << _year << "/" << _month << "/" << _day << endl;
}
private://私有
int _year;
int _month;
int _day;
};
int main() {
Date d1(2024, 11, 24);
d1.print();
return 0;
}
写了构造函数对变量d1进行了初始化操作,还用到了上节课的缺省参数
d1这里后面什么都不写
Date d1;
运行结果为1/1/1,因为缺省参数帮助初始化了。
可以发现我们没有进行调用,而是在实质化对象的时候自动调用,这就是它的好用之处。这就是构造函数的使用。
3>>析构函数(我称之为销毁函数)
析构函数功能与构造函数相反,听名字可以理解为解析构造,将构造函数一步步剖析。它的工作是完成对象中资源的清理释放工作。类似与之前栈使用的Destroy功能。
析构函数的特点:
1.析构函数名是在类名前加上字符~。~本身也是按位取反的意思,这里代表相反。
2.无参数无返回值。不需要void!!!
3.一个类只能有一个析构函数。
4.对象生命周期结束时,系统会自动调用析构函数。
5. 自定义类型的成员变量都会调用析构函数(不管是写的还是默认生成的)。
6.若类无申请资源,析构可以不写,如Date;若类都是自定义成员变量,它们都有自己的析构,也可以不写,如用两个栈实现队列的MyQueue;若有申请资源,一定要写,如用两个栈实现队列的Stack。
7.若有多个对象,后定义的先析构。
#include<iostream>
using namespace std;
typedef int StDataType;
class Stack {
public://公共
Stack(int n=4) {//构造函数
_arr = (StDataType*)malloc(sizeof(StDataType) * n);
if (nullptr == _arr)
{
perror("malloc获取失败");
return;
}
_capacity = n;
_top = 0;
}
~Stack() {//析构函数
free(_arr);
_arr = nullptr;
_capacity = _top = 0;
}
private://私有
StDataType* _arr;
int _capacity;
int _top;
};
int main() {
Stack st;
return 0;
}
4>>拷贝构造函数(我称之为复制函数)
拷贝构造函数第一个参数时自身类型的引用,其他参数都是缺省参数。
拷贝构造的特点:
1.拷贝构造函数是构造函数的重载。
2.拷贝构造参数第一个必须是自身类型的引用。
3.自定义类型对象进行拷贝时必须调用拷贝构造,因此传值传参和传值返回的行为都会调用拷贝构造。
4.若未自己写,编译器会自动生成。自动生成的拷贝构造是值拷贝/浅拷贝(一个字节一个字节拷贝),自定义成员变量会调用它的拷贝构造。
5.需要我们写析构的也要写拷贝构造,例如Stack。因为它有申请资源,浅拷贝会导致两块st1和st2都指向同一块空间,因此要进行深拷贝,所以要自己写。
6.传值返回会调用拷贝构造,传值“引用”返回没产生拷贝,因此不需要调用。但是如果返回的引用时一个局部对象,在函数结束时销毁,那么引用会变成野引用,那就不行。传引用返回前提是函数结束后还在。
#include<iostream>
using namespace std;
class Date {
public://公共
Date(int year = 1, int month = 1, int day = 1) {//构造函数
_year = year;
_month = month;
_day = day;
}
void print() {//打印函数
cout << _year << "/" << _month << "/" << _day << endl;
}
private://私有
int _year;
int _month;
int _day;
};
int main() {
Date d1(2024,11,24);
Date d2(d1);
d1.print();
d2.print();
return 0;
}
用栈的拷贝构造:
//拷贝构造
#include<iostream>
using namespace std;
typedef int StDataType;
class Stack {
public://公共
Stack(int n = 4) {//构造函数
_arr = (StDataType*)malloc(sizeof(StDataType) * n);
if (nullptr == _arr)
{
perror("malloc获取失败");
return;
}
_capacity = n;
_top = 0;
}
Stack(const Stack& source) {//拷贝构造函数
//需要再开辟一个同样大小空间
_arr = (StDataType*)malloc(sizeof(StDataType) * source._capacity);//乘上之前空间大小
if (nullptr == _arr)
{
perror("malloc获取失败");
return;
}
memcpy(_arr, source._arr, sizeof(StDataType) * source._top);//复制有效数据
_capacity = source._capacity;
_top = source._top;
}
~Stack() {//析构函数
free(_arr);
_arr = nullptr;
_capacity = _top = 0;
}
private://私有
StDataType* _arr;
int _capacity;
int _top;
};
int main() {
Stack st;
Stack st2(st);
return 0;
}
5>>运算符重载
1.这里重载和之前的函数重载区分开,并不是一个意思。
2.当两个类类型对象进行运算符操作时,不能直接操作,需要我们写一个对应的运算符重载函数才可以。
3.它由operator和定义的运算符构成。
4.它的参数个数和运算对象一样多。
5.如果一个重载运算符函数是成员函数,那它少一个参数,因为有隐藏的this指针。
6.特别注意5个运算符不能重载:(.* :: sizeof :? .)
7.至少有一个类类型参数。
//运算符重载
#include<iostream>
using namespace std;
class Date {
public://公共
Date(int year = 1, int month = 1, int day = 1) {//构造函数
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d2) {//重载格式:operator符号
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
void print() {//打印函数
cout << _year << "/" << _month << "/" << _day << endl;
}
private://私有
int _year;
int _month;
int _day;
};
int main() {
Date d1(2024,11,24);
Date d2(d1);
int r= d1 == d2;
cout << r;
return 0;
}
5.2>>赋值运算符重载
跟拷贝构造区分开,它是两个已经存在的对象进行赋值。
赋值运算符重载特点:
1.必须重载为成员函数,建议带上const。
2.有返回值,写当前类类型的引用,提高效率。
//赋值运算符重载
#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& operator=(const Date& d2) {//重载格式:operator符号
//跳过自己给自己赋值
if (this != &d2) {
_year = d2._year;
_month = d2._month;
_day = d2._day;
}
//因为d1=d2返回的是d1,而*this也是d1,所以返回*this
return *this;
}
void print() {//打印函数
cout << _year << "/" << _month << "/" << _day << endl;
}
private://私有
int _year;
int _month;
int _day;
};
int main() {
Date d1(2024, 11, 24);
Date d2(2024, 11, 299);
d1 = d2;
d1.print();
d2.print();
return 0;
}
6>>结语
今日份C++入门就先到这里啦,来总结一下:主要讲了类和对象(中)(包括:构造函数、析构函数、拷贝构造函数、赋值运算符重载),感兴趣的宝子们欢迎持续订阅小编,小编在这里谢谢宝子们啦~C++的学习很陡,时而巨难时而巨简单,希望宝子和小编一起坚持下去~你们的三连是我的动力,感谢支持~