决定用继承前,我们要考虑几个问题:
1. 是否真的需要使用继承,还是应该用复合?
继承表达的是is-a关系,派生类应该和父类有相同的行为。
复合表达的是has-a关系,只需要复用子类的实现,而不需要子类的接口。
2. 是否需要多继承?
单继承的层次更为清晰,多继承则会引入更多的复杂性,使用的时候应该谨慎。
多继承会导致变量存在多份的现象,避免的方法是使用virtual进行继承。
多继承也有合理应用的场合,如:使用public方式从一个类继承接口,同时用private方式从另一个类继承实现。
3. 继承的时候,用private、还是public?
private继承方式表达的是implemented-in-term-of,用于从一个类继承实现。
如果需要访问父类的protected类型成员,又不希望继承接口,就可以用private类型的继承。
4. 需要继承什么,接口还是实现?
定义pure virtual 函数,继承的是接口。pure virtual函数要求派生类必须提供实现。容易让人忽略的是,pure virtual函数也是可以有实现的。利用这个特性,我们可以定义接口,同时提供默认的实现。派生类中可以根据需要来调用默认的实现。
定义impure virtual 函数,继承的是接口,但是提供了默认的行为。这样默认的行为可能会带来麻烦,因为用户可能并不知道会有这种默认的行为。如果想提供默认的行为,又需要用户明确的调用,可以采用pure virtual函数。
定义 non-virtual 函数,继承的是实现,而且是不希望被重新定义的实现。
在派生类中,我们需要注意以下几点:
1. Name hiding
对non-virtual函数,如果在派生类中提供了新的定义,父类中所有同名函数的定义都会被隐藏。
派生类和父类可以理解为不同的作用域,父类的作用域在外,子类的作用域在内,内层作用域中定义的符号将会隐藏外层作用域中定义的同名符号。
如果需要父类中的定义,可以使用using声明。
2. 对virtual函数的默认参数,采用的是静态联编
如果C++采用动态机制,需要比较大的开销,所以没有提供动态联编机制。
3. 对非virtual函数,不应该重新定义
重新定义会隐藏父类中的定义,导致父类和子类行为的不一致,这违背了is-a关系。