C++环境下每一个类在定义是时编译器会自动生成六个成员函数(在没有显示定义的情况下),分别是构造函数、析构函数、拷贝构造函数、赋值运算符重载、普通变量和const常量的取地址重载,它们大大弥补了原先C语言的一些不足,为代码编写者提供了极大的遍历,下面我们来一一解释各个默认成员函数
以栈数据结构为例子,C环境下我们在实例化一个栈结构的时候,必须手动调用初始化函数,但这一步也经常在编写代码的时候忽略,本贾尼先生思来想去想出了一个绝妙的方法,即在实例化类对象的同时让编译器自动调用一个能够给类属性赋初值的成员函数
为了区分与一般成员函数的与众不同,构造函数的形式非常独特,它没有返回值,但不需要再声明定义处前加上void关键字,当不需要传参时调用不需要添加空括号,函数名与类对象名相同。
//栈
class Stack
{
public:
Stack(int n=4){
_a=(int*)malloc(sizeof(int)*n);
_size=0;
_capacity=0;} //显示定义带有缺省值构造函数
private:
int *_a;
int _size;
int _capacity;
};
Stack st1; //不传参调用,写括号编译器分不清是调用还是声明
Stack st1(10); //传参调用
如果我们显示声明定义了构造函数,那么编译器就不会自动生成,而是执行我们写的,那么如果我们不写的话打印一下三个类属性会出现什么结果呢?
答案是一串随机值,这里可以说是C++的一个缺陷,自动生成的构造函数不会对内置类型作出任何处理,只对自定义类型调用该类型本身的构造函数,举个例子
class Queue
{
public:
Queue(){
_head=_tail=nullptr;}
private:
int* _head;
int * _tail;
};
//定义一个Queue对象 q1 充当Stack的类属性
在调试窗口观察四个类属性的值,
q的值确实被初始化成了空指针(由于编译器版本的原因,可能会将内置类型初始化为0,但这里我们依然认为不对内置类型做处理)
C++11在此基础上做了优化,在类属性声明处可以直接给缺省值来实现初始化,具体例子在讲解拷贝构造时给出
C环境下总是要手动调用销毁结构释放空间,同样极易被遗忘,从而在C++环境中衍生出另一个默认成员函数,当类对象生命结束时会自动调用析构函数实现空间释放
与构造函数很像,析构函数也是没有返回值且不需要前缀void的特殊函数,并且它不具有参数,不能重载(构造函数可以重载)
为了区分构造函数,析构函数的函数名前有前缀按位取反符 ~,
//实现一个栈的析构函数
class Stack{
public:
//………………
~Stack(){
free(_a);_a=nullptr;
_size=_capacity=0;}
//………………
};
同理如果不显示声明定义,编译器会自动生成析构函数并调用
我们有时候会想要创造一个与一个已有类对象的对象,这个时候我们就需要拷贝,编译器默认的拷贝方式是浅拷贝(值拷贝),即将被拷贝的值逐个字节的传递给拷贝值,这样的方式对于int、char、double这些内置类型是没有问题的,但是对于指针类型或者自定义类型是不行的,原因是因为如果含有指针类型,浅拷贝后会出现两个指针指向同一块空间的情况,这样出现两次析构的时候就会报错,因此这里需要采用深拷贝的方式,C++使用了拷贝构造函数来实现。
拷贝构造函数是构造函数的重载,没有返回值,参数类对象引用,(这里必须通过引用传参,一是能够提升效率,二是如果传值调用会出现无限递归的结果导致程序崩溃,具体图解在后文会给出)
class Date//定义日期类
{
public:
Date(const Date& d){ //我们不想改变被拷贝值,加const是一个好习惯
_year=d._year;
_month=d._month;
_day=d._day;}
private:
_year=2024;
_month=1;
_day=1;//C++11的特性
};
Date d1;
Date d2(d1);//①
Date d2=d1; //② 两种方法是等价的,都是初始化时将d1值拷贝给d2
解释为何不能传值调用:
既定事实:自定义类型的拷贝需要使用拷贝构造
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)
1、不能通过连接其他符号来创建新的操作符:比如operator@
2、重载操作符必须有一个类类型或者枚举类型的操作数
3、用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4、作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
5、**. 、:: 、sizeof 、?: 、.** 注意以上5个运算符不能重载*
以对+运算符的重载为例(Date类)
class Date//定义日期类
{
public:
Date& operator+(int year)
{
_year+=year;
return *this;//思考一下为什么传引用返回
}
private:
_year=2024;
_month=1;
_day=1;//C++11的特性
};
Date d1;
d1+1;//隐式调用
d1.operator+(1);//显示调用
赋值运算符重载:
赋值运算符作为默认成员函数之一,对于类属性只有内置类型的对象可以不需要我们手动编写,编译器会自动生成并调用。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,故不做过多阐述。
……………………………………………………………………………………………………