About C++ "Virtual"

本文探讨了C++中虚函数与多态的概念及其实现机制,包括虚函数表的工作原理、构造函数中调用虚函数的行为以及虚继承如何解决菱形继承问题。并通过示例代码展示了不同情况下虚函数调用的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 class   A
{
public:
  go()
  {
  cout < < "A   " < <endl;
  }
};
class   B   :   virtual   public   A     //这里virtual   有用吗   ?偶想问一下大家
{
public:
go()
{
cout < < "B   " < <endl;
}
};
class   C
{
public:
  virtual   go()
  {
  cout < < "A   " < <endl;
  }
};
class   D   :     public   C
{
public:
go()
{
cout < < "D   " < <endl;
}
};
int   main(int   argc,   char*   argv[])
{
A   *x;
B   dev;
x=&dev;
x- >go();//结果是   A

C   *x1;
D   dev1;
x1=&dev1;
x1- >go();//结果是   D

return   0;
}

因为A,   B中的go()成员函数不是虚函数啊,B中的go()只是重定义了A中的go()
既然他们不是虚函数,那他们也就没有多态特性,调用的时候就根据指针的类型来调用相应的成员函数。
x是A*指针,所以就调用A::go()

virtual   函数是针对类里面的成员函数,这样该类的继承类能够复写virtual   的成员函数。主要目的:实现多态
virtial   继承,是使用在类之上的,两个类同时virtial   继承一个类,这样被继承的那个类只会产生一个实体,主要目的:避免菱形继承。

虚拟继承原来是防止在多重继承时,可能存在一个共同的基类出现二义性。

    如:      
    A                       A      
    ¦                       ¦      
    B                       C      
    /                       /      
        /               /      
            /       /      
                D      
    这种情况下,一般应该使用虚继承

  此时b,c要这样声明:      
    class       b:virtual       public       a      
    {};      
    class       c:virtual       public       b      
    {};      
    代价就是不能用基类对象的指针指向虚拟继承类的对象. 

********************************************************************
请大家说说内联函数,构造函数,静态成员函数为什么不能为virtual函数?
重点虚函数是运行期间确定的
1 >   内联函数

内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数


2 >   构造函数

构造函数用来创建一个新的对象,而虚函数的运行是建立在对象的基础上,在构造函数执行时,对象尚未形成,所以不能将构造函数定义为虚函数


3 >   静态成员函数

静态成员函数属于一个类而非某一对象,没有this指针,它无法进行对象的判别

***********************************************************************************************
昨天在网上看到一篇关于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,为什么呢?

楼上的说法似乎也有点道理.关于VTABLE什么时候创建的以前我还真没想过.应该是在构造函数调用之后.
还有种解释,因为Parent   Clas调用构造函数初始化之前先调用Base   Class的构造函数,这时Parent   Class的对象并没有生成,所以也不存在VTable之类的东西,当然这时只能调用Base的One函数了.
从对象的角度看:在Base()调用时,基类对象部分构造完成,派生类对象部分尚未构建,此时的对象呈现基类特性。
从虚表的角度看:在Base()调用时,对象的虚表被设为基类的虚表,所以会调用基类的函数;
在CParent()调用后,对象的虚表被reset为派生类的虚表,所以会调用派生类的函数。

XXXXXX基类是看不到父类的成员的
XXXXX通过父类调用才能体现出多态
----------------------------
CBase   *example   =   new   CParent;
example- >ABC();

结果是一样的

C++   Primer   3rd   Edition   17.5.8节

        如果在基类的构造函数中调用了一个虚拟函数,而基类和派生类都定义了该函数的实例,
将会怎么样?应该调用哪一个函数实例?如果可以调用虚拟函数的派生类实例,并且它访问
任意的派生类成员,那么调用的结果在逻辑上是未定义的。而程序可能会崩溃。
        为了防止这样的事情发生,在基类构造函数中调用的虚拟实例总是在基类中活动的虚拟
实例。实际上,在基类构造函数中,派生类对象只不过是一个基类类型的对象而已。
        对于派生类对象,在基类析构函数中也是如此;派生类部分也是未定义的,但是这一
次不是因为它还没有被构造,而是因为它已经被销毁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值