《A Tour of C++》要点收录

二、用户定义类型

2023-10-15  本文已影响0人  akuan

Link


我们通常希望数据表示对用户不可访问,从而避免被使用, 确保该类型数据的使用一致性,这还让我们后续能够改进数据表示。 要达成这个目的,必须区分类型的(供任何人使用的)接口和(可对数据排他性访问的)实现。 这个语言机制叫做类(class)。 类拥有一组成员(member),成员可以是数据、函数或者类型成员。 接口由类的public成员定义,而private成员仅允许通过接口访问。例如:

class Vector{
public:
    Vector(int s) :elem{new double[s]}, sz{s} { }
    double& operator[](int i) { return elem[i]; }
    int size() { return sz; }
private:
    double* elem;   // 指向元素的指针
    int sz;         // 元素的数量
};

有了这些,就可以定义新的Vector类型的变量了:

Vector v(6);    // 具有6个元素的Vector

Vector对象可图示如下:


大体上,Vector对象就是个“把手”, 其中装载着指向元素的指针(elem)和元素数量(sz)。 元素数量(例中是6)对不同的Vector对象是可变的,而同一个Vector对象, 在不同时刻,其元素数量也可以不同。但是Vector对象自身的大小始终不变。 在C++中,这是处理可变数量信息的基本技巧:以固定大小的把手操控数量可变的数据, 这些数据被放在“别处”(比如用new分配在自由存储上)。

在这里,Vector的数据(成员elem及sz)只能通过接口访问, 这些接口都是public成员:Vector()、operator及size()。


联合(union)就是结构体(struct),只不过联合的所有成员都分配在相同的地址上, 因此联合所占据的空间,仅跟其容量最大的那个成员相同。 自然而然,任何时候联合都只能持有其某一个成员的值。
举例来说,有个符号表条目,它包含一个名称和一个值。其值可以是Node*或int:

enum Type { ptr, num }; // 一个 Type 可以是ptr和num
struct Entry {// 结构体
    string name;    // string是个标准库里的类型
    Type t;
    Node* p;        // 如果t==ptr,用p
    int i;          // 如果t==num,用i
};
void f(Entry* pe) {
    if (pe->t == num)
        cout << pe->i;
    // ...
}

成员p和i永远不会同时使用,但这样空间就被浪费了。 可以指定它们都是某个union的成员,这样空间就轻而易举地节省下来了,像这样:

union Value {// 使用联合
    Node* p;
    int i;
};
/******然后这样使用:******/
struct Entry {
    string name;
    Type t;
    Value v;    // 如果t==ptr,用v.p;如果t==num,用v.i
};
void f(Entry* pe) {
    if (pe->t == num)
        cout << pe->v.i;
    // ...
}

标准库有个类型叫variant,使用它就可以避免绝大多数针对 联合 的直接应用。 variant存储一个值,该值的类型可以从一组类型中任选一个。 举个例子,variant<Node,int>的值,可以是Node或者int。

借助variant,可以这样写:

struct Entry {
    string name;
    variant<Node*,int> v;
};
void f(Entry* pe) {
    if (holds_alternative<int>(pe->v))  // *pe的值是int类型吗?
        cout << get<int>(pe->v);        // 取(get)int值
}

很多情况下,使用variant都比union更简单也更安全。


枚举用于表示一小撮整数值。 使用它们,可以让代码更具有可读性,也更不易出错。

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指明了这是个强类型的枚举,并且限定了这些枚举值的作用域。 作为独立的类型,enum class有助于防止常量的误用。 比方说,Traffic_light和Color的值无法混用:

Color x = red;                  // 错误:哪个颜色?
Color y = Traffic_light::red;   // 错误:此red并非Color类型
Color z = Color::red;           // OK

同样,Color的值也不能和整数值混用:

int i = Color::red; // 错误:Color::red不是int类型
Color c = 2;        // 初始化错误:2不是Color类型

默认情况下,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;   // next 将是 Traffic_light::green

默认情况下,枚举值的整数值从0开始,每增加一个新枚举值就递增一。

如果你的枚举值不需要独立的作用域,并希望把它们作为int使用(无需显式类型转换), 可以省掉enum class中的class,以获得一个“普通”enum。 “普通”enum中的枚举值的作用域跟这个enum相同,还能隐式转换成整数值。

忠告

上一篇 下一篇

猜你喜欢

热点阅读