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
两个同名的对象或函数,否则会使编译器连接失败。
命名空间的作用域就像类一样,是全局的。