zoukankan      html  css  js  c++  java
  • C++继承中的多态、虚函数、虚析构函数

    多态

    C++继承中的兼容原则中我们知道:父类指针可以直接指向子类对象,父类引用可以直接引用子类对象。当父类和子类有相同方法时调用的是父类方法,即是根据指针类型或引用类型来确定调用的方法类型的。如果我们想根据指针实际指向的对象类型(引用实际引用的类型)来决定调用的方法类型,需要把这个函数声明为虚函数,这就是多态。

    #include <iostream>
    using namespace std;
    
    class A
    {
    public:
        void print()
        {
            cout<<"I am A"<<endl;
        }
    
        virtual void printv()
        {
            cout<<"I am vA"<<endl;
        }
    };
    
    class B:public A
    {
    public:
        void print()
        {
            cout<<"I am B"<<endl;
        }
    
        virtual void printv()
        {
            cout<<"I am vB"<<endl;
        }
    };
    
    void test(A *pA)
    {
        pA->print();
        pA->printv();
    }
    
    int main()
    {
        B b;
        test(&b);
    
        return 0;
    }
    View Code

    执行结果:

    I am A
    I am vB

    多态成立的条件 :

    1 要有继承
    2 要有虚函数重写
    3 要有父类指针(父类引用)指向子类对象

    虚析构函数

    构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数。

    析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象

    #include <iostream>
    using namespace std;
    
    class A
    {
    public:
        A()
        {
            p = new char[20];
            strcpy(p, "objA");
            cout<<"A()"<<endl;
        }
        virtual ~A()
        {
            delete [] p;
            cout<<"~A()"<<endl;
        }
    private:
        char *p;
    };
    
    class B:public A
    {
    public:
        B()
        {
            p = new char[20];
            strcpy(p, "objB");
            cout<<"B()"<<endl;
        }
        ~B()
        {
            delete [] p;
            cout<<"~B()"<<endl;
        }
    private:
        char *p;
    };
    
    class C:public B
    {
    public:
        C()
        {
            p = new char[20];
            strcpy(p, "objC");
            cout<<"C()"<<endl;
        }
        ~C()
        {
            delete [] p;
            cout<<"~C()"<<endl;
        }
    private:
        char *p;
    };
    
    void test(A *pA)
    {
        delete pA;
    }
    
    int main()
    {
        C *pC = new C;
    
        //deletc pC;    直接通过子类指针释放对象,析构函数不需要声明为虚函数
    
        test(pC);    //通过父类指针释放对象,析构函数必须声明为虚函数,要不然会只释放父类的成员p
    
        return 0;
    }
    View Code

    执行结果:

    A()
    B()
    C()
    ~C()
    ~B()
    ~A()
    View Code

    多态的实现原理

    当类中声明虚函数时,编译器会在类中生成一个虚函数表
    虚函数表是一个存储类成员函数指针的数据结构;
    虚函数表是由编译器自动生成与维护的;
    virtual成员函数会被编译器放入虚函数表中;
    存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。

     

    当调用函数func时,编译器首先确定func是不是虚函数:

    (1)func不是虚函数:编译器可以直接确定被调用的函数(静态链编,根据指针或引用类型确定)

    (2)func是虚函数:编译器根据实际指向的对象的vptr指针指向的虚函数表查找func函数并调用(动态链编,运行时完成)

    说明:
    1. 通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需
    要通过寻址操作才能确定真正应该调用的函数。 而普通成员函数是在编译时就确定了调用的函数。在效率上, 虚函数的效率要低很多。

    2.出于效率考虑,没有必要将所有成员函数都声明为虚函数.

  • 相关阅读:
    JavaScript日常学习6
    JavaScript日常学习5
    JavaScript日常学习4
    JavaScript日常学习3
    JavaScript日常学习2
    JavaScript日常学习1
    iOS开发之 Lottie -- 炫酷的动效
    Adobe After Effect CC2017 for Mac
    随机四则运算的程序
    关于四则运算进一步优化的构思
  • 原文地址:https://www.cnblogs.com/xumaomao/p/12932285.html
Copyright © 2011-2022 走看看