面试小卡片--虚函数/纯虚函数

虚函数

1、定义:基类可以有定义,子类可以没有定义
2、示例:EX: virtual void run();
3、目的:

纯虚函数

  • 定义:用于定义接口,只进行函数声明而不能定义,需要子类重写接口方法
  • 示例:EX: virtual void run() = 0;
  • 目的:实现“运行时多态”,同一操作作用于不同对象产生不同结果
    在运行时,通过指向基类的指针,创建子类对象,从而调用子类方法

以上,可以联想到java中抽象类于接口的区别:

  • 抽象类:可以有非抽象方法
  • 接口:实现接口需要重写所有接口方法

接下来,可以看看代码实际走走,
下面两个类分别是接口声明和抽象类定义。

class MediaBaseInterface {
  public:
    //纯虚函数在基类中无定义
    virtual void foo() = 0;

    virtual void setName(string name) = 0;
    virtual string  getName() = 0;
};

//这是一个抽象类-抽象类中写虚析构函数,不能创建该类实例
class DummyMediaBaseAbstract {
    public:
        DummyMediaBaseAbstract() { cout << "===>Media: Constructot Called!\n"; }
        //父类析构是否为虚
        virtual ~DummyMediaBaseAbstract() { cout << "===>Media Destructor Called!\n"; }
        //虚函数可以在基类中有定义
        virtual void foo()  { cout << "Media: virtual foo\n"; }
        virtual void setName(string name) { m_name = name; }
        virtual string getName() { return m_name; }

        //纯虚函数在基类中无定义
        virtual void bar()  = 0; // { cout << "virtual bar\n"; }

    private:
         string m_name;
};

接下来创建子类实现父类方法

//book class
class DummyBookDerived : public DummyMediaBaseAbstract {
    public:
        DummyBookDerived()  { cout << "===>Book: Constructor Called!\n"; }
        ~DummyBookDerived() { cout << "===>Book: Destructor Called!\n"; }

        void foo() { cout << "Book: foo\n"; }
        void bar() override { cout << "Book: implement pure  virtual bar function\n"; }
};

//video class
class DummyVideoDerived : public DummyMediaBaseAbstract {
    public:
        ~DummyVideoDerived() { cout << "===>Video Destructor Called!\n"; }

        //可以不重载虚函数,必须重载纯虚函数
        void bar() override  { cout << "Video: implement pure  virtual bar function\n"; }
};

//class music
class DummyMusicDerived : public DummyMediaBaseAbstract {
    public:
        DummyMusicDerived()  {cout << "===>Music: Constructor Called!\n"; }
        ~DummyMusicDerived() { cout << "===>Music Destructor Called!\n"; }

        void foo() { cout << "Music: foo\n"; } 
        //必须重写纯虚函数
        void bar() override  { cout << "Music: implement pure  virtual bar function\n"; }

        void setName(string name) {  m_music = name; }
        string getName() { return m_music; }

    private:
        string m_music;
};

最后,在主函数中定义父类指针,指向子类对象,看看会发生什么奇妙的事
前面代码中有一个疑问,不知你有没有注意到

int main() {
    list<DummyMediaBaseAbstract*> m_medias;

    //指向基类的指针,只调用基类析构函数
    DummyMediaBaseAbstract* p_music = new DummyMusicDerived(); 
    p_music->setName("Love Story");
    cout << p_music->getName() << endl;
    //p->foo();

    DummyMediaBaseAbstract* p_book = new DummyBookDerived();
    DummyMediaBaseAbstract* p_video = new DummyVideoDerived();

    m_medias.push_back(p_book);
    m_medias.push_back(p_music);
    m_medias.push_back(p_video);

    for(auto x : m_medias) {
        x->foo();
        x->bar();
    }
    delete p_music;
    printf("============================\n");
    delete p_book;
    printf("============================\n");
    delete p_video;
    return 0;
}

看看不同的输出结果,你能看出何种差别

发现1:构造对象时,先调用父类构造函数再调用子类构造函数,析构顺序正好相反。
发现2:当父类析构函数为virtual时,创建的父类指针会释放子类对象,没有的话,则只会释放父类对象。
发现3:静态与动态,若子类重写父类方法,则运行时调用子类方法,否则,调用父类方法。

posted @ 2022-06-10 16:35  一别琉璃春夏  阅读(72)  评论(0)    收藏  举报