基类指针指向派生类对象

成员函数一般化三个结论
#include <iostream>
using std::cout;
using std::endl;

class Employee
{
public:
        void Work() { cout << "Employee work" << endl; }
};

class Dev : public Employee
{
public:
        void Work() { cout << "Development work" << endl; }
};

int main()
{
        Employee *pEmployee = NULL;
        Dev *pDev = NULL;
        Dev dev;
        pEmployee = &dev;
        pEmployee->Work();
        pDev = &dev;
        pDev->Work();

        return 0;
}

1.    如果以“基类指针”指向“派生类对象”,那么经由该指针只能调用基类所定义的函数
2.    如果以“派生类指针”指向“基类对象”,必须先做明显的转换操作。这种做法很危险,
        不符合真实生活经验,在程序设计上也会带给程序员困惑
3.    如果基类和派生类都定义了“相同名称的成员函数”,那么经由对象指针调用成员函数时,
        到底调用哪一个函数,必须视该指针的原始类型而定,而不是视指针所指的对象的类型而定


基类的指针指向派生类的对象

#include <iostream>
using namespace std;

class A
{
public:
        A() {   cout << "A" << endl; }
        virtual ~A() {  cout << "~A" << endl; }
};

class B: public A
{
public:
        B() { cout << "B" << endl; }
        ~B() {cout << "~B" << endl; }
};


int main()
{

        B *pDerive = new B;
        A *pBase = pDerive;
        cout << pDerive << "   " << pBase << endl;
        delete pBase;
        pBase = NULL;

        return 0;
}

output:
A
B
0xc1e010   0xc1e010
~B
~A


由于类A的析构函数是虚函数,所以其对象的存储空间中包含了虚表;
类B继承了类A,所以类B的析构函数也变成了虚析构函数,类B对象的
存储空间中也包含了虚表;根据多态的原则,用基类的指针删除派生类对象,
实际上是通过虚指针(vptr)找到虚表(vtable),然后在找到虚函数的地址。

 

成员函数的虚拟化

虚函数是为了对“如果以基类指针指向派生类对象,那么通过该指针就只能调用基类所定义的成员函数”
这条规则反其道而行的设计。多态就是以单一指令调用不同函数。
为了达到动态绑定的目的,c++编译器通过某个表格,在执行期“间接”调用实际上欲绑定的函数。
这样的表格称为虚函数表(vtable)。每一个“内含虚函数的类”,编译器都会为它做出一个虚函数表,
表中的每一个元素都指向一个虚函数的地址。此外,编译器当然也会为类加上一项成员变量,
是一个指向该虚函数表的指针(vptr)。





Object slicing 与虚函数

#include <iostream>
using std::cout;
using std::endl;

class Junior
{
public:
        virtual void level() { cout << "level B" << endl; }
};

class Senior : public Junior
{
public:
        void show()
        {
                cout << "show senior level" << endl;
                level();
        }
        virtual void level() { cout << "level C" << endl; }
};

class Princeple : public Senior
{
public:
        void show()
        {
                cout << "show princeple level" << endl;
                level();
        }
        virtual void level() { cout << "level D" << endl; }
};

int main()
{
        Princeple prin;
        cout << "test 1" << endl;
        prin.show();
        cout << "test 2" << endl;
        ((Senior *)(&prin))->show();
        cout << "test 3" << endl;
        Princeple *pPrin = new Princeple;
        pPrin->show();
        cout << "test 4" << endl;
        ((Senior)prin).show();
        delete pPrin;
        pPrin = NULL;

        return 0;
}



对于test 4的输出结果
由于向上强制转型, (Senior)prin, 将会造成对象的内容被切割(object slicing),当调用
((Senior)prin).show(); prin已经是一个被切割得只剩下半条命的对象,
由于((Senior)prin).show()是传值而非传址操作,编译器以所谓的拷贝构造函数把Senior对象的内容复制了一份,
使得prin的vtable内容与Senior对象的vtable相同。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值