C/C++编程:确定你的public继承塑模出is-a关系

1059 篇文章 286 订阅

规则

以C++进行面向对象编程,最重要的一个规则是:public inheritance(公开继承)意味着"is-a"的关系

如果你令class D(“Derived”)以public形式继承class B(“Base”),就意味着,每一个类型为D的对象同时也是一个类型为B的对象,反之则不成立。也意味着”B对象可以派上用场的任何地方,D对象一样可以派上用场“,因为每一个D对象都是一个B对象,反之不成立。

C++对于”public继承“严格指向上面见解,比如:

class Person{
};
class Student : public Person {};

void eat(const Person &p){};   // 任何人都会吃
void study(const Student &s){}; // 只有学生会学习

int main(){
    Person p;
    Student s;

    eat(p);  // 人会吃
    eat(s); // 学生也会吃
   
    study(p); // error!!!不是所有的人都会学习
    study(s); // 只有学生会学习
}

每个学生都是人,但不是每一个人都是学生。(一般)人能成立的事情[比如生日],对学生也成立。对学生成立的事情[比如学校]对人不一定成立。

于是,在C++中,任何函数如果期望获得一个类型为Person(或者pointer-to-Person或者reference-to-Person)的实参,也接受一个Student对象(或者pointer-to-Student或者reference-to-Student))

示例一

有时候你的直觉可能会误导public继承与is-a之间的等价关系这个概念。举个例子,企鹅是一种鸟,这是事情。鸟可以飞,这也是事实。从而,你可以会这样描述:

class Bird{
public:
	virtual void fly();
};

class Pengin : public Bird{  //企鹅是一种鸟

}

但是实际上企鹅不会飞。这是怎么回事。事实上,不是所有的鸟都会飞,真正的应该这样写:

class Bird{                   // 没有声明fly
};

class FlyingBird : public {  // 会飞的鸟
public:
	virtual void fly();
};


class Pengin : public Bird{  // 没有声明fly
}

但这个方法不一定使用于所有,比如对于某些软件系统来说,不需要区分会飞的鸟和不会飞的鸟,从而第一个实现版本更好些。也就是说,世界上不存在一个”适用于所有软件系统“的设计,应该应地制宜。

另一种处理”所有的鸟都会飞,企鹅是鸟,但是企鹅不会飞“的方法是为企鹅重新定义fly函数,令它产生一个运行期错误:

class Pengin : public Bird{
public:
	virtual void fly() { error("Attempt to make a pengin fly!")};
}

还有一种方法是不为Pengin 定义fly函数【推荐,因为好的接口可以防止无效的代码通过编译】:

class Bird{                   // 没有声明fly
};

class Pengin : public Bird{  // 没有声明fly
}

示例二

Square类应该以public形式继承Rectangle吗?
在这里插入图片描述

class Rectangle{
public:
	virtual void setHeight(int newHeight);
	virtual void setWidth(int newWidth);
	virtual int height() const;
	virtual int width() const;
}

void makeBigger(Rectangle &r){
	int oldHeight = r.height();
	r.setWidth(r.width() + 10);
	assert(r.height() == oldHeight );
}

显然,assert永远为真。因为makeBigger只改变r的宽度不改变高度

class Square : public Rectangle{};
Square s;
assert(s.width() == s.height());  // 这对所有的正方形一定为真
makeBigger(s);  //所有继承,s是一种矩形,所以可以增加面积
assert(s.width() == s.height());  // 对所有的正方形应该仍然为真

但是,显然我们遇到了问题:

  • 调用makeBigger()之前,s的高度和宽度相同
  • makeBigger()函数内,s的宽度改变,高度不变
  • 调用makeBigger()之后,s的高度和宽度应该还是相同(注意这里是传引用,所以改变的是原本,不是s的副本)

也就是说,某些可以施行于矩形身上的事情却不可以施行于正方形上。但是public继承主张,能够施行于基类对象的每件事情,也可以施行于派生类对象上。所以,用public继承塑模正方形和矩形的关系不正确,编译器通过并不代表程序正确。

总结

  • "public继承"意味着is-a。适用于基类身上的每一件事情也一定适用于派生类身上,因为每一个派生类对象也都是一个基类对象

  • is-a并不是唯一存在于类之间的关系,另两个常见的关系是has-a(有一个)和is-implemented-in-tems-of(根据某物实现出)。将上面这些重要关系的任何一个塑模为is-a都是错误的,所以你应该确定你确实了解这些类相互关系之间的差异,并指导如何在C++中最好的塑模它们

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值