CppCoreGuidelines C.9 最小化数据展露
02 August 2022
“Minimize exposure of members”
理由
封装。信息隐藏。最小化意外访问。简化代码维护。
例子
template<typename T, typename U> struct pair { T a; U b; // ... };
因为 a 和 b 是公开的,不管注释部分提供了什么接口, pair 的用户都可以随意且独立地修改 a 和 b 的数据。在更大的代码库中,我们无法轻松找到哪些代码修改了 pair 的哪些成员数据。如果我们想强化成员变量的关系,最好是把它们设置成私有数据,然后通过构造函数和成员函数来强化他们之间的关系(不变体,invariant)。
在以下例子中,我们通过 meters 从成员变量中计算出以米为单位的距离数据。通过 set_unit
设置距离单位信息。这样,就不用直接访问成员数据进行计算,确保了 meters
返回的是正确的数值。想象一下,如果用户不知道 magnitude
和 unit
之间的关系,直接访问 magnitude
就可能用错数据值了。
class Distance { public: // ... double meters() const { return magnitude*unit; } void set_unit(double u) { // ... check that u is a factor of 10 ... // ... change magnitude appropriately ... unit = u; } // ... private: double magnitude; double unit; // 1 is meters, 1000 is kilometers, // 0.001 is millimeters, etc. };
注意
如果哪些数据能被哪些用户直接访问还不确定,那么这个数据类型就很难修改改进。public 和 protected 成员数据就是这种情况。
例子
类通常会提供两类接口给用户。一类给继承此类的子类(protected),另一类提供给普通用户(public)。比如,继承的类可以跳过运行时检查,因为已经确保运行时正确了。
class Foo { public: int bar(int x) { check(x); return do_bar(x); } // ... protected: int do_bar(int x); // do some operation on the data // ... private: // ... data ... }; class Dir : public Foo { //... int mem(int x, int y) { /* ... do something ... */ return do_bar(x + y); // OK: derived class can bypass check } }; void user(Foo& x) { int r1 = x.bar(1); // OK, will check int r2 = x.do_bar(2); // error: would bypass check // ... }
注意
尽量不要用 protected
访问层级的数据
注意
按照以下顺序排列成员变量:public, protected, private
强化
- 标记 protected 数据
- 标记 public 和 private 数据混合在一起的地方