您的位置:程序门 -> c/c++ -> c++ 语言



关于virtual函数


[收藏此页] [打印本页]选择字色:背景色:字体:[][][]


关于virtual函数[已结贴,结贴人:jyq686]
发表于:2007-02-09 10:48:42 楼主
昨天在网上看到一篇关于this指针的文章,结果不但没看懂,还暴露出我对虚函数的疑惑.如下:

#include <iostream>
using   namespace   std;

class   cbase
{
public:
        virtual   void   one()
        {
                cout   < <   "base   class\n ";
        }
        cbase()
        {
                this   ->   one();
        }
};

class   cparent   :   public   cbase
{
public:
        cparent()
        {
        }
        virtual   void   one()
        {
                cout   < <   "parent   class\n ";
        }

};

cparent   example;

int   main()
{
        return   0;
}
结果输出是:base   class  

我的疑问是:程序执行cparent   example;句时,先调用cbase类的构造函数cbase(),那末它就会执行this-> one()句,而因为cparent类中改写了函数函数one(),那么它必调用的是cparnet的虚函数one()吧?所以输出应该是:parent   class啊。

/////////////////////////////////////////////////
下面我加一个函数abc来说明这一点啊。
#include   "stdafx.h "
#include   <vector>
#include   <iostream>

using   namespace   std;

class   cbase
{
public:
        virtual   void   one()
        {
                cout   < <   "base   class\n ";
        }
        cbase()
        {
                this-> one();
        }

  void   abc()
  {
    cout < < "cbase   abc " < <endl;
    this-> one();
  }
};

class   cparent   :   public   cbase
{
public:
        cparent()
        {
        }
        virtual   void   one()
        {
                cout   < <   "parent   class\n ";
        }

};

 

 

int   main(int   argc,   char*   argv[])
{
//   printf( "hello   world!\n ");
//   cbase   *example;
  cparent   parent   ;
  parent.abc();
  return   0;
}


//输出结果:
base   class
cbase   abc
parent   class

看,调用abc时输出的就是parnet   class,为什么呢?
发表于:2007-02-09 11:00:411楼 得分:0
当然是输出base   class了,你注意this-> one().其实和不加this-> 一样,因为都是成员函数.这里的this对象肯定是base类型.
发表于:2007-02-09 11:00:512楼 得分:0
以前也没注意这问题,   看上面的结果意思好象是   虚表是在构造函数只后才产生的..?
发表于:2007-02-09 11:07:103楼 得分:1
楼上的说法似乎也有点道理.关于vtable什么时候创建的以前我还真没想过.应该是在构造函数调用之后.
发表于:2007-02-09 11:10:074楼 得分:2
还有种解释,因为parent   clas调用构造函数初始化之前先调用base   class的构造函数,这时parent   class的对象并没有生成,所以也不存在vtable之类的东西,当然这时只能调用base的one函数了.
发表于:2007-02-09 11:16:025楼 得分:10
从对象的角度看:在base()调用时,基类对象部分构造完成,派生类对象部分尚未构建,此时的对象呈现基类特性。
从虚表的角度看:在base()调用时,对象的虚表被设为基类的虚表,所以会调用基类的函数;
在cparent()调用后,对象的虚表被reset为派生类的虚表,所以会调用派生类的函数。
发表于:2007-02-09 11:20:186楼 得分:0
构造函数不能是虚的.
发表于:2007-02-09 11:21:197楼 得分:0
基类是看不到父类的成员的
通过父类调用才能体现出多态

发表于:2007-02-09 11:29:098楼 得分:0
johnny_de(是的,当时就是这样的!)  
magicsutra()  

两位说的好像都有道理,不过缺少论证。呵呵。

有没有这方面的文章啊。
发表于:2007-02-09 11:33:059楼 得分:0
wanfustudio(雁南飞:知识之败,慕虚名而不务潜修也)   (   )   信誉:97         blog     2007-02-09   11:21:19     得分:   0    
 
 
  基类是看不到父类的成员的
通过父类调用才能体现出多态
----------------------------
cbase   *example   =   new   cparent;
example-> abc();

结果是一样的
   
 
发表于:2007-02-09 12:21:1110楼 得分:0
对象角度说法:看c++   primer;   虚表角度的说法:看inside   c++   object.
发表于:2007-02-09 12:37:1911楼 得分:0
magicsutra()
从虚表的角度看:在base()调用时,对象的虚表被设为基类的虚表,所以会调用基类的函数;
在cparent()调用后,对象的虚表被reset为派生类的虚表,所以会调用派生类的函数。


#include   "stdafx.h "
#include   <vector>
#include   <iostream>

using   namespace   std;

class   cbase
{
public:
        virtual   void   one()
        {
                cout   < <   "base   class\n ";
        }
        cbase()
        {
                cout < <*(unsigned   int   *)this < <endl;//虚表地址
        }
};

class   cparent   :   public   cbase
{
public:
        cparent()
        {
                cout < <*(unsigned   int   *)this < <endl;//虚表地址
        }
        virtual   void   one()
        {
                cout   < <   "parent   class\n ";
        }

};


int   main(int   argc,   char*   argv[])
{
  cparent   parent   ;
  return   0;
}
发表于:2007-02-09 15:47:1812楼 得分:0


首先   base   的函数指针初始化为基类的,   因为基类是在子类完成之前就必须完成定义的,所以当时函数指针确实还是指向base::one().   而当初始完基类后,子类相关的虚函数进行函数指针的改写.这时,   函数指针才指向parent::one();

因为调用的时机恰好在base   初始化之后,   parent初始化之前,因此才出现这样的结果.

发表于:2007-02-09 18:37:4513楼 得分:7
c++   primer   3rd   edition   17.5.8节

        如果在基类的构造函数中调用了一个虚拟函数,而基类和派生类都定义了该函数的实例,
将会怎么样?应该调用哪一个函数实例?如果可以调用虚拟函数的派生类实例,并且它访问
任意的派生类成员,那么调用的结果在逻辑上是未定义的。而程序可能会崩溃。
        为了防止这样的事情发生,在基类构造函数中调用的虚拟实例总是在基类中活动的虚拟
实例。实际上,在基类构造函数中,派生类对象只不过是一个基类类型的对象而已。
        对于派生类对象,在基类析构函数中也是如此;派生类部分也是未定义的,但是这一
次不是因为它还没有被构造,而是因为它已经被销毁。
发表于:2007-11-05 01:34:4614楼 得分:0
学习标记.好问题!


快速检索

最新资讯
热门点击