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()始终执行的是子类的虚函数;

  • 相关阅读:
    一文让你明白Redis持久化
    spring-data-redis 2.0 的使用
    中间自适应布局的5种解法
    php实现只需要一个QQ号就可以获得用户信息
    基于LINUX下的进程管理问题
    【初码干货】记一次分布式B站爬虫任务系统的完整设计和实施
    初码-爬虫系列-文章目录
    初码-阿里云系列-文章目录
    初码-Azure系列-存储队列的使用与一个Azure小工具(蓝天助手)
    初码-Azure系列-记一次MySQL数据库向Azure的迁移
  • 原文地址:https://www.cnblogs.com/klcf0220/p/6889122.html
Copyright © 2011-2022 走看看