—
is_a原则
概念
举一个例子,有一个Horse类可以保存关于马的所有信息,身高体重等等,那么我们就可以从Horse类中派生出白马类,白马类包含所有Horse类的成员,在白马类中可以新增关于白马的成员,这个成员通常不用于Horse类。
class Horse
{
public:
int Tall;
int Weight;
};
class WhiteHorse:public Horse
{
public:
int Color;
};
上面的代码中,WhiteHorse多了颜色这个成员。
整体来看,is-a表示了一种是的关系。比如白马是马,香蕉是水果,老师是人这种关系。
并且public的继承方式建立了is_a
的关系。
使用实例
派生类Soldier继承自基类Person
//Person.h
class Person
{
public:
Person(string name = "Jim");
~Person();
void play();
protected:
string m_strName;
};
//Soldier.h
class Soldier : public Person
{
public:
Soldier(string name = "James", int age = 20);
~Soldier();
void work();
protected:
int m_iAge;
};
- 派生类可以给基类赋值:
//main.cpp
int main(){
Soldier s1;
Person p1 = s1;
return 0;
}
因为一个士兵也是一个人。
+ 基类指针可以指向派生类(派生类可以取地址给基类):
//main.cpp
int main(){
Soldier s1;
Person *p2 = &s1;
return 0;
}
同理,一个士兵也是一个人,父类的指针也可以指向子类对象。
+ 把基类的指针或者是基类的对象或者是基类的引用作为函数的参数来使它可以接收所传入的子类的对象,并且也可以传入基类的对象
“`
void func1(Person *p){
……
}
void func2(Person &p){
……
}
//main.cpp
int main(){
Person p1;
Soldier s1;
func1(&p1); func2(p1);
func1(&s1); func2(s1);
return 0;
}
+ 基类只能接收和访问派生类中自己有的数据成员和成员函数
//#include “Person.h”
# include “Soldier.h”
int main() {
Soldier soldier;
Person person;
person = soldier;
person.play();
Person *p = &soldier;
p->play(); // 使用基类声明的对象只能调用基类的方法
//p->work(); // 使用基类声明的对象不能调用派生类的方法
return 0;
}
如果需要调用子类的成员函数,那么就是多态的内容了,后面再说。
这是在编译期就决定了的。
+ 通过基类的指针指向派生类的对象
//#include “Person.h”
#include “Soldier.h”
int main() {
Person *pp = new Soldier; // 基类的指针去指向派生类的内存空间
pp->play();
delete pp; // 销毁基类的对象,执行的是基类的析构函数,派生类的内存并没有释放
pp = NULL;
return 0;
}
LOG:
Person::play()
James
Person::~Person()
“`
has_a原则
概念
has-a体现了有这个思想。
比如,午餐有香蕉。但是午餐不是香蕉。
其实私有跟保护继承体现了has-a原则是因为,私有跟保护继承是实现继承。
什么是实现继承呢?
实现继承的主要目标是代码重用,我们发现类B和类C存在同样的代码,因此我们设计了一个类 A,用于存放通用的代码,基于这种思路的继承称为实现继承。
我们可以说,午餐中存在香蕉。
而共有继承则不是,为什么?
在需要基类对象的任何地方都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。
那么就可得到,当为私有或保护继承的时候,是包含的关系,基类在派生类中是私有的。需要基类的时候是不能用派生类代替的。
两种情况
- 包含
class Banana{..};
class Lauch
{
private:
class Banana;
......
};
私有继承
c++还有另一种实现has-a关系的途径—-私有继承。使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。
使用公有继承,基类的公有方法将成为派生类的公有方法。简而言之,派生类将继承基类的接口,这是is-a关系的一部分。使用私有继承,基类的公有方法将成为派生类的私有方法。简而言之,派生类不能继承基类的接口。正如从被包含对象中看到的,这种不完全继承是has-a关系的一部分。
因此私有继承提供的特性与包含相同:获得实现,但不获得接口。所以,私有继承也可以用来实现has-a关系。
使用包含还是私有继承
大多数c++程序员倾向于前者。不过私有继承所提供的特性确实比包含多。例如,假设类包含保护成员,则这样的成员在派生类中是可用的,但在继承层次机构外是不可用的。如果使用组合奖这样的类保护在另一类中,则后者将不是排成类,而是位于继承层次结构之外,因此不能访问保护成员。但通过继承的到的将是派生类,因此他能够访问保护成员。
另一种需要使用私有继承的情况是需要重新定义虚函数。派生类可以重新定义虚函数,但包含类不能。使用私有继承,重新定义的函数将只能在类中使用,而不是公有的。