面向对象的三大基本特征:封装、继承和多态。类对象通过public/private/protected关键字实现对象的封装,封装后通过继承实现多样性,而这个多样性又需要通过多态来完成。

假设要实现一个攻击的功能,不同的角色战斗力的都不同,在以往的c中,要完成这个功能需要对每个不同的角色都添加一个攻击函数:

void attack_normal(obj n) { cout << "我砍了你一刀,你流了一滴血!" << endl;}
void attack_vip(obj v) { cout << "我是VIP,我的刀是屠龙宝刀,你流了十滴血!" << endl;} 
void attack_rmb(obj r) { cout << "我是RMB玩家,你已经死了!" << endl; }

而在有多多态后,所有的函数都可以合并为一个:

void attack(obj *o) {
    // 根据对象o的实际类型,攻击敌方。
}

一、继承中的兼容性原则

赋值兼容性原则的说的是把基类指向之类,以此来完成相应的功能。例如可以把父类的指针或者引用指向子类:

#include<iostream>
using namespace std;

class parent {
public:
    void print(){
        cout << "I''m parent!" << endl;
    }
};

class child: public parent {
public:
    void print(){
        cout << "I''m child!" << endl;
    }
};

int main(){

    parent p1, *p = &p1;
    child c1;

    p->print();
    c1.print();

    p = &c1; // 把父类的指针指向子类
    p->print();  // 此时会运行父类的print

    cout << "sizeof(int*):" << sizeof(int*) << endl;
    cout << "sizeof(parent):" << sizeof(parent) << endl;
    cout << "sizeof(child):" << sizeof(child) << endl;
    
    return 0;
}

64位操作系统中,程序输出:

I''m parent!
I''m child!
I''m parent!  // 运行了父类的print()
sizeof(int*):8  // 用作判断编译器环境类型为x64
sizeof(parent):1
sizeof(child):1

二、virtual关键字

上面的例子中用到了继承,并把基类指向子类。但看起来并没有什么用处,因为把父类指针指向子类后还是调用的父类的函数。并且事实上我们更希望它调用子类的函数,如果要达到这个目的就需要用到多态了。

实际上,继承往往是和多态相辅相成的,没有多态也不能体现出继承的多样性,多态会更灵活的展现继承。

使用多态的关键就是加入关键字virtualvirtual关键字可以使得函数在调用时可以根据实际的类型来决定使用哪一个函数。

例如上面的例子中,在父类的print()函数前面加一个virtual,再把父类指针指向子类的时候就会输出子类的print()

class parent {
public:
    virtual void print(){
        cout << "I''m parent!" << endl;
    }
};

子类中的print()可以不加virtual,因为如果没加virtual的话编译器默认会添加。

运行后输出:

I''m parent!
I''m child!
I''m child!  // 此时打印的就会是子类的print
sizeof(int*):8
sizeof(parent):8
sizeof(child):8

一个有趣的现象就是在添加了关键字后parentchild的大小都变成了8,目前可以先了解有这个现象。至于为什么会这样,下次再讨论。

最后修改:2018 年 04 月 08 日
如果觉得我的文章对你有用,请随意赞赏