精华内容
下载资源
问答
  • 虚函数private
    千次阅读
    2017-09-04 22:31:59
    我们都知道虚函数的使用要点:

    1.在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类中是虚的。2.如果使用指向对象的引用或指针来调用虚函数,程序将使用为对象类型定义的方法,而不是使用为引用或指针类型定义的方法。这样基类引用或指针就可以指向派生类对象。3.如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚的。

    一般这里说的虚函数都是public的,如果虚函数用作private呢?看下面一个例子:

    #include "stdafx.h"
    #include <iostream>
    
    using std::cout;
    using std::endl;
    
    class Bclass
    {
    private:
    	virtual void fun() { cout << "Bclass fun is here!" << endl ; }
    	void fun2() { cout << "Bclass fun2 is here!" << endl; }
    	friend int main();
    };
    
    class Dclass : public Bclass
    {
    public:
    	virtual void fun() { cout << "Dclass fun is here!" << endl; }
    	void fun2() { cout << "Dclass fun2 is here!" << endl; }
    };
    
    
    int main()
    {
    	Bclass *pObject = new Dclass();
    	pObject->fun();
    	pObject->fun2();
    
    	return 0;
    }


    由执行结果可知,private并不改变多态的性质(注意这里用到了友元)

    更多相关内容
  • C++之private虚函数

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

    一般我们说虚函数,它的访问级别都是public的,用类对象可以直接调用,这样就可以实现运行时的类型绑定,那如果我们将虚函数私有化会出现什么情况呢?

    我们先来看一个非虚函数私有化的例子

    class Base
    {
    private:
        void PrintClassName ()
        {
            cout<<"Base"<<endl;
        }
    public:
        void print()
        {
            PrintClassName();
        }
    };
    
    class Derived : public Base
    {
    private:
        void PrintClassName()
        {
            cout<<"Derived"<<endl;
        }
    };

    在main函数里产生一个Derived的对象d,然后调用print()函数,即d.print(),结果输出的却是Base,print()函数没有调用子类的PrintClassName函数,而是调用父类的PrintClassName函数,原来是由于PrintClassName函数不是虚函数之故,所以Base的print()函数调用PrintClassName()函数是在编译时就已经绑定了,而不是运行期绑定。

    下面我们让PrintClassName()函数变成虚函数再执行,就可以看到输出的类名为子类的名称,即Derived。

    那么我们有没有办法调用私有的虚函数呢?当然是有的,不管公有还是私有,只要是虚函数,它的函数地址都会放在虚函数表vftable中,只要我们找到虚函数表中存放的PrintClassName()函数的地址,我们就可以直接调用,前提是你必须对C++类对象的内存布局要熟悉,代码如下,这样也输出Derived,与前面效果相同

    int _tmain(int argc, _TCHAR* argv[])
    {
        
        Derived d;
        //d.print();
        typedef void (*Fun)();
        Fun pFun = NULL;
        pFun = (Fun)*((int *)(*(int *)&d + 0) + 0);
        pFun();
    
        getchar();
        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外部访问成为可能!.。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
  • 1.父类的虚函数(或纯虚函数)是private,子类可以重写成public并且之后可以被外面访问: #include <iostream> using namespace std; class Base { public: Base(){} ~Base(){} private: virtual void ...

    C++中子类重写父类的虚函数能改变其访问权限吗


    答案是:可以的!没问题!

    试了两个最极端的情况:
    1.父类的虚函数(或纯虚函数)是private,子类可以重写成public并且之后可以被外面访问:

    #include <iostream>
    
    using namespace std;
    
    class Base {
    public:
    	Base(){}
    	~Base(){}
    
    private:
    	virtual void myVirtualFunc() { cout << "Base::myVirtualFunc()" << endl; }
    	virtual void myPureVirtualFunc() = 0;
    };
    
    class Derive : public Base
    {
    public:
    	Derive(){}
    	~Derive(){}
    
    public:
    	void myVirtualFunc() { cout << "Derive::myVirtualFunc()" << endl; }
    	void myPureVirtualFunc() { cout << "Derive::myPureVirtualFunc()" << endl; }
    };
    
    int main()
    {
    	Derive d;
    	d.myVirtualFunc();
    	d.myPureVirtualFunc();
    
        std::cout << "Hello World!\n";
    }
    

    运行结果:
    在这里插入图片描述

    2.父类的虚函数(或纯虚函数)是public,子类可以重写成private并且之后就不可被外界访问:

    #include <iostream>
    
    using namespace std;
    
    class Base {
    public:
    	Base(){}
    	~Base(){}
    
    private:
    	virtual void myVirtualFunc() { cout << "Base::myVirtualFunc()" << endl; }
    	virtual void myPureVirtualFunc() = 0;
    };
    
    class Derive : public Base
    {
    public:
    	Derive(){}
    	~Derive(){}
    
    	void callFunc() { myVirtualFunc(); myPureVirtualFunc(); }
    
    public:
    	void myVirtualFunc() { cout << "Derive::myVirtualFunc()" << endl; }
    	void myPureVirtualFunc() { cout << "Derive::myPureVirtualFunc()" << endl; }
    };
    
    int main()
    {
    	Derive d;
    	d.callFunc();
        std::cout << "Hello World!\n";
    }
    
    

    运行结果:
    在这里插入图片描述

    展开全文
  • 当一个函数是内联和虚函数时,会发生代码替换或使用虚表调用吗? 为了弄清楚内联和虚函数,让我们将它们分开来考虑。通常,一个内联函数是被展开的。 class CFoo { private:  int val; public:  int GetVal...
  • 【C/C++】C++之private虚函数

    千次阅读 2013-12-03 00:31:36
    原文地址:C++之private虚函数 一般我们说虚函数,它的访问级别都是public的,用类对象可以直接调用,这样就可以实现运行时的类型绑定,那如果我们将虚函数私有化会出现什么情况呢? 我们先来看一个非虚函数...
  • 虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:...
  • 我们把一个仅仅含有纯虚函数的类...那么,让我们想一想,纯虚函数或者虚函数可以为private吗?如果这种方式是可行的,那么什么时候可以将(纯)虚函数设为private了?这些都是本文将要讨论的主题。一起来看看。 一....
  • C++中虚函数能不能声明为private

    千次阅读 2019-07-13 22:11:12
    如果声明虚函数声明为private时编译会报错,但是还是有手段调用声明为private虚函数的。首先让我们来看看调用声明为private虚函数时会发生什么情况。 #include <iostream> using namespace std; class ...
  • C++虚函数(9) - 虚函数能否为private

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

    万次阅读 多人点赞 2021-05-26 10:25:43
    1.虚函数的使用? 1.1虚函数的定义 在实现c++多态时会用到虚函数虚函数使用的其核心目的是通过基类访问派生类定义的函数。所谓虚函数就是在基类定义一个未实现的函数名,为了提高程序的可读性,建议后代中虚函数...
  • 虚函数指针 虚函数

    千次阅读 2020-02-04 14:02:42
    虚函数指针和虚函数表 1.虚函数的含义 只有用virtual声明类的成员函数,称之为虚函数。 2.虚函数的作用 就是一句话:实现多态的基石 实现多态的三大步: 1.存在继承关系 子类继承父类 2.子类重写父类的virtual ...
  • u010029439的博客-CSDN博客 5)虚函数的缺陷-private只能限制当前类,父类的私有虚函数可以被子类访问 虚函数的缺陷-private只能限制当前类,父类的私有虚函数可以被子类访问_u010029439的博客-CSDN博客 class ...
  • 调用private/protected修饰的虚函数

    千次阅读 2019-08-04 15:16:31
      前些天面试,被问到一个很有意思的问题:c++中能否调用protected修饰的虚函数,之前没有尝试过,所以只能靠直觉回答:“在技术上,只要知道虚函数表的地址就可以调用,但是不清楚protected保护的机制是具体在哪...
  • 虚函数详解

    万次阅读 多人点赞 2019-03-09 20:05:26
    文章目录一、虚函数实例二、虚函数的实现(内存布局)1、无继承情况2、单继承情况(无虚函数覆盖)3、单继承情况(有虚函数覆盖)4、多重继承情况(无虚函数覆盖)5、多重继承情况(有虚函数覆盖)三、虚函数的相关...
  • 本博客主要通过查看类的内容的变化,深入探讨有关指针和表的问题。 一、继承产生的基类表指针和基类表 ...private: int c; }; class Son1 :virtual public Base { public: int a; protected: i
  • 关于虚函数的回顾: 1) :虚函数的地址存放于虚函数表之中。运行期多态就是通过虚函数虚函数表实现的。 2):类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。 3):虚函数的调用会被...
  • C 中的虚函数(virtual function)

    千次阅读 2021-05-23 09:37:39
    简介虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次:class Father{public:virtual void foo() { cout << "Father::foo() is called"<...
  • C++ 纯虚函数 虚函数 override

    千次阅读 2021-08-26 09:23:42
    与“静态绑定”相对的是虚函数的“动态绑定”,即无论 p被声明为 Person* 还是 Student* 类型,其调用的虚函数取决于 p实际指向的对象类型 2 重写 (override) 在程序中加override 关键字,可以避免派生类中忘记重写...
  • 这篇文章主要介绍了C++虚函数虚函数表,内容非常详细,思路清晰,需要的朋友可以参考下。
  • 通常我们都将构造函数的声明置于public区段,假如我们将其放入private区段中会发生什么样的后果?这意味着什么?  当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也...
  • 我们知道一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,...
  • 就是class Derivative 想要用class Base的函数接口,但是又不想让别人通过使用Derivative的对象来使用Base的函数接口。这时可以用private继承。或者Base和Derivative根本没有任何逻辑上的联系,单纯的D想要复用一下...
  • 在基类中定义了public虚函数,在派生类中将其重写,但是设置为private,为什么通过基类指针仍然可以发生动态绑定调用派生类中的private虚函数? 例子如下: class Base { public: // public虚函数 virtual void ...
  • C++虚函数的陷阱

    2020-12-22 19:41:47
    首先看一段代码: 1 class B 2 { 3 public: 4 virtual void fun(int i = 1) ... 12 private: 13 virtual void fun(int i = 2) 14 { 15 std::cout << "driver fun called, " << i; 16 };
  • C++ 多态与虚函数

    千次阅读 2022-03-24 17:15:50
    多态性与虚函数多态性虚函数 多态性 多态性是面向对象程序设计的关键技术之一,若程序设计语言不支持多态性,不能称为面向对象的语言,利用多态性技术,可以调用同一个函数名的函数,实现完全不同的功能 在C++中有两...
  • 虚函数表和虚函数指针

    千次阅读 2020-05-26 20:04:01
    虚函数的地址存放于虚函数表中。运行期多态就是通过虚函数虚函数表实现的。 类的对象内部会有指向类内部的虚表地址的指针(每个类用了一个虚表,每个类的对象用了一个虚指针)。 通过这个指针调用虚函数虚函数的...
  • 虚函数private

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

    千次阅读 多人点赞 2017-07-10 22:25:57
    函数重载和运算符重载实现的多态属于静态多态,而通过虚函数可以实现动态多态。实现函数的动态联编其本质核心则是虚表指针与虚函数表。   1. 虚函数与纯虚函数区别 1)虚函数在子类里面也可以不重载的;但纯虚必须...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 86,349
精华内容 34,539
关键字:

虚函数private