2.3 Classes
struct与class没有根本上的差别;我们可以认为struct是class的简略版本,其中的成员变量,默认都是public。在struct中,我们也能够定义构造函数,析构函数以及成员函数。
2.4 Unions
我们可以将union作为struct,只不过union中的所有成员都会分配在同一个地址,也就是说,union所占用的空间就是其最大的成员所占的空间。一般来说,在同一时刻,一个union只能拥有其中的一个成员。假设,有一个符号表,其中的每一个成员都有一个名字以及一个值。而每一个值既可以是Node*也可以是int:
enum Type { ptr, num }; // 表示值的类型 struct Entry { string name; Type t; Node* p; int i; }; void f(Entry* pe) { if(pe->t == num) cout<< pe->i; // ... }
很明显,成员变量p与i并不会同时使用,所以我们使用struct作为Entry的类型就会造成空间的浪费。此时,我们就可以使用union:
union Value { Node* p; int i; }; struct Entry { string name; Type t; Value v; // t为ptr,那么我们使用v,反之则使用i。 }
但是上述代码中,我们必须要维护Type中的变量与union中的成员变量的一一对应的关系,这显然很容易造成错误。因此,一般来说,我们会将union以及其对应的Type封装在一个class中。在实践中,我们也建议大家尽可能减少“裸露”union的使用。在c++的标准库中,variant能够很方便的用于替代union类型,且使用的安全性更高。
struct Entry { string name; variant<Node*, int> v; }; void f(Entry* pe) { if(holds_alternative<int>(pe->v)) cout << get<int>(pe->v); }
2.5 Enumerations
enum class Color { red, blue, green }; enum class Traffic_light { green, yellow, red }; Color col = Color::red; Traffic_light light = Traffic_light::red;
上述代码中enum后面的class表示,该枚举类型是强类型。这从一定程度上能够避免枚举之间的混淆:
Color x = red; // error!!! 哪一个red? Color y = Traffic_light::red; // error!!! 不属于Traffic_light Color z = Color::red; // 运行ok int i = Color::red; // error!!! Color::red并不是int Color c = 2; // error!!! 2并不是Color类型 Color x = Color{5}; // 运行ok,但是有点冗长 Color y {6}; // 运行ok
默认情况下,enum class只有赋值,初始化与比较运算。但是,我们可以定义其运算符,例如:
Traffic_light& operator++(Traffic_light& t) { switch(t) { case Traffic_light::green: return t = Traffic_light::yellow; case Traffic_light::yellow: return t = Traffic_light::red; case Traffic_light::red: return t = Traffic_light::green; } } Traffic_light next = ++light;
2.6 Advice
[06] 避免“裸露”的union,将其与相对应的type封装在class中。
[08] 使用class enum而不是单纯的enum以避免隐式转换。
[09] 为枚举类型定义运算符,使其更便于使用也更安全。