C++的六个默认成员函数。
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成6个默认成员函数 ,构造函数,析构函数,拷贝函数,赋值重载函数,普通对象和const对象的取地址重载。这些函数如果创建类的人没有显式定义,编辑器将自己生成默认函数。
class Date {};
构造函数
概念
对于一个类,我们可以定义一个方法,来对类的内容做出合适设置。但是这样频繁地调用方法是很麻烦的。能不能在这个类创建的时候就自动初始化呢?
构造函数能做到这一点。构造函数是一个很特殊的成员函数,它的名字与类名相同,当创建一个类时,编辑器将自动调用该函数。保证类的每个数据成员都有一个合适的初始值。并且,该函数在类的生命的周期中只会调用一次。
特性
构造函数虽然有构造两字,但是构造函数并不是用来创建类的。它是在一个类被创建的时候,去初始化这个类。
构造函数的函数名与类目相同。
构造函数没有返回值,注意这里的没有返回值就是什么不写,而不是写void。如下面代码就显式定义了一个构造函数。
class Date { public: void Print(); Date(int year = 2022, int month = 5, int day = 20) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; };
当一个对象实例化的时候,编辑器会自动调用构造函数。
构造函数可以重载。如下面代码就将构造函数进行了重载。
class Date { public: void Print(); Date(int year = 2022, int month = 5, int day = 20) { _year = year; _month = month; _day = day; } Date(int year,int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; void Date::Print() { cout << _year << "-" << _month << "-" << _day << endl; } int main() { Date tmp; tmp.Print(); Date x(2022, 5, 20); x.Print(); return 0; }
如果类中没有显式定义构造函数,编辑器会自动生成一个默认构造函数,如果有定义则不会生成。
不管是我们显式定义的无参构造函数,还是全缺省构造函数,又或者是编辑器默认生成的构造函数,都称为默认构造函数。默认构造函数,在不初始化的创建,会自动调用默认构造,如:
Date tmp;
默认构造和普通构造是有些区别的,默认构造只允许存在一个。
- 对于编辑器生成的默认构造函数,其实是存在一些问题的。因为编辑器生成的默认构造函数并不会初始化内置类型成员(如int,char,float等),这些内置类型成员仍会为随机值。那这个默认构造在干嘛呢?
虽然对于内置类型成员,生成的默认构造函数并不会初始化。但是对于自定义类型成员,生成的默认构造函数会去调用该自定义类型成员的默认构造函数。
也就是说,当我们的一个类,它的成员全是自定义类型时,而恰好这些成员的默认构造函数都有。那么我们就不需要去写这个类的默认构造函数,直接用编辑器生成的即可。
析构函数
概念
析构函数和构造函数刚好是相反的。构造函数用于初始化,析构函数则用于类的一些资源清理工作。注意析构函数并不是用来销毁对象的,销毁对象的工作是编辑器来完成的。当局部对象除了作用域时,编辑器进行销毁工作。
那么析构函数的资源清理是什么呢?其实就是有些类可能malloc了一些空间,当一个局部变量出了作用域时,这些空间可能没被释放。这就需要析构去将他们进行free。
特性
- 析构函数的函数名为,~ + 类名。
- 析构函数没有任何参数,也没有任何返回值。
- 一个类只能有一个析构函数,如果没有显式定义,编辑器将自动生成默认的析构函数。
- 当一个对象的生命周期结束后,编辑器将自动调用析构函数。
- 和构造函数一样,当一个类中有自定义成员时,析构函数会调用这些自定义成员的析构函数。
拷贝构造函数
概念
在创建类时,我们能否,复制出一个一模一样的类呢?拷贝构造函数就能完成这件事。
拷贝构造函数只有一个形参(不包括this),该形参是同类对象的引用,一般最好要加const修饰。当用已经存在的类型去创建一个新类型时,编辑器自动调用该函数。
特性
- 拷贝构造是构造的一种重载。
- 拷贝构造只有一个参数,且必须是同类对象的引用。如果是传值参数,将会发生无限循环。因为形参是实参的一份拷贝。当把这个形参传入时,又需要拷贝构造。就这样无限循环。而且最好加const修饰,防止权限的放大。
- 如果没有显式定义拷贝构造函数,编辑器将生成默认的拷贝构造函数。默认的拷贝构造函数对于内存的中的数据进行按字节完成拷贝。这种拷贝叫做浅拷贝,也叫值拷贝。但是这种拷贝是有问题的,例如当类中有指针变量时,浅拷贝只会把两个指针变成相同的值,而忽略了实际意义。如当这个指针指向的是一个栈时,浅拷贝只是让两个指针指向同一个栈,而没有去创建一个新的栈。
运算符重载
概念
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数。该函数名为关键字operator加需要重载的运算符符号。
注意:
- 不可以通过operator来引入新的操作符。
- 重载操作符必须要一个类类型或者枚举类型的操作数。
- 用于内置类型的操作符,其含义不能改变,如:int+,不能改变其+的含义。
- 运算符重载,都默认有一个this形参,且限定为第一个形参。
- *. ** , :: , sizeof , **?: ** , . 注意这五个运算符时不可以重载的。
特性
- 当一个类要实现 += 和 + 时,优先实现 += ,然后用 += 实现 + ,会提高效率。
- 前置++ 和 后置++ 是不相同的,一是前置++除了this外没有参数,后置++除了this外还有一个参数,只是这个参数可以只写int,而不写参数名。二是前置++,返回的是++后的 *this,后置++返回的是,修改前的 *this(创建临时变量,用来返回)。
const成员函数
概念
将const修饰的类成员函数称之为const成员函数。const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
特性
class Date { public: void Print() const { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; };
当加上const修饰后,*this将会被修饰,不能被改变。
取地址及const取地址操作符重载
这两个默认成员函数一般不需要重现定义,编辑器会默认生成。
class Date { public: Date* operator&() { return this ; } const Date* operator&()const { return this ; } private: int _year ; // 年 int _month ; // 月 int _day ; // 日 };
rn this ;
} const Date* operator&()const { return this ; }
private:
int _year ; // 年
int _month ; // 月
int _day ; // 日
};