c++ 虚函数 虚基类

1、多态的实现需要利用虚函数的动态联编,即在运行时进行加载,进行动态调用~

#include <iostream>

using namespace std;

class Base{
    public:
        Base( int val ){ value = val; }
        void print(){ cout << "Base" << " " << value << endl; }  // virtual void print(){ cout << "Base" << " " << value << endl; } 
    protected:
        int value;    
};

class B : public Base{
    public:
        B( int val ) : Base( val ){}
        void print(){ cout << "B" << endl; }
};

int main(){
   
    Base *base;
    Base base1(0);
    base = &base1;
    base->print();

    B b(2);
    base = &b;
    base->print();

}

输出结果为 Base0 Base2;

分析: 因为print()函数前面没有virtual,普通成员函数采用的是静态联编(标识符和存储地址联系在一起),所以在编译链接生成可执行代码的过程已经把对象识别了,而非在运行的时候进行动态加载,所以输出的是Base2,而非B

修改:将Base类中的print()函数修改为virtual类型

 

2、析构函数必须申明为virtual类型

如果一个基类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明。
说明虚析构函数的目的在于在使用delete运算符删除一个对象时,能保析构函数被正确地执行。因为设置虚析构函数后,可以采用动态联编方式选择析构函数。

#include <iostream.h>
class A
{
public:
  virtual ~A() { cout<<"A::~A() Called.\n"; }
};
class B : public A
{
public:
  B(int i) { buf = new char[i]; }
  virtual ~B()
  {
  delete [] buf;
  cout<<"B::~B() Called.\n";
  }
private:
  char * buf;
};
void fun(A *a)
{
  delete a;
}
void main()
{
  A *a = new B(15);
  fun(a);
}
执行该程序输出如下结果:
  B::~B() Called.
  A::~A() Called.
如果类A中的析构函数不用虚函数,则输出结果如下:
  A::~A() Called.
当说明基类的析构函数是虚函数时,调用fun(a)函数,执行下述语句:
  delete a;
由于执行delete语句时自动调用析构函数,采用动态联编,调用它基类的析构函数,所以输出上述结果。
当不说明基类的析构函数为虚函数时,delete隐含着对析构函数的调用,故产生
A::~A() Called.
的结果。 此部分转载自:http://topic.csdn.net/u/20110302/23/089640d3-0eb4-4063-a664-55edb0643802.html

追述:virtual函数的实现细节

a、欲实现virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数会被调用,而这份信息通常是由一个所谓的vptr(virtual table pointer)指针指出。vptr指向一个由函数指针构成的数组,成为vtbl(virtual table),每一个带有virtual函数的class都有一个相应的vtbl。当对象调用某一virtual函数,实际被调用的函数取决于该对象的vptr所指向的那个vtbl——编译器在其中寻找适当的函数指针

b、如果一个point class内涵virtual函数,其对象体积会增加:32-bits计算机体系结构中将占用64bits(为了存放两个ints)至96bits(再加上vptr),64-bits的同理。因此,为Point添加一个vptr会增加其对象达50~100%,而且c++的Point对象不再和其他语言(如C)内的相同声明有着一样额结构(因为其他语言中的对应物并没有vptr),也因此不再具有可移植性

c、因此,武端的将所有的classes的析构函数声明为virtual,就像从未声明他们为virtual一样,都是错误的。只有当class内含有至少一个virtual函数,才为他声明virtual析构函数

总结:

a、带有多态特性的base类应该声明一个virtual西沟函数。如果class带有任何的virtual函数,它就应该拥有一个virtual析构函数

b、class的设计目的如果不是作为base calss使用的话,或不是为了具备多态性,就不该声明virtual析构函数

 

3、多继承和虚基类

    a、由于c++的多继承性,导致了在派生类继承基类的过程中,会出现某些基类成员访问的不唯一现象,也即二义性

    b、多继承的路径上,可能出现公共基类的,从而产生多个访问实例,为了避免产生这种现象,可以将公用的基类声明称virtual类型,从而之产生单个基类的实例

#include <iostream>

using namespace std;

class Base{
    public:
        Base( int val ){ value = val; }
        virtual void print(){ cout << "Base" << " " << value << endl; }
    protected:
        int value;    
};

class A : virtual public Base{   // 虚基类
    public:
        A( int val ) : Base( val ){}
        void print(){ cout << "A" << endl; }   
};

class B : public Base{
    public:
        B( int val ) : Base( val ){}
        void print(){ cout << "B" << endl; }
};

class C : public Base{
    public:
        C( int val ) : Base( val ){}
        void print(){ cout << "C" << endl; }
};

class D : virtual public Base, A{  // 多继承,Base虚基类避免多次重复继承
    public:
        D( int val ) : Base( val ), A( val ){}
        void print(){ cout << "D" << endl; }    
};

class E : public B, C{
    public:
        E( int val ) : B( val ), C( val ){}
        void print(){ cout << "E" << endl; }    
};

int main(){
   
    Base *base;
    Base base1(0);
    base = &base1;
    base->print();
    A a(1);
    base = &a;
    base->print();
    B b(2);
    base = &b;
    base->print();
    C c(3);
    base = & c;
    base->print();
    D d(4);
    base = &d;
    base->print();
    E e(4);
    e.print();

    system("pause");
    return 0;   
}

输出结果: Base0  A  B  C  D E

分析:上述如果在类D继承Base 和 A 的时候不把 Base声明成virtual类型,会报错

说明:1、构造函数调用的过程中,virtual类型的基类将被首先调用

            2、如果该基类也是派生类,也将遵循从基类开始构造的原则,然后是派生类

            3、同一层次的虚基类按照他们声明的次序来进行调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值