【C++指南】一文总结C++类和对象【中】
🌟 各位看官好,我是egoist2023!
🌍 种一棵树最好是十年前,其次是现在!
🚀 今天来学习C++类和对象的语法知识
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦
目录
类的默认成员函数
构造函数
内置类型和自定义类型
特点
析构函数
特点
拷贝构造函数
特点
传值返回注意事项
类的默认成员函数
构造函数
构造函数 是特殊的成员函数,其主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时,空间就开好了), 而是对象实例化时初始化对象 。构造函数的本质是要替代我们以前Stack和Seqlist类中写的Init函数的功能,其自动调用的特点完美的替代了Init。
内置类型和自定义类型
C++把类型分为内置类型和自定义类型。内置类型即语言提供的原生数据类型,如:int/char/double/指针等;自定义类型就是使用的class/struct等关键字自己定义的类型。
特点

6. 无参构造函数、全缺省构造函数、编译器默认生成的构造函数 --> 默认构造函数 。有且只能有一种存在。(默认构造函数并不是指编译器默认生成的构造函数,简单来说不传实参就可以调用的构造就叫默认构造)
原因有两点:1.语法上默认构造函数有且只能有一种存在;2.写了一个无参一个全缺省,那么构造 Date d( ) 时编译器怎么知道走哪个构造函数呢。![]()
//声明和实例化混淆
Date Func();//函数声明
Date d1();
class Date
{
public:// 1.⽆参构造函数Date(){_year = 1;_month = 1;_day = 1;}// 2.全缺省构造函数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;
};
析构函数
析构函数与构造函数功能相反, 析构函数 不是完成对对象本身的销毁(比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要管),C++规定对象在销毁时会自动调用析构函数, 完成对象中资源的清理释放工作 。析构函数的功能类比我们之前Stack实现的Destroy功能(若没有向堆申请空间之类其实就是没有资源需要释放,如Date实现是不需要析构函数的。
特点
int* arr=(int*)malloc(sizeof(int));
class Date
{
public://带参构造函数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;
};
typedef int STDataType;class Stack
{
public:Stack(int n = 4){//向堆空间申请了n*STDataType字节空间STDataType* _a = (STDataType*)malloc(sizeof(STDataType) * n);if (_a == NULL){perror("malloc fail!");exit(1);}_capacity = n;_top = 0;}//析构函数~Stack(){free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;int _top;int _capacity;
};
如果默认生成的析构就可以用,也就不需要显示写析构,如MyQueue --> 即两个栈实现一个队列
那么显式写了析构函数,编译器是否还会调用默认生成的析构?
会的,这里通过调试来一步步观察。
对象生命周期结束时,通过调试可以发现编译器先调用了显式的析构函数,观察运行窗口确实打印了"~MyQueue()"。接着,继续调试发现编译器调用了MyQueue默认生成的析构(调用了stack的析构函数),完成了资源的回收。在这里,我们就可以发现,显式析构函数的实现并未达到我们想要的目的,编译器还会调用MyQueue默认生成的析构从而达到了资源回收的目的。
因此,可以总结编译器不放心仍会调用不显示的析构(尽管写了显式析构),怕的就是内存泄露等问题。
8. 一个局部域的多个对象,C++规定后先定义后析构。(注意:这里指的是局部域的情况下)
拷贝构造函数
如果一个构造函数的第一个参数是自身类类型的引用(因为需要此参数来拷贝),且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。
特点
2. 拷贝构造函数的第一个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。 (拷贝构造函数支持多个参数,但后面的参数必须有缺省值)3. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,自定义类型传值传参和传值返回都会调用拷贝构造完成。

传值返回注意事项
在上面两段代码中,一个是传值返回,一个是传引用返回,分别运行后运行窗口都是没问题的。但是代码2这种情况实际上是不对的,不符合预期。
传值返回会产生临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),可以减少拷贝。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使用引用返回是有问题的,这时的引用相当于一个野引用,类似⼀个野指针一样。因此传引用返回一定要确保返回对象,在当前函数结束后还在,才能用引用返回。
4. 若未显式定义拷贝构造,编译器会成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成 员变量会完成浅拷贝(一个字节一个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。
默认生成的拷贝构造
在Func1()函数中,为传值返回,此时我们并没有写拷贝构造函数,编译器默认生成拷贝构造函数。但这种拷贝仅仅是浅拷贝,如果是针对有资源申请的成员则无法满足要求。上面这段程序并不涉及资源的申请,因此运行不会出错(一旦涉及资源申请,切记显式实现拷贝构造实现目的)。

Stack(const Stack& st){// 需要对_a指向资源创建同样大的资源再拷贝值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (_a = NULL){perror("malloc fail!");exit(1);}//malloc 成功memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}