精华内容
下载资源
问答
  • C++虚函数(9) - 虚函数能否为private

    千次阅读 2015-05-26 00:48:05
    C++虚函数(9) - 虚函数能否为private

    C++中,虚函数可以为private,并且可以被子类覆盖。

    例如,下面程序工作正常。

    #include<iostream>
    
    class Base {
    private:
        virtual void fun() { std::cout << "Base Fun" << std::endl; }
        friend int main();
    };
    
    class Derived : public Base {
    public:
        void fun() { std::cout << "Derived Fun" << std::endl; }
    };
    
    int main() {
        Base* ptr = new Derived;
        ptr->fun();
        return 0;
    }

    运行结果:
    Derived fun()

    对于上面的程序,有下面几点是需要注意的方面:
    1) ptr是一个Base类型的指针,指向的是Derived对象。最终实际调用的是Derived::fun()。

    2) int main()是Base类的友元函数。如果删除这个友元声明,则程序会编译失败。因为在编译期间,会进行权限检查。对于这行代码ptr->fun(), 编译器会检查到fun是私有函数,base类型的对象/指针无权访问。

    这种行为与Java完全不同。在Java中,私有方法默认是final的,不能被覆盖。

    展开全文
  • 虚函数private

    2013-09-04 02:05:37
    我们把一个仅仅含有纯虚函数的类称为...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。 一

    我们把一个仅仅含有纯虚函数的类称为接口,我们也好像已经习惯了将这个接口中的所有纯虚函数全声明为public,而且按照这样的设计,一切都工作得不错。比如COM正是这样做的,它的接口中几乎不会存在private的纯虚函数。那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数设为private了?这些都是本文将要讨论的主题。一起来看看。

    一.访问限定符与继承
    如果基类隐式(间接)向子类暴露了私有成员,那么从某种意义上讲,该私有成员对于子类是可见的。

    任何一本讲C++基础的课本上都详细地介绍了访问限定符与继承的关系,在这里就不重复了,但是,课本上的东西并不全,不信?那么请先看看下面的例子:

    C++语言c++例子
    #include <string>
    #include <iostream>
    using namespace std;
    class Base
    {
    private:
        string classID() const
        {
           return string("Base");
        }
    protected:
        virtual void doWork() =0 ; //纯虚函数
    public:
        void work()
        {
            cout<<"this class id is "<<classID()<<endl;
            doWork();
        }
        virtual ~Base() {}
    };

    class DerivedA : public Base
    {
    private:
        string classID() const
        {
           return string("DerivedA");
        }
    protected:
        void doWork()
        {
           cout<<"this is DerivedA doWork !"<<endl;
        }
    };

        以上的代码声明了一个基类和一个子类,不过比较奇特的是基类的提供的公共接口是非虚的,而这个非虚的公共接口却调用了一个非虚的私有函数和一个虚拟的保护函数。接着,子类重定义了这两个函数。那么下面的调用会输出什么了?

    Base* bp = new DerivedA();
    bp->work();
    delete bp;

    以下是输出的结果:

    this class id is Base
    this is DerivedA doWork!

    怎么回事?为什么不是

    this class id is DerivedA
    this is DerivedA doWork !

        子类的classID()不是将基类的classID()覆盖了么?我们来分析一下,基类中的公共的work()成员函数调用了私有的classID()成员函数,根据输出的结果来看,在子类中定义的classID方法并没有覆盖基类的同名方法,为什么呢?难道是因为classID是private导致的?那好,我们将classID函数改为public再次运行,我们期望的结果出现了吗?呵呵,很抱歉,没有,希望再次破灭了,为什么会这样?这主要涉及的原因是:普通函数的调用是在编译期确定的,当work函数一看到所调用的classID是非虚的,就会毫无疑问地去直接使用基类的classID。这一切与Base类是否会被继承没有任何关系,跟Base类被继承后子类会否再次定义classID就更没有关系了。

       那么这种情况下,Base类将classID声明为private和public/protected有什么区别了?当将classID声明为private时,DerivedA看不到基类的classID的声明,所以不会发生重定义;当将classID声明为public/protected时,DerivedA将看到基类的classID声明,于是会发生重定义,即会覆盖调基类的classID的定义。讲到这里就要提一下,如果当将classID声明为public/protected,并且子类也定义同名的函数classID,但是子类的classID与基类的classID的函数签名不同,那么此时发生的将是函数重载而不是覆盖。 让我们更进一步,将基类和子类的classID声明都改为virtual public,再次运行程序,会得到以下输出:

    而这正是我们所期望的,不是吗?这其中的原因也很容易理解,因为classID是virtual ,并且是public的,所以会产生多态调用。
       再往下走,将基类和子类的classID声明改为virtual private,再次运行程序,看看输出了什么。

    this class id is DerivedA
    this is DerivedA doWork !

    没有变化,将classID声明为virtual private和声明为 virtual public得到的结果是一样的。“为什么会这样,classID是private啊?”你惊讶地叫出来。是,classID是private,但classID也是virtual,原因就在这里,用基类指针或引用进行虚函数调用采用的是动态绑定,看看编译器为调用classID产生的代码就知道了:

    //c++伪码

    (this->vptr[1])() ;

    在运行时期,通过this指针将会找到正确的vtbl,即DerivedA类的vtbl,这样自然就会出现上面的结果了。那么将classID 声明为private限制了什么?和将非虚函数声明为private一样,这将使得在Base类外部无法调用多态函数classID,只能在Base内部调用,如通过work函数调用。

       可见,多态性与将实现多态的函数的访问限定符没有任何关系,private 函数仍然可以实现多态,它的指针仍然位于vtbl中,只不过该函数的多态一般只能在基类的内部由其他非虚函数调用该函数的时候反映出来,访问限定符仅仅限制外部对类的成员的访问权限,它并没有破坏以下规则:

        通过基类指针或引用调用成员函数时,如果该函数时非虚的,那么将采用静态绑定,即编译时绑定;如果该函数是虚拟的,则采用动态绑定,即运行时绑定。 二.virtual与访问限定符结合

        上面我们通过分析,已经知道了多态的实现与访问限定符没有任何关系,访问限定符只是控制类的成员对外部的可见性,但不限制多态。正如上面提到的,将classID声明为virtual private和声明为 virtual public后再次运行程序,得到的结果是一样的,上面我们简单的地分析了一下表面现象,但这个问题决不是这么简单,让我们挖掘更深层次的意义,我想这应该属于OOA、OOD的范畴了。好,让我们一步步看过来。

        当我们将classID声明为非虚的 private时,子类将看不见它,当然也就无法覆盖或重载它,即在这中情况下,子类无法更改classID的实现,但是子类继承了公共接口work(),而这个接口调用了classID,所以,可以看作,子类间接地继承了classID的实现,并且这个实现是无法修改的。于是,我可以说,基类中声明一个普通私有成员函数,表示这是一个不可被更改的实现细节。

        再来讨论将classID声明为virtual private的情况,声明为private表示基类不想让子类看到这个函数,但是又声明为virtual,表示基类想让这个函数实现多态。呵呵,基类既想实现多态,却又不让子类看见这个函数,这似乎有点自相矛盾,是吗?其实,这其中的意思是,子类既可以修改这个实现,也可以继承其基类默认的实现。所以可以这么说,如果基类中有一个虚拟私有成员函数,表示这是一个“可以”被派生类修改的实现细节。注意,当中的用词,是“可以”,而不是别的。

        最后来看看将classID声明为virtual protected的情况。将classID声明为protected表示基类“需要”子类看见这个函数,注意,我使用“需要”这个动词,这个词表示了一定的“强制”意味。与将classID声明为virtual private的情况对比一下,我想你已经知道答案了,即是,如果基类中有一个虚拟保护成员函数,表示这是一个必须被派生类修改的实现细节。“必须”这个词表达了强制的意思。

        关于“将virtual与访问限定符结合”的问题就讨论这么多,你也许说,还漏掉了将classID声明为virtual public的情况。是的,其实,我并不推荐将虚拟函数声明为public,尽管这种方式在现在很流行,我推荐将其使用virtual protected来替换,这就说明基类必须另外发布一个几乎不更改的非虚public接口,在这个接口中调用了virtual protected或virtual private函数,这样以来,我们就对类的内部实现作了进一步的隐藏,而这无论是对系统的可扩展性,还是可维护性都是大有帮助的。“虚拟函数应该和数据成员一样对待――让他们成为私有的,除非设计需求表明应该有较少的限制。提升它们到更高存取级别比把它们降到更私有的级别更容易些。”

    最后,把上面所说的小结一下:

         基类中的一个普通私有成员函数,表示这是一个不可被更改的实现细节。 
          基类中的一个虚拟私有成员函数,表示这是一个可以被派生类修改的实现细节。
          基类中的一个虚拟保护成员函数,表示这是一个必须被派生类修改的实现细节。 
          最好不要将虚拟成员函数声明为public,而是用protected来替换。

    三.模板方法模式

    在理解了上面所述的内容的情况下,再来理解模板方法模式就非常easy了,模板方法是在GOF的经典大作《设计模式》中阐述了一种模式,该模式定义了一个操作中的算法的骨架,而将一些步骤的实现延迟到子类中,模板方法使得派生类可以不改变一个算法的结构即可重定义算法的某些特定步骤。在这里,我不想再重复解释这个模式如何实现的,我仅仅举个例子,这个例子将体现出模板方法中最重要的思想。

    假设基类定义的一个算法的骨架由3个步骤完成,其中第一个步骤是该继承体系中不可被改变的一个步骤,即所有的类对该步骤的实现都是一样的,那个这个步骤可以设置为非虚的private ;第二个步骤是一个可以被派生类改写也可以不被改写的步骤,通过上面的讨论知道,可以将其设为virtual private ;第三个步骤是针对每一个派生类的实现都不同,那么这个步骤可以被设为virtual protected ,而且,步骤三只能针对特定的派生类才有意义,所以将步骤三也设为纯虚函数。如下面的代码所示:

    class BaseTemplate

    private: 
        void step1(void) // 不可被更改的实现细节 
        {       
        }
        virtual void step2(void ) // 可以被派生类修改的实现细节 
        {        
        }
    protected:
        virtual void step3(void ) =0; // 必须被派生类修改的实现细节
    public:
        void work(void) // 骨架函数,实现了骨架
        {
           step1() ; 
           step2() ; 
           step3() ;     
        } 
    };

       注意,上例中根本没有暴露任何虚函数,所有的这一切都是通过work()这个非虚的public接口展现出来的,当我们用一个BaseTemplate指针调用work()时,表面上是一个非虚函数调用,采用静态绑定,事实上也正是这样,但是,这个调用的背后隐藏的却是多态调用,即step2和step3动态绑定了。看见,采用模板方法模式,不仅定义了一个算法的骨架,而且把这个骨架的实现的细节作了进一步的封装。我们可以在模板方法模式中可以这样设计:

    (1)   如果一个函数作为算法骨架中不可变更的一部分,那么可以将此函数作为基类的私有函数,并且在基类的公共骨架函数中调用该函数,即该函数作为骨架的一个不可更改的实现细节。

    (2)    如果一个函数提供了算法骨架某环节的一个缺省实现,那么可以考虑将该函数作为基类的私有虚函数,表示子类可以改写它,也可以不改写它。

    (3)    如果作为算法骨架一部分某个函数要求在子类中拥有不同的实现,那么可以考虑将该函数作为基类的保护(纯)虚函数,表示子类必须改写它。

        讲到这里,已经差不多了,在结束的时候,提一下语法与语义的联系。通常,语法是表象,语义是表象后面隐藏的东西,而这些隐藏的语义往往更具有价值。举个例子,public继承与private继承在语法方面似乎没有什么更多的东西值得探讨,它们的区别仅仅在于改变了继承得到的成员的可见性,但是从语义方面来分析,它们就相差太远了,private继承在语义上来讲是“通过基类来实现自己”,即是“实现继承”,在这种继承关系中,基类和子类的关系是很薄弱的;而public继承在语义上即是我们所熟知的“IS-A”关系,它体现了基类和子类之间的亲密性,也正是这种“IS-A”关系为多态性提供了基础。

        所以,通过表面的语法来挖掘其背后的语义很有意义,就像这篇文章中提到的将访问限定符与virtual结合起来的语法背后隐藏的语义,挖掘出这些语义,对于我们以后在进行设计时作恰当的抉择无疑是大有帮助的。

    展开全文
  • 大家,先想一想,如果把原来实现多态的父类中virtual函数声明为private的形式,是否还可以实现多态呢。答案将由下面的代码给出。 #include using namespace std; class A { private: virtual void print() { ...

    大家,先想一想,如果把原来实现多态的父类中virtual函数声明为private的形式,是否还可以实现多态呢。答案将由下面的代码给出。

    #include<iostream>
    using namespace std;
    class A
    {
    private:
            virtual void print()
            {
                    cout<<"in class A!"<<endl;
            }
    public:
            void f()
            {
                    print();
            }
    };
    class B:public A
    {
    public:
            void print()
            {
                    cout<<"in class B!"<<endl;
            }
    };
    class D:public A
    {
    public:
            void print()
            {
                    cout<<"int class D!"<<endl;
            }
    };
    int main(void)
    {
            A *p1=new B;
            p1->f();
            A *p2=new D;
            p2->f();
            return 0;
    }
    


    答案是可以的,至于为什么,这是一种设计模式的理念。具体的等我弄懂了再补充大家可以先自己想下,研究研究。

    展开全文
  • private虚函数

    千次阅读 2012-05-14 15:13:30
    我们把一个仅仅含有纯虚函数的类称为接口...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。
    我们把一个仅仅含有纯虚函数的类称为接口,我们也好像已经习惯了将这个接口中的所有纯虚函数全声明为public,而且按照这样的设计,一切都工作得不错。比如COM正是这样做的,它的接口中几乎不会存在private的纯虚函数。那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数设为private了?这些都是本文将要讨论的主题。一起来看看。

    一.访问限定符与继承   

    如果基类隐式(间接)向子类暴露了私有成员,那么从某种意义上讲,该私有成员对于子类是可见的。
    任何一本讲C++基础的课本上都详细地介绍了访问限定符与继承的关系,在这里就不重复了,但是,课本上的东西并不全,不信?那么请先看看下面的例子:
     
    #include <string>  
    #include <iostream> 
    using namespace std ; 
    
    class Base 
    { 
    private: 
        string classID() const
        { 
           return string("Base") ; 
        } 
    
    protected: 
        virtual void doWork() =0 ; //纯虚函数
    
    public:  
        void work() 
        { 
            cout<<"this class id is "<<classID()<<endl ;  
            doWork() ; 
        } 
    
        virtual ~Base() 
        { 
        } 
    }; 
    
    class DerivedA : public Base 
    { 
    private: 
        string classID() const  
        { 
           return string("DerivedA") ;
        } 
    
    protected: 
        void doWork() 
        { 
           cout<<"this is DerivedA doWork !"<<endl ; 
        } 
    };


        以上的代码声明了一个基类和一个子类,不过比较奇特的是基类的提供的公共接口是非虚的,而这个非虚的公共接口却调用了一个非虚的私有函数和一个虚拟的保护函数。接着,子类重定义了这两个函数。那么下面的调用会输出什么了?   

    Base* bp = new DerivedA() ; 
    bp->work() ;  
    delete bp ; 
       以下是输出的结果:
       this class id is Base 
       this is DerivedA doWork ! 


       怎么回事?为什么不是 
          this class id is DerivedA 
          this is DerivedA doWork !

    子类的classID()不是将基类的classID()覆盖了么?我们来分析一下,基类中的公共的work()成员函数调用了私有的classID()成员函数,根据输出的结果来看,在子类中定义的classID方法并没有覆盖基类的同名方法,为什么呢?难道是因为classID是private导致的?那好,我们将classID函数改为public再次运行,我们期望的结果出现了吗?呵呵,很抱歉,没有,希望再次破灭了,为什么会这样?这主要涉及的原因是:普通函数的调用是在编译期确定的,当work函数一看到所调用的classID是非虚的,就会毫无疑问地去直接使用基类的classID。这一切与Base类是否会被继承没有任何关系,跟Base类被继承后子类会否再次定义classID就更没有关系了。

       那么这种情况下,Base类将classID声明为private和public/protected有什么区别了?当将classID声明为private时,DerivedA看不到基类的classID的声明,所以不会发生重定义;当将classID声明为public/protected时,DerivedA将看到基类的classID声明,于是会发生重定义,即会覆盖调基类的classID的定义。讲到这里就要提一下,如果当将classID声明为public/protected,并且子类也定义同名的函数classID,但是子类的classID与基类的classID的函数签名不同,那么此时发生的将是函数重载而不是覆盖。 让我们更进一步,将基类和子类的classID声明都改为virtual public,再次运行程序,会得到以下输出: 
             this class id is DerivedA 
             this is DerivedA doWork !
    而这正是我们所期望的,不是吗?这其中的原因也很容易理解,因为classID是virtual ,并且是public的,所以会产生多态调用。
       再往下走,将基类和子类的classID声明改为virtual private,再次运行程序,看看输出了什么。
          this class id is DerivedA 
          this is DerivedA doWork !
    没有变化,将classID声明为virtual private和声明为 virtual public得到的结果是一样的。“为什么会这样,classID是private啊?”你惊讶地叫出来。是,classID是private,但classID也是virtual,原因就在这里,用基类指针或引用进行虚函数调用采用的是动态绑定,看看编译器为调用classID产生的代码就知道了:
    //c++伪码
    (this->vptr[1])() ;
    在运行时期,通过this指针将会找到正确的vtbl,即DerivedA类的vtbl,这样自然就会出现上面的结果了。那么将classID 声明为private限制了什么?和将非虚函数声明为private一样,这将使得在Base类外部无法调用多态函数classID,只能在Base内部调用,如通过work函数调用。
       可见,多态性与将实现多态的函数的访问限定符没有任何关系,private 函数仍然可以实现多态,它的指针仍然位于vtbl中,只不过该函数的多态一般只能在基类的内部由其他非虚函数调用该函数的时候反映出来,访问限定符仅仅限制外部对类的成员的访问权限,它并没有破坏以下规则: 

           通过基类指针或引用调用成员函数时,如果该函数时非虚的,那么将采用静态绑定,即编译时绑定;如果该函数是虚拟的,则采用动态绑定,即运行时绑定。 二.virtual与访问限定符结合
    上面我们通过分析,已经知道了多态的实现与访问限定符没有任何关系,访问限定符只是控制类的成员对外部的可见性,但不限制多态。正如上面提到的,将classID声明为virtual private和声明为 virtual public后再次运行程序,得到的结果是一样的,上面我们简单的地分析了一下表面现象,但这个问题决不是这么简单,让我们挖掘更深层次的意义,我想这应该属于OOA、OOD的范畴了。好,让我们一步步看过来。
    当我们将classID声明为非虚的 private时,子类将看不见它,当然也就无法覆盖或重载它,即在这中情况下,子类无法更改classID的实现,但是子类继承了公共接口work(),而这个接口调用了classID,所以,可以看作,子类间接地继承了classID的实现,并且这个实现是无法修改的。于是,我可以说,基类中声明一个普通私有成员函数,表示这是一个不可被更改的实现细节。
    再来讨论将classID声明为virtual private的情况,声明为private表示基类不想让子类看到这个函数,但是又声明为virtual,表示基类想让这个函数实现多态。呵呵,基类既想实现多态,却又不让子类看见这个函数,这似乎有点自相矛盾,是吗?其实,这其中的意思是,子类既可以修改这个实现,也可以继承其基类默认的实现。所以可以这么说,如果基类中有一个虚拟私有成员函数,表示这是一个“可以”被派生类修改的实现细节。注意,当中的用词,是“可以”,而不是别的。
    最后来看看将classID声明为virtual protected的情况。将classID声明为protected表示基类“需要”子类看见这个函数,注意,我使用“需要”这个动词,这个词表示了一定的“强制”意味。与将classID声明为virtual private的情况对比一下,我想你已经知道答案了,即是,如果基类中有一个虚拟保护成员函数,表示这是一个必须被派生类修改的实现细节。“必须”这个词表达了强制的意思。
    关于“将virtual与访问限定符结合”的问题就讨论这么多,你也许说,还漏掉了将classID声明为virtual public的情况。是的,其实,我并不推荐将虚拟函数声明为public,尽管这种方式在现在很流行,我推荐将其使用virtual protected来替换,这就说明基类必须另外发布一个几乎不更改的非虚public接口,在这个接口中调用了virtual protected或virtual private函数,这样以来,我们就对类的内部实现作了进一步的隐藏,而这无论是对系统的可扩展性,还是可维护性都是大有帮助的。“虚拟函数应该和数据成员一样对待――让他们成为私有的,除非设计需求表明应该有较少的限制。提升它们到更高存取级别比把它们降到更私有的级别更容易些。”
    最后,把上面所说的小结一下:
          基类中的一个普通私有成员函数,表示这是一个不可被更改的实现细节。 
          基类中的一个虚拟私有成员函数,表示这是一个可以被派生类修改的实现细节。
          基类中的一个虚拟保护成员函数,表示这是一个必须被派生类修改的实现细节。 
          最好不要将虚拟成员函数声明为public,而是用protected来替换。
    三.模板方法模式
    在理解了上面所述的内容的情况下,再来理解模板方法模式就非常easy了,模板方法是在GOF的经典大作《设计模式》中阐述了一种模式,该模式定义了一个操作中的算法的骨架,而将一些步骤的实现延迟到子类中,模板方法使得派生类可以不改变一个算法的结构即可重定义算法的某些特定步骤。在这里,我不想再重复解释这个模式如何实现的,我仅仅举个例子,这个例子将体现出模板方法中最重要的思想。
    假设基类定义的一个算法的骨架由3个步骤完成,其中第一个步骤是该继承体系中不可被改变的一个步骤,即所有的类对该步骤的实现都是一样的,那个这个步骤可以设置为非虚的private ;第二个步骤是一个可以被派生类改写也可以不被改写的步骤,通过上面的讨论知道,可以将其设为virtual private ;第三个步骤是针对每一个派生类的实现都不同,那么这个步骤可以被设为virtual protected ,而且,步骤三只能针对特定的派生类才有意义,所以将步骤三也设为纯虚函数。如下面的代码所示:
     
    class BaseTemplate 

    private: 

        void step1(void)  // 不可被更改的实现细节 
        { 
             
        } 
        virtual void step2(void ) // 可以被派生类修改的实现细节 
        { 
             
        } 

    protected: 
        virtual void step3(void ) =0; // 必须被派生类修改的实现细节     

    public: 
        void work(void) // 骨架函数,实现了骨架 
        { 
           step1() ; 
           step2() ; 
           step3() ;     
        } 
    };    

       注意,上例中根本没有暴露任何虚函数,所有的这一切都是通过work()这个非虚的public接口展现出来的,当我们用一个BaseTemplate指针调用work()时,表面上是一个非虚函数调用,采用静态绑定,事实上也正是这样,但是,这个调用的背后隐藏的却是多态调用,即step2和step3动态绑定了。看见,采用模板方法模式,不仅定义了一个算法的骨架,而且把这个骨架的实现的细节作了进一步的封装。我们可以在模板方法模式中可以这样设计:
    (1)       如果一个函数作为算法骨架中不可变更的一部分,那么可以将此函数作为基类的私有函数,并且在基类的公共骨架函数中调用该函数,即该函数作为骨架的一个不可更改的实现细节。
    (2)       如果一个函数提供了算法骨架某环节的一个缺省实现,那么可以考虑将该函数作为基类的私有虚函数,表示子类可以改写它,也可以不改写它。
    (3)       如果作为算法骨架一部分某个函数要求在子类中拥有不同的实现,那么可以考虑将该函数作为基类的保护(纯)虚函数,表示子类必须改写它。
    讲到这里,已经差不多了,在结束的时候,提一下语法与语义的联系。通常,语法是表象,语义是表象后面隐藏的东西,而这些隐藏的语义往往更具有价值。举个例子,public继承与private继承在语法方面似乎没有什么更多的东西值得探讨,它们的区别仅仅在于改变了继承得到的成员的可见性,但是从语义方面来分析,它们就相差太远了,private继承在语义上来讲是“通过基类来实现自己”,即是“实现继承”,在这种继承关系中,基类和子类的关系是很薄弱的;而public继承在语义上即是我们所熟知的“IS-A”关系,它体现了基类和子类之间的亲密性,也正是这种“IS-A”关系为多态性提供了基础。
    所以,通过表面的语法来挖掘其背后的语义很有意义,就像这篇文章中提到的将访问限定符与virtual结合起来的语法背后隐藏的语义,挖掘出这些语义,对于我们以后在进行设计时作恰当的抉择无疑是大有帮助的。
    展开全文
  • 纯虚函数能为private吗? 我们把一个仅仅含有纯虚函数的类称为接口,...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是
  • 纯虚函数可以为private吗?

    千次阅读 2008-11-17 13:56:00
    我们把一个仅仅含有纯虚函数的类...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。一.访
  • 如果声明虚函数声明为private时编译会报错,但是还是有手段调用声明为private虚函数的。首先让我们来看看调用声明为private虚函数时会发生什么情况。 #include <iostream> using namespace std; class ...
  • 关于虚函数的回顾: 1) :虚函数的地址存放于虚函数表之中。... 3):虚函数的调用会被编译器转换虚函数表的访问 。 当虚函数是public的,这个时候实现多态的方式: class Base { public: virtual...
  • 在基类中定义了public虚函数,在派生类中将其重写,但是设置为private什么通过基类指针仍然可以发生动态绑定调用派生类中的private虚函数? 例子如下: class Base { public: // public虚函数 virtual void ...
  • 纯虚函数为private

    千次阅读 2013-11-19 08:28:01
    我们把一个仅仅含有纯虚函数的类称为接口...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的, 那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看
  • C++之private虚函数

    2018-09-16 23:40:04
    一般我们说虚函数,它的访问级别都是public的,用类对象可以直接调用,这样就可以实现运行时的类型绑定,那如果我们将虚函数私有化会出现什么情况呢? 我们先来看一个非虚函数私有化的例子 class Base { private:...
  • 虚函数用作private会怎样?

    千次阅读 2017-09-04 22:31:59
    2.如果使用指向对象的引用或指针来调用虚函数,程序将使用对象类型定义的方法,而不是使用引用或指针类型定义的方法。这样基类引用或指针就可以指向派生类对象。3.如果定义的类将被用作基类,则应将那些要在派生...
  • 我们把一个仅仅含有纯虚函数的类...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。一.访
  • 纯虚函数为private吗?

    千次阅读 2006-08-22 15:25:00
    我们把一个仅仅含有纯虚函数的类称为...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。一
  • 在用户程序中,派生类对象的虚指针指向的虚函数表中是仍然有基类private虚函数的条目,但是由于属性为private,无法实现正常的访问!// 通过得到private虚函数的地址强制访问说明其确实存在于子类的虚函数表中! ...
  • 原理:如果基类包含虚函数,则派生类的对象都会有一个虚函数表指针,而且这个指针通常都是处于对象的最前面,即对象的地址和虚函数表指针的地址是一样的。   #include using namespace std; class Base { ...
  • 【C/C++】C++之private虚函数

    千次阅读 2013-12-03 00:31:36
    一般我们说虚函数,它的访问级别都是public的,用类对象可以直接调用,这样就可以实现运行时的类型绑定,那如果我们将虚函数私有化会出现什么情况呢? 我们先来看一个非虚函数私有化的例子 class Base { private:...
  • 一般我们说虚函数,它的访问级别都是public的,用类对象可以直接调用,这样就可以实现运行时的类型绑定,那如果我们将虚函数私有化会出现什么情况呢? 我们先来看一个非虚函数私有化的例子 class Base { ...
  • C++虚函数表剖析

    万次阅读 多人点赞 2016-02-18 20:28:23
    关键词:虚函数,虚表,虚表指针,动态绑定,多态一、概述为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。二、类的虚表每个...
  • 我们把一个仅仅含有纯虚函数的类...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数为private了?这些都是本文将要讨论的主题。一起来看看。 一....
  • C++虚函数指针虚函数

    千次阅读 2017-07-10 22:25:57
    函数重载和运算符重载实现的多态属于静态多态,而通过虚函数可以实现动态多态。实现函数的动态联编其本质核心则是虚表指针与虚函数表。   1. 虚函数与纯虚函数区别 1)虚函数在子类里面也可以不重载的;但纯虚必须...
  • 很多情况下要求当前的程序中只有一个object。... 当我们在程序中声明一个对象时,编译器调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私
  • 虚函数

    千次阅读 多人点赞 2015-12-17 22:37:05
    虚函数是面向对象编程函数的一种特定形态,是C++用于实现多态的一种有效机制。1、什么是虚函数? 指向基类的指针在操作它的多态类对象时,会根据不同的类对象调用其相应的函数,这个函数就是虚函数虚函数用...
  • C++虚函数虚函数表原理

    万次阅读 多人点赞 2018-07-26 19:49:54
    虚函数的调用会被编译器转换虚函数表的访问: ptr-&amp;gt;f(); //ptr代表this指针,f是虚函数 *(ptr-&amp;gt;vptr[1])(ptr); 上述代码中,ptr代表一个this指针,ptr指向的vptr是类内部的虚表...
  • 虚函数详解

    千次阅读 多人点赞 2019-03-09 20:05:26
    文章目录一、虚函数实例二、虚函数的实现(内存布局)1、无继承情况2、单继承情况(无虚函数覆盖)3、单继承情况(有虚函数覆盖)4、多重继承...问题1、构造函数什么不能定义为虚函数2、析构函数什么要定义为虚函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,668
精华内容 31,067
关键字:

虚函数可以为private