设为首页 加入收藏

TOP

C++中的多态、重载、覆盖与隐藏(二)
2012-11-02 08:54:24 】 浏览:2456
Tags:重载 覆盖 隐藏

    
    2、虚函数的语法
   
    虚函数的标志是“virtual”关键字。
   
    2.1 使用virtual关键字
   
    考虑下面的类层次:
   
    class A
   
    {
   
    public:
   
    virtual void foo();
   
    };
   
    class B: public A
   
    {
   
    public:
   
    void foo(); // 没有virtual关键字!
   
    };
   
    class C: public B // 从B继承,不是从A继承!
   
    {
   
    public:
   
    void foo(); // 也没有virtual关键字!
   
    };
   
    这种情况下,B::foo()是虚函数,C::foo()也同样是虚函数。因此,可以说,基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。
   
    2.2 纯虚函数
   
    如下声明表示一个函数为纯虚函数:
   
    class A
   
    {
   
    public:
   
    virtual void foo()=0; // =0标志一个虚函数为纯虚函数
   
    };
   
    一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”.它告诉使用者,我的派生类都会有这个函数。
   
    2.3 虚析构函数
   
    析构函数也可以是虚的,甚至是纯虚的。例如:
   
    class A
   
    {
   
    public:
   
    virtual ~A()=0; // 纯虚析构函数
   
    };
   
    当一个类打算被用作其它类的基类时,它的析构函数必须是虚的。考虑下面的例子:
   
    class A
   
    {
   
    public:
   
    A() { ptra_ = new char ;}
   
    ~A() { delete[] ptra_;} // 非虚析构函数
   
    private:
   
    char * ptra_;
   
    };
   
    class B: public A
   
    {
   
    public:
   
    B() { ptrb_ = new char[20];}
   
    ~B() { delete[] ptrb_;}
   
    private:
   
    char * ptrb_;
   
    };
   
    void foo()
   
    {
   
    A * a = new B;
   
    delete a;
   
    }
   
    在这个例子中,程序也许不会象你想象的那样运行,在执行delete a的时候,实际上只有A::~A()被调用了,而B类的析构函数并没有被调用!这是否有点儿可怕?
   
    如果将上面A::~A()改为virtual,就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。
   
    纯虚的析构函数并没有什么作用,是虚的就够了。通常只有在希望将一个类变成抽象类(不能实例化的类),而这个类又没有合适的函数可以被纯虚化的时候,可以使用纯虚的析构函数来达到目的。
   
    2.4 虚构造函数
   
    构造函数不能是虚的。
   
    3、虚函数使用技巧
   
    3.1 private的虚函数
   
    考虑下面的例子:
   
    class A
   
    {
   
    public:
   
    void foo() { bar();}
   
    private:
   
    virtual void bar() { …}
   
    };
   
    class B: public A
   
    {
   
    private:
   
    virtual void bar() { …}
   
    };
   
    在这个例子中,虽然bar()在A类中是private的,但是仍然可以出现在派生类中,并仍然可以与public或者protected的虚函数一样产生多态的效果。并不会因为它是private的,就发生A::foo()不能访问B::bar()的情况,也不会发生B::bar()对A::bar()的override不起作用的情况。
   
    这种写法的语意是:A告诉B,你最好override我的bar()函数,但是你不要管它如何使用,也不要自己调用这个函数。
   
    3.2 构造函数和析构函数中的虚函数调用
   
    一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”.例如:
   
    class A
   
    {
   
    public:
   
    A() { foo();} // 在这里,无论如何都是A::foo()被调用!
   
    ~A() { foo();} // 同上
   
    virtual void foo();
   
    };
   
    class B: public A
   
    {
   
    public:
   
    virtual void foo();
   
    };
   
    void bar()
   
    {
   
    A * a = new B;
   
    delete a;
   
    }
   
    如果你希望delete a的时候,会导致B::foo()被调用,那么你就错了。同样,在new B的时候,A的构造函数被调用,但是在A的构造函数中,被调用的是A::foo()而不是B::foo()。
   
    3.3 多继承中的虚函数
   
    继承的子类中只有一个虚函数指针,来访问所有的虚函数(包括父类和自己的)。无论继承的虚类(抽象类)有多少,都只保持一个虚函数指针,但虚拟继承有些实现不同。其实子类的这个指针就是继承父类的虚函数指针,只不过是每个类的虚函数表不一样,所以子类的虚函数指针指向的表不一样。子类的虚函数表包括继承、覆盖和新生的虚函数。
   
    3.4 什么时候使用虚函数
   
    在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。
   
    以设计模式 中Factory Method模式为例,Creator的factoryMethod()就是虚函数,派生类override这个函数后,产生不同的Product类,被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作,当然Product类一定也有多态(虚函数)。
   
    另外一个例子就是集合操作,假设你有一个以A类为基类的类层次,又用了一个std::vector[url=]来保存这个类层次中不同类的实例指针,那么你一定希望在对这个集合中的类进行操作的时候,不要把每个指针再cast回到它原来的类型(派生类),而是希望对他们进行同样的操作。那么就应该将这个“一样的操作”声明为virtual.
   
    现实中,远不只我举的这两个例子,但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的”.这句话也可以反过来说:“如果你发现基类提供了虚函数,那么你最好override它”.

      

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇设计模式笔记之---单例模式 下一篇VC++编程实现镜像劫持

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目