4.2 Virtual Member Functions(虚拟成员函数)

本文详细探讨了C++中的虚拟成员函数,包括两个基本概念、单一继承、虚拟继承和多重继承下的虚拟函数。虚拟函数实现了多态性,每个类有一个虚拟表,对象包含指向虚拟表的指针。虚拟析构函数在slot 1,其他虚函数按顺序分配。在虚继承和多重继承的情况下,内存布局和虚表处理变得复杂,建议避免在虚拟基类中声明非静态数据成员。
摘要由CSDN通过智能技术生成

一、两个基本概念

我们已经讨论过virtual function的一般实现模型:每个class 有一个virtual table,内含该class中有作用的virtual function的地址,然后每一个object有一个vptr,指向virtual table的所在。

在C++中,多态表示“以一个public base class的指针(或引用),寻址出一个derived class object“。如:

// Point2d : Point
Point* ptr_point = new Point2d();
Point &ref_point = new Point2d();

二、单一继承下的Virtual Functions

这里的单一继承指的是一般的单一继承,不是虚拟继承。在我们的Point class继承体系中:

// Point
class Point
{

public:
    virtual ~Point();
    virtual Point& mult(float) = 0;
    // ... 其他操作

    float x() const { return _x; }

    virtual float y() const { return 0; }
    virtual float z() const { return 0; }
    // ...

protected:
    Point(float x = 0.0);
    float _x;
};

// Point2d : Point
class Point2d : public Point
{
public:
    Point2d(float x = 0.0, float y = 0.0)
        :Point(x), _y(y) {}

    ~Point2d();

    // 重写base class virtual functions
    Point2d& mult(float);
    float y() const { return _y; }
    // ... 其他操作

protected:
    float _y;
};

// Point3d : Point2d
class Point3d : public Point2d
{
public:
    Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
        :Point2d(x, y), _z(z) {}

    ~Point3d();

    // 重写base class virtual functions
    Point3d& mult(float);
    float z() const { return _z; }
    // ... 其他操作

protected:
    float _z;
};

Point、Point2d和Point3d的内存布局和其virtual tables如下图所示:
内存布局和虚表

virtual destructor被指派slot 1,而mult()被指派slot 2,此例并没有mult()的函数定义(因为它是一个纯虚函数),所以pure_virtual_called()的函数地址会被放在slot 2中。如果该函数被意外的调用,通常的操作是结束掉这个程序。y()被只拍到slot 3,而z()被指派到slot 4。x()的slot是多少?并没有,因为它非virtual function。这里,内存布局只有数据成员和虚函数的虚表指针,并没有其他成员函数。如果该类中有static member,那么也不会出现在该内存布局中,因为static member存在于data segment,不在class object中。

关于成员函数位置的讨论

成员函数可以被看作是类作用域的全局函数,不在对象分配的空间里,只有虚函数才会在类对象里有一个指针,存放虚函数的地址等相关信息。成员函数的地址,编译期就已确定,并静态绑定或动态的绑定在对应的对象上。对象调用成员函数时,编译器可以确定这些函数的地址,并通过传入this指针和其他参数,完成函数的调用,所以类中就没有必要存储成员函数的信息。

这里写图片描述


三、虚拟继承下的Virtual Functions

考虑下面的virtual base class派生体系,从Point2d派生出Point3d:

class Point2d
{
public:
    Point2d(float x = 0.0, float y = 0.0);
    virtual ~Point2d();

    virtual void mumble();
    virtual float z();
    // ...

protected:
    float _x, _y;
};

// Point3d : public virtual Point2d
class Point3d : public virtual Point2d
{
public:
    Point3d(float x = 0.0, float y = 0.0, float z = 0.0);
    ~Point3d();

    float z();
protected:
    float _z;
};

虚继承下的内存布局如下图:

这里写图片描述

到这里我想上图已经看得有点神经错乱了,Lippman在P169页这样写道:

当一个 virtual base class从另一个 virtual base class 派生而来,并且两者都支持 virtual functions 和nonstatic data members 时,编译器对于 virtual base cIass 的 支持简直就像进了迷宫一样。虽然我手上有一整柜带有答案的例程,并且有一个以上的算法可以决定适当的 offsets以及各种调整,但这些素材实在太过诡异迷离,不适合在此处讨论!我的建议是,不要在一个 virtual base class中声明 nonstatic data members。 如果这么做,你会距离复杂的深渊愈来愈近,终不可拔。


四、多重继承下的Virtual Functions

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值