🌟 各位看官好,我是egoist2023!
🌍 种一棵树最好是十年前,其次是现在!
🚀 今天来学习C++类和对象的语法知识
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦
目录
类的默认成员函数
构造函数
内置类型和自定义类型
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;
}