zoukankan      html  css  js  c++  java
  • 虚继承与虚函数继承

    1.什么是虚函数
    简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现一共同的方法,但因个体差异而采用不同的策略。虚函数主要通过虚函数表(V-Table)来实现。

    2.什么是纯虚函数
    纯虚函数相当于基类只提供接口而不定义具体实现,在函数声明后加=0,如:
    virtual void Eat() = 0;

    3.纯虚函数和虚函数的区别
    虚函数在派生类里面也可以覆盖,也可以不覆盖的,直接使用基类的实现;但纯虚函数必须在派生类中实现,因为它只提供了一个接口。

    4.1成员函数被重载的特征
    (1)相同的范围(在同一个类中);
    (2)函数名字相同;
    (3)参数不同;
    (4)virtual 关键字可有可无。
    4.2“覆盖”是指派生类函数覆盖基类函数,特征是:
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual 关键字。
    4.3“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,特征是:
    (1)如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
    (2)如果派生类的函数与基类的函数同名,但是参数相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
    static成员函数不能覆盖和隐藏

    5.虚继承和虚基类
    虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数,这时会产生二义性。为了解决二义性,同时为了节约内存,B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类,这样D中就只有一份A中的变量和函数。

    虚继承主要用于菱形 形式的继承形式。

    虚继承是为了在多继承的时候避免引发歧义,
    比如类A有个就是a,B继承了A,C也继承了A,当D多继承B,C时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝。
    虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联

    虚函数继承:
    class A
    {
        virtual void fun() {cout < <'A' < <endl;};
    };
    class B : public A
    {
        virtual void fun() {cout < <'B' < <endl;};
    };
    int main(int argv, char** argc)
    {
        A* p = new B;
        p->fun(); //结果输出B,而不是A,至于实现原理,其实是对象头部多了四个字节,它是一个指向虚函数表的地址指针,程序运行时通过这个表,找到了这个B::fun()的入口地址
        return 0;
    }

    通俗的讲,虚继承就是为了节约内存的,他是多重继承中的特有的概念。适用与菱形继承形式。
    如:类B、C都继承类A,D继承类B和C。为了节省内存空间,可以将B、C对A的继承定义为虚拟继承,此时A就成了虚拟基类。
    class A;
    class B:vitual public A;
    class C:vitual public A;
    class D:public B,public C;
    虚函数继承就是覆盖。即基类中的虚函数被派生类中的同名函数所覆盖。
    class parent
    {
      public:
      vitual void foo(){cout < <"foo from parent";};
      void foo1(){cout < <"foo1 from parent";};
    };
    class son:public parent
    {
      void foo(){cout < <"foo from son";};
      void foo1(){cout < <"foo1 from son";};
    };
    int main()
    {
        parent *p=new son();
        p->foo();
        p->foo1();
        return 0;
    }
    其输出结果是:
    foo from son,foo1 from parent

    1、真正意义上的虚函数调用,是运行时绑定的;
    2、什么是真正意义上的虚函数调用?通过指针或者引用执行虚函数;
    3、通过对象执行虚函数会不会是动态绑定的?不会。
    4、一个类是否有虚函数,就看它是否包含一个指向虚函数表的指针;
    5、如果类本身含有virtual 声明的函数,或者继承了virtual 函数,那么它肯定会包含一个指向虚函数表的指针;
    6、从纯抽象类或者非抽象类继承了virutal,意义上是一样的,效率上是一样的,并不因为你是纯抽象类的继承而效率变高;
    7、虚函数调用比普通函数调用慢多少?假设这个函数仅执行 return i  > j,大概慢 15%左右(3000万 * 100次规模测试),如果是个真正有意义上的函数,效率影响可以忽略不计;
    8、因此说虚函数慢的基本上是放屁,担心虚函数影响效率的基本上是杞人忧天;
    9、虚函数会慢,但是那是对内联函数而言的,虚函数会忽略 inline前缀,请注意这一点;
    10、继承层次不影响虚函数效率,如果你这个类是原始类的第10层继承,那么虚函数调用效率和第1层继承的类没有差别,当然如果你要在该函数中调用上一层的虚函数那就另当别论了;
    11、每个类应该只有一个virtual table,而不是每个对象有一个(对象只含有指向虚表的指针),那些说虚函数增大空间开销的可以自宫了;
    12、如果一个类含有虚函数,在构造时,使用memset(this, 0, sizeof(*this))是找死的行为;
    13、虚函数是运行时多态,模板是编译时多态,一个动,一个是静。

    14、子类覆盖父类的虚函数的时候,实际上是在构造函数中修改了虚表中的函数指针;因此使得 FatherClass* p = new ChildClass();的情况下,p->VirtualFunc()始终执行的是子类的虚函数;

  • 相关阅读:
    UVa 1451 Average (斜率优化)
    POJ 1160 Post Office (四边形不等式优化DP)
    HDU 3507 Print Article (斜率DP)
    LightOJ 1427 Substring Frequency (II) (AC自动机)
    UVa 10245 The Closest Pair Problem (分治)
    POJ 1741 Tree (树分治)
    HDU 3487 Play with Chain (Splay)
    POJ 2828 Buy Tickets (线段树)
    HDU 3723 Delta Wave (高精度+calelan数)
    UVa 1625 Color Length (DP)
  • 原文地址:https://www.cnblogs.com/klcf0220/p/6889122.html
Copyright © 2011-2022 走看看