一.默认构造
构造函数是特殊的成员函数,构造函数并不进行对成员变量开空间(局部变量在栈帧创建时,就已经有空间了),构造函数是为了初始化成员变量,构造函数是创建对象时会自动调用的
构造函数有以下特点:
-
函数名与类名相同
-
构造函数不需要返回值
-
对象实例化时会自动调用构造函数
-
构造函数可以重载
-
如果没有显示的创建构造函数,编译器会默认生成一个无参默认构造函数,当用户显示定义时就不会存在
-
无参构造函数,全缺省构造函数,以及编译器默认生成的构造函数都叫做构造函数,半缺省构造不能叫做默认构造函数;而且三者只能存在其一,不能同时存在无参构造和全缺省构造重载但会有调用歧义;
//加入有个类A
A a;
A a(1,2);//假如是半缺省
//类似这样只要没传参的调用才是 默认构造 -
编译器默认生成的构造函数对内置类型成员变量不会进行初始化,其初始值取决于编译器实现。对于自定义类型成员变量,编译器会尝试调用该类型的默认构造函数进行初始化。若该类型没有默认构造函数,则会导致编译错误。
二.析构函数
析构函数与构造函数的功能相反,析构函数并不是完成对对象本身的摧毁,变量生命周期到了,自己就摧毁了,而是需要我们将变量所分配的空间给释放掉,C++在对象销毁时会自动调用析构函数,所有没有动态开辟空间的类是不需要析构函数的,调用编译器默认的即可
析构函数特点
-
析构函数名是在类名前加上~
-
无参数无返回值
-
一个类只有一个析构,未显示定义编译器会生成
-
对象生命周期结束会自动调用析构函数
-
与构造函数相似,不显示写编译器析构函数对内置类型不做处理,自定义类型调用自定义类型的析构函数
-
显示的写析构函数时,自定义类型的成员也会调用它的析构函数,注意不要给自定义成员析构两次
-
当类中无需管理资源时,可以省略析构函数的显式定义,直接使用编译器生成的默认析构函数。
-
一个局部域的对象,C++规定后定义的先析构
三.拷贝构造函数
如果构造函数第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数叫做拷贝构造函数
拷贝构造函数特点
-
拷贝构造函数是构造函数的一个重载
-
拷贝构造函数第一个参数必须是类类型的引用传参,传值引用会导致无限递归的问题。拷贝构造函数可以接受多个参数,但第一个参数必须是该类类型对象的引用,后续参数则必须提供默认值。
-
C++规定自定义类型的对象进行拷贝必须调用拷贝构造,所以自定义类型的传值传参和传值返回都会调用拷贝构造
-
若未显示生成构造函数,编译器会生成一个,自动生成的拷贝构造对内置类型会完成值拷贝/浅拷贝,自定义类型会调用其自己的拷贝构造
-
如果一个类内没有分配资源,编译器自动生成的拷贝构造即可,但如果一个类需要分配资源,使用自动生成的拷贝就会有问题,因为浅拷贝,会让两个变量指向同一块资源,这明显不是我们想要的,所以需要自己实现默认构造函数达成深拷贝
-
传值返回会产生一个临时对象调用拷贝构造,所以当传值传参进行创建拷贝构造时,参数需要调用拷贝构造,这就导致了参数需要拷贝构造去创建,而拷贝构造的参数也需要拷贝构造,造成无限递归。但如果所返回的对象是局部内创建的,局部结束就销毁,传引用返回也就会有问题,类似野指针,所以要确保出了函数返回的引用对象还在
class A;
A a;
A b=a;//调用的也是拷贝构造
四.运算符重载
在C++中,当运算符作用于类类型的对象时,可以通过运算符重载来赋予其新的含义。C++规定类类型对象使用运算符时,必须转换为调用对应的运算符重载函数,若未定义相应重载则会引发编译错误。
运算符重载函数的特性:
- 由关键字
operator和运算符符号共同构成函数名 - 与其他函数类似,具有返回类型、参数列表和函数体
- 参数数量与运算符作用的运算对象数量一致:
- 一元运算符:1个参数
- 二元运算符:2个参数(左侧对象传给第一个参数,右侧对象传给第二个参数)
- 作为成员函数时,第一个运算对象默认绑定到
this指针,因此参数数量比运算对象少一个 - 重载后保持原运算符的优先级和结合性
运算符重载的限制:
- 不能创建语法中不存在的运算符(如
operator@) - 以下5个运算符不可重载(需重点记忆):
.*::sizeof?:.
- 必须至少包含一个类类型参数,不能改变内置类型运算符的含义
- 应选择对类有实际意义的运算符进行重载(如
Date类适合重载operator-而非operator+)
特殊运算符重载注意事项:
- 区分前置++和后置++:
- 后置++需添加
int形参以构成重载
- 后置++需添加
- 流运算符
<<和>>:-
需重载为全局函数
-
将
ostream/istream作为第一个参数 -
类类型对象作为第二个参数
-
避免因
this指针导致调用形式不符合常规习惯(如对象<<cout)//区分
class A;
A a;
A b=a;//调用拷贝构造函数
A c;
c=a;//调用赋值重载运算符
-
五.赋值运算符重载
赋值运算符重载是类的默认成员函数,用于实现两个已存在对象间的拷贝赋值操作。需要注意的是,它与拷贝构造函数有所区别:拷贝构造函数用于在创建新对象时通过已有对象进行初始化。
- 赋值运算符重载是一个运算符重载,规定必须重载为成员函数
- 有返回值,目的为了连续的赋值
- 当未显式实现时,编译器会自动生成默认的赋值运算符重载函数。该默认函数的行为类似于默认拷贝构造函数:对于内置类型的成员变量执行逐字节的值拷贝(浅拷贝),对于自定义类型的成员变量则调用其自身的赋值运算符重载函数。
六.取地址运算符重载
const成员函数
class A{
void print(){}
}
const A a;
a.print()//调用不了因为 默认print里面this参数是 A this类型而传入的是 const A this类型导致权限放大
如果把 void print()const{}
就可以使用a.print();了
- 将const修饰的成员函数成为const成员函数,const放在函数参数列表后面
const修饰该成员函数时,实际上是作用于隐含的this指针,表示在该成员函数中不能修改类的任何成员。对于Date类的Print成员函数,当使用const修饰后,其隐含的this指针类型会从Date* const this变为const Date* const this。
取地址运算符重载有两种一个是非const一个是const一般默认生成的就可以用了;
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
七.构造函数参数列表
- 构造函数除了在函数体内赋值,还可以用参数列表进行赋值,初始化列表。
- 每个成员变量在初始化列表只能出现一次,不能多次赋值,可以理解成初始化列表是每个成员初始化定义的地方
- 引用成员变量和const成员变量只能在初始化列表初始化,因为在默认构造函数体内无法给他们赋值
- C++11支持成员变量声明时缺省,这个缺省值是给没有初始化列表的成员变量用的
- 即使没有显示的使用参数列表,成员变量也会走参数列表,因为相当于给变量一个定义
- 初始化列表初始化顺序与列表顺序无关,而是与变量声明顺序一致

类型转换
C++ 支持将内置类型隐式转换为类类型对象,这需要类中定义了以该内置类型为参数的构造函数。
关键点:
- 若在构造函数前添加 explicit 关键字,则会禁用该隐式转换功能
- 类类型对象之间也可以进行隐式转换,前提是提供了相应的构造函数支持
static成员变量
使用static修饰的成员变量称为静态成员变量,必须在类外进行初始化。
特点:
- 静态成员变量由所有类对象共享,不属于特定对象,存储在静态区而非对象内存中
- 使用
static修饰的成员函数称为静态成员函数,没有this指针 - 静态成员函数可以访问其他静态成员,但不能访问非静态成员(因缺少this指针)
- 非静态成员函数可以访问所有静态成员变量和函数
- 访问方式:
- 通过类名::静态成员
- 通过对象.静态成员
- 静态成员同样受public/protected/private访问权限限制
- 静态成员变量不能在声明处用缺省值初始化,因为:
- 缺省值用于构造函数初始化列表
- 静态成员不属于特定对象
- 不通过构造函数初始化列表进行初始化