类
引入 const 成员函数
默认情况下 this 指针是指向类类型的非常量版本的常量指针。其数据类型是一个 ClassName *const。这是一个顶层const,它一直指向某个固定的 ClassName 对象(注意这个对象并不一定是const)。但是由于数据类型不一样,不能把 this 指针绑定在一个常量对象上(即 const ClassName)。也就是不能在常量对象上调用普通的成员函数。
为了能让 this 指针能够绑定到const对象上,所以 this 指针的数据类型应该是 const ClassName *const。但是 this 指针是个隐式的,所以给他挂上这个底层const的工作就得依靠const成员函数。其声明方式就是在形参列表后面加一个const,如
1 | string isbn() const {return this->bookNo;} |
这个const
表示this
是一个指向正儿八经常量的指针
定义类相关的 非成员函数
某些函数从概念上来说属于类的接口的组成部分,但是他们实际上并不属于类本身,比如一些成员函数的辅助函数。不过一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该和类在同一个头文件内。
构造函数
构造函数不能被声明成 const
类型。因为即使创建一个 const
对象,直到对象被构造函数初始化之后才能取得其 const
属性。所以构造函数可以在 const
对象构造的过程中向其写入值。
注:如果包含有内置类型或复合类型的成员,则只有当这些成员全被赋予了类内初值时,这个类才适合于使用合成的默认构造函数。
定义构造函数有如下几种方式(先用struct关键字)
1 | struct Sales_data |
= default 的含义:在C++11中,如果需要默认行为,可以在参数列表后面写上 = default
来要求编译器生成构造函数。如果 = default
出现在类内,则默认构造函数是内联的;如果出现在类外,则默认构造函数不是内联的。这个默认构造函数之所以有效,是因为我们为内置类型的数据成员提供了初值。
构造函数初始值列表:即构造函数后的冒号以及冒号到花括号之间的代码。里面放的是类内成员名字的列表,后面跟的是成员初始值(可以使用小括号也可以使用花括号)。不同成员的初始化通过逗号分隔。
构造函数不应该轻易覆盖掉类内初始值。
使用构造函数初始化列表和在构造函数中为成员赋值这两种操作在某些时候有很大差别,如
1 | class ConstRef |
访问控制与封装
当我们希望定义的类的所有成员是 public
时,使用 struct
;如果希望成员是private
的,使用class
。这也是struct
和class
的唯一区别
友元
类可以通过友元函数或友元类来允许别的函数和类访问自己的非公有成员。友元不是类的成员,也不受访问控制的约束。如果类想把一个函数作为他的友元,只需要增加一条以friend开头的函数声明语句。
1 | class Sales_data |
友元的声明仅仅制定了访问的权限,而非一个通常意义上的函数声明。如果希望该函数被调用,就必须在友元声明之外再专门对函数进行一次声明。通常把友元函数的声明放到和类本身相同的头文件中。
类之间的友元关系
如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。如
1 | class Screen |
这样声明之后Window_mgr
中所有的成员都可以访问screen
中的成员了。需要注意的是,友元关系不存在传递性。
令成员函数作为友元
也可以让某个类中的某几个成员函数作为友元
1 | class Screen |
函数重载和友元
重载的函数需要对这组函数中的每一个部分分别声明。记住:重载函数仍然是几个不同的函数!
类成员
类可以自定义某种类型在类中的别名,也存在访问限制,可以是public
或private
的一种。用来定义类型的成员必须先定义后使用
1 | class Screen |
可变数据成员
在变量声明中加入一个mutable
关键字来获得一个可变数据成员,它在任何情况下都不会是const
类型,就算是const对象也可以改。
类内初值
当我们提供一个类内初值时,必须以符号 = 或者花括号表示
返回 *this 的成员函数
1 | class Screen |
类的声明
不完全类型(前向声明得来的类型)只能在非常有限的情况下使用:可以定义指向这种类型的指针或引用,也可以声明(但不能定义)以不完全类型作为参数或者返回类型的函数。一旦一个类的名字出现后,他就被认为是声明过了(但是没有定义)。所以类允许包含指向它自身类型的引用或指针。如
1 | class Screen; |
友元声明和作用域
即使在类的内部定义了友元函数,也必须在类的外部提供相应的声明从而使得函数可见。例如
1 | struct X |
类的作用域
以前从未见过的用法:
1 | class Window_mgr |
名字查找与类的作用域
成员函数体直到整个类可见后才会被处理。所以成员函数可以使用类中定义的任何名字。
成员初始化的顺序
成员初始化的顺序与它们在类的定义中出现顺序一致。而构造函数的初始值列表不能限定成员初始化的具体顺序。如
1 | class X |
注:尽可能使用构造函数的参数作为成员的初始值,这样就不用考虑初始化顺序了。
委托构造函数
C++11新增:一个委托构造函数使用他所属类的其他构造函数执行他自己的初始化过程。在委托构造函数内,成员初始值列表只有一个唯一的入口就是类名本身。如
1 | class Sales_data |
隐式的类类型转换
如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,该构造函数有时又称转换构造函数。能通过一个参数调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。在需要使用类的时候,可以通过实参的类型来代替,如
1 | string null_book = "9999-9999"; |
因为Sales_data类的combine函数是一个const引用,所以是可以传递临时量的。
但是这种转换只能是一步一步的,例如下面的代码就是错误的
item.combine("9999-9999");
因为他进行了两步隐式转换:首先要从”9999-9999”转换到string类型,其次要把这个string类型转成Sales_data类。如果要想这样用,要按照以下方式
1 | item.combine(string("9999-9999")); |
抑制构造函数的隐式转换
使用关键字explicit来阻止这种转换,如
1 | class Sales_data |
explicit关键字只对 一个实参 的构造函数有效!而且只能在类内声明构造函数时使用explicit关键字,在类外部定义的时候不应该重复。如
1 | explicit Sales_data::Sales_data(istream &s) //这样是错误的 |
当使用explicit关键字声明构造函数时,他将只能以直接初始化的形式使用。而且编译器不会在自动转换过程中使用该构造函数。
可以使用强制类型转换来使用explicit构造函数,如
1 | item.combine(Sales_data(null_book)); //这是显式调用explicit构造函数 |
聚合类
使得用户可以直接访问其成员,并且它具有特殊的初始化语法形式。满足如下特点的就是聚合类:所有成员都是public、没有定义任何构造函数、没有类内初值、没有基类,也没有virtual函数,如
1 | struct Data |
字面值常量类
数据成员都是字面值类型的聚合类是字面值常量类,或者满足下面要求的非聚合类也是字面值常量类:数据成员都是字面值类型、类必须至少含有一个constexpr构造函数、如果数据成员含有类内初值,则类内初值必须是常量表达式;如果成员属于某种类类型,则初始值必须使用类成员自己的constexpr含构造函数、累必须使用析构函数的默认定义。
constexpr构造函数
构造函数不能是const,但是字面值常量类的构造函数是constexpr。constexpr构造函数可以声明成 = default,否则函数体内只能为空。使用前置关键字constexpr来声明constexpr构造函数
1 | class Debug |
constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型,如
constexpr Debug io_sub(false, true, false);
类的静态成员
静态成员和类本身直接相关,而不是和类的各个对象保持关联。静态数据成员这个类的被所有对象共享。静态成员不包含this指针,也不能被声明称const的。如
1 | class Account |
定义静态成员
可以在类内或者类外定义静态成员函数,如果在类外定义静态成员时,不能重复static关键字,它只出现在类内的声明语句,如
1 | void Account::rate(double newRate) //指向类外部的静态成员时,必须指明成员所属的类名。static只出现在类内声明 |
注:必须在类外定义和初始化每个静态成员
静态成员的类内初始化
通常情况下静态成员不能在类内初始化,但是可以为他提供const整数类型的类内初始值,不过要求静态成员必须是字面常量类型的constexpr。如
1 | class Account |
静态数据成员由于独立于任何对象,所以他可以是不完全类型。如
1 | class Bar |
静态成员可以作为默认实参而普通成员不行。因为普通成员属于对象的一部分。如
1 | class Screen |