第七章 类
7.1 定义抽象数据类型
类成员
- 必须在类的内部声明,不能在其他地方增加成员。
- 成员可以是数据,函数,类型别名。
- 类的const成员函数不会修改类的数据成员:
void func() const;
- const成员函数的声明和定义处都要加const
- const成员函数不能调用本类的非const成员函数
- 内联函数
- 定义在类内部的函数是隐式inline函数
- inline成员函数应该与类定义同一个头文件中
- 可变数据成员 (mutable data member):表示数据成员永远可变
mutable int cnt;
,这样即使在const成员函数中也可以修改cnt的值
- [[ch07-类#返回
this
的成员函数|返回this的成员函数]] - 构造函数
- 构造函数初始值列表:
Sales_item(): units_sold(0), revenue(0.0) { }
^742596- 但是类内初始值必须使用等号或者花括号进行初始化
- 当一个类没有定义任何构造函数时,编译器才会生成一个默认构造函数(也称合成的默认构造函数),使用
=default
要求编译器使用合成的默认的构造函数。 ^15282e
- 构造函数初始值列表:
非成员函数
- 和类相关的非成员函数,定义和声明都应该在类的外部。
7.2 访问控制与封装
- 访问说明符(access specifiers):
public
、private
、protected
class
与struct
都可以被用于定义一个类,唯一的却别在于默认访问权限:class
:默认成员是priavte
的。struct
:默认成员是public
的。
友元
- 在类A中设置友元B(函数或类),即允许B访问A中的非共有成员
- 语法相关:
- 通常将友元声明成组地放在类定义的开始或者结尾,但友元不是类的成员,不受public/private的约束
- 如果类想把一组重载函数声明为友元,需要对这组函数中的每一个分别声明。
- 友元关系不存在传递性。
- 把其他类的成员函数声明为友元时,必须明确指定该函数所属的类名。
- 友元函数可以直接定义在类的内部(隐式内联),但是必须在类外部提供相应声明,并且要在调用之前进行声明
1 2 3 4 5 6 7 8 9 10 11 12
struct X { friend void fir() { /* do something */ } public: void pub(); private: void pri(); }; void X::pub() { fir(); } // 错误:友元函数fri在类外必须进行声明且需要在调用之前进行声明 void fir(); // 类X的友元函数fri,在类外进行声明 void X::h() { f(); }
7.3 类的其他特性
this
- 每个成员函数都有一个额外的、隐含的形参
this
,this
总是指向调用该成员函数的对象- 在普通成员函数中,this是一个
T *const
类型的指针 - 在const成员函数中,this是一个
const T *const
类型的指针(因此数据成员无法修改) - 静态函数中不能使用this指针
- 在普通成员函数中,this是一个
- const对象或是const对象的指针/引用,只能调用const成员函数;否则均可
return *this;
可以让成员函数连续调用。
类类型
- 可以声明一个类而暂时不定义它,称为前向声明,用于引入类的名字;在前向声明之后、定义之前是一个不完全类型
- 可以定义指向不完全类型的指针或引用,也可以声明(但是不能定义)以不完全类型作为参数或返回值类型的函数
[[ch07-类#友元|友元]]
7.4 类的作用域
类型别名如果在类外已经定义过,不能在类内再次定义。
7.5 构造函数再探
- 使用初始值列表进行初始化才是真正的初始化,在构造函数体中进行“初始化”只是赋值
- 如果是const成员或是引用类型,则必须在构造函数初始值列表中将其初始化(圆括号初始化)例子
委托构造函数 (delegating constructor)
委托构造函数通过其他构造函数来执行自己的初始化过程
- Sale_data(): Sale_data("", 0, 0) {}
隐式的类型转换
- 转换构造函数:如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,因此可以叫做转换构造函数。
- 编译器只会自动进行一步的隐式类型转换
- 将转换构造函数声明为
explicit
从而抑制隐式类型转换- explicit关键字只对接受一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,因此也无须将其指定为explicit
- 只在类内声明构造函数时使用explicit,在类外部定义时不应重复
explicit
构造函数只能用于直接初始化(圆括号初始化),不能用于拷贝赋值初始化(例子)
聚合类
字面值常量类
constexpr
函数的参数和返回值必须是字面值。- 字面值类型:算数类型,引用,指针,聚合类
- constexpr 构造函数
7.6 类的静态成员
- 语法相关:
- 静态成员可以是public或是private的
- 静态成员可以是常量、指针、引用、类
- 静态成员函数不包含this指针,不能声明为const成员函数
- 定义和初始化
- 在类内声明,类外定义并初始化。
- 在类外定义时,不能重复 static 关键字,static 只出现在类内的声明中。
- 只有
constexpr
类型的静态数据成员可以在类内定义。
- 使用:
- 使用作用域运算符
::
直接访问静态成员:r = Account::rate();
- 也可以使用类的对象访问:
r = ac.rate();
- 使用作用域运算符
- 特殊场景:例子
- 静态数据成员的类型可以是不完全类型,比如可以是它所属的类类型,而普通变量不能(只能声明为所属类类型的指针或引用)
- 可以使用静态成员变量作为函数的默认实参