Composition
Each object has its own memory consists of other objects. -- Alan Kay
组合是在一个类中使用其他的类的对象作为成员。
包含的两种类型:
- 完全包含(Fully)
- 引用包含(By reference)(这种方法允许共享对象)
语法
class Person{}; class Currency{}; class SavingsAccount{ public: SavingsAccount(const char* name, const char* address, int cents); ~SavingsAccount(); void print(); private: Person m_saver; Currency m_balance; };
构造函数
SavingsAccount::SavingsAccount(const char* name, const char* address, int cents): m_saver(name, address), m_balance(0, cents){}
注意事项:
- 可以使用子对象的构造列表
- 如果不使用列表初始化,子对象的默认构造函数会被调用
子对象在本体中的属性
Private vs. Public
通常来说,子对象应该设为 private
。这是因为我们希望子对象只与本体对象交互,而对象与外界的交互应该由本体的接口完成。
但如果你想将子对象作为本体与外界的一个交互平台,那么应该设为 public
。
Fully vs. By Reference
如果子对象完全包含于本体,那么本体中会为子对象保留空间,同时子对象的构造函数、析构函数会自动调用。
如果本体只包含子对象的引用,那么本体中只保留了指针的空间,子对象的构造和析构需要在类的外部手动完成。
应当使用引用包含的情况:
-
逻辑上子对象应该在外部(监视器等)
-
子对象的大小在开始时不确定
-
子对象的空间应该在运行时被分配,或者被链接过来
其他的编程语言只使用 By reference。
举例:Clock Display
我们想实现一个表示时间的类,有时、分、秒。可以分别用对象表示。
时间流逝,可以让秒动一下,如果到了秒的上界(60),就让分动一下。
numberdisplay.h
部分内容:
class NumberDisplay{ public: NumberDisplay(int limit, int value = 0);// create a number display with a given up-limit void setValue(int value);// set the value int getValue();// get the value std::string toString();// get a string description of the value, with leading 0 for value less than 10 bool dida();// increase the value by one, turn over to zero if the value reaches the limit, returns true if the value turned to zero private: int value, limit; };
clock.h
部分内容:
class Clock{ public: Clock(int hour, int minute, int second);// create a clock with given time std::string toString();// get a string description of "hh:mm:ss" void dida();// run a second private: NumberDisplay hh, mm, ss; };
具体实现,请读者自行补充(可能是一次 Lab)。
Namespace
需求
通过作用域避免名字的冲突。
之前使用的方法:
class Marbles{ enum Colors{Blue, Red, Green}; //... }; class Candy{ enum Colors{Blue, Red, Green}; //... };
现在可以使用 namespace
语句声明命名空间,完成此类任务。
只推荐在仅需要命名封装的时候使用 namespace
。
语法
namespace sp1{ void f(); void g(); } namespace sp2{ void f(); void g(); }// No terminating end colon! namespace alias = sp1; void f(); void g(); int main(){ sp1::f(); sp2::f(); ::f(); f();// the same as ::f() alias::f();// the same as sp1::f() return 0; }
通过 using
语句可以使我们包含一个命名空间内的内容,去掉 ::
。
using std::cin; using namespace std;
显然,不能 using
两个同名的对象或函数,否则会使编译器连接失败。
命名空间的作用域就像类一样,是全局的。