C++面向对象:为什么抽象基类不能创建对象?
同学们,前面我们学习了:
什么是类
什么是继承
什么是虚函数
什么是多态
今天我们学习一个很重要的概念:
为什么抽象基类不能创建对象?
这个问题在 GESP、CSP-J/S 中非常常见。
一、先回忆:什么是基类?
假设我们要设计一个动物系统。
有:
狗
猫
鸟
它们都是动物。
我们可以写:
class Animal { public: void eat() { cout << "吃东西" << endl; } };然后:
class Dog : public Animal { }; class Cat : public Animal { };这里:
Animal | -------------- | | Dog CatAnimal 是:
父类(基类)
Dog、Cat 是:
子类(派生类)
二、普通基类可以创建对象
例如:
Animal a;完全没有问题。
因为 Animal 自己是完整的。
它有:
吃东西()这个功能。
所以:
Animal对象 { eat() }可以存在。
但是问题来了:
如果 Animal 只是一个概念呢?
比如:
动物会叫。
但是:
狗怎么叫?
汪汪猫怎么叫?
喵喵鸟怎么叫?
叽叽那么:
Animal 的叫声是什么?
不知道。
三、什么是抽象基类?
我们看:
class Animal { public: virtual void speak() = 0; };这里:
virtual void speak() = 0;叫:
纯虚函数(pure virtual function)
意思:
“我只规定规则,但是不写具体内容。”
例如:
动物必须会叫但是:
怎么叫?交给孩子。
然后:
狗:
class Dog : public Animal { public: void speak() { cout<<"汪汪"<<endl; } };猫:
class Cat : public Animal { public: void speak() { cout<<"喵喵"<<endl; } };结构:
Animal (会叫,但是不知道怎么叫) | ---------------- | | Dog Cat (汪汪) (喵喵)四、为什么 Animal 不能创建对象?
如果写:
Animal a;C++ 会报错。
为什么?
因为:
Animal 里面有:
virtual void speak() = 0;它只是告诉大家:
必须有叫的方法但是它自己没有:
叫的方法所以:
Animal对象 { ? }不完整。
打个比方:
动物学校
校长说:
所有学生必须会唱歌。
但是校长没有规定:
唱什么歌。
现在来了一个学生:
Animal学生老师问:
请唱歌。
学生:
我不知道唱什么。
所以:
这个学生不能报名。
五、抽象类为什么存在?
有人问:
既然不能创建对象,
那为什么还要写它?
这是重点!
抽象类的作用:
为子类制定统一规则。
例如:
游戏里面:
角色有:
战士
法师
弓箭手
设计:
class Role { public: virtual void attack()=0; };意思:
所有角色必须攻击。
战士:
class Warrior:public Role { public: void attack() { cout<<"挥剑攻击"; } };法师:
class Mage:public Role { public: void attack() { cout<<"释放魔法"; } };以后游戏写:
Role *p;可以:
p=new Warrior(); p->attack();也可以:
p=new Mage(); p->attack();这就是:
多态
六、抽象类和普通类的区别
| 普通类 | 抽象类 | |
|---|---|---|
| 有没有纯虚函数 | 没有 | 有 |
| 能否创建对象 | 可以 | 不可以 |
| 作用 | 描述具体东西 | 规定规则 |
| 是否可以继承 | 可以 | 主要给子类继承 |
七、判断一个类是不是抽象类
非常简单:
看有没有:
=0例如:
不是抽象类
class A { public: virtual void fun() { } };可以:
A a;是抽象类
class A { public: virtual void fun()=0; };不能:
A a;八、抽象类可以有普通函数吗?
当然可以。
例如:
class Animal { public: virtual void speak()=0; void eat() { cout<<"吃东西"; } };这里:
Animal | |-- speak() | (没有实现) | |-- eat() (已经实现)子类继承:
class Dog:public Animal { public: void speak() { cout<<"汪汪"; } };然后:
Dog d; d.eat(); d.speak();输出:
吃东西 汪汪九、抽象类可以有构造函数吗?
可以。
例如:
class Animal { public: Animal() { cout<<"动物出生"; } virtual void speak()=0; };虽然:
Animal a;不允许。
但是:
创建:
Dog d;时:
执行顺序:
Animal构造函数 ↓ Dog构造函数因为:
先出生父亲,
再出生孩子。
十、抽象类最重要的应用:接口思想
很多时候,我们不是为了创建对象。
而是为了:
规定所有子类必须完成什么功能。
例如:
支付系统:
Payment | -------------------- | | 支付宝 微信抽象类:
class Payment { public: virtual void pay()=0; };支付宝:
class AliPay:public Payment { void pay() { cout<<"支付宝支付"; } };微信:
class WeChatPay:public Payment { void pay() { cout<<"微信支付"; } };以后:
Payment *p;不用管具体是谁。
这就是:
统一接口 + 多态十、一句话总结(考试重点)
抽象基类因为包含至少一个纯虚函数,表示它是不完整的“规则模板”,没有具体实现,所以不能创建对象;它的作用是让派生类继承并实现这些纯虚函数,从而实现多态。
记忆口诀:
有纯虚函数 ↓ 抽象类 ↓ 不能创建对象 ↓ 只能被继承 ↓ 实现多态对于 GESP/CSP-J ,同学们可以把它和前面学过的知识串起来:
继承 ↓ 虚函数 ↓ 纯虚函数 ↓ 抽象基类 ↓ 多态这条链是 C++ 面向对象最核心的一条线。