c++ 中如何调用基类的虚函数?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++ 中如何调用基类的虚函数?相关的知识,希望对你有一定的参考价值。

下面是例子:
#include <iostream>

using namespace std;

class A

public:
A(int i) n=i;
A() n=1;
virtual void f() std::cout<<"A.f() is used"<<endl;
void g() std::cout<<"A.g() is used"<<endl;
void k() std::cout<<"A.k() is used"<<endl;

private:
int n;

;

class B:public A

public:
B(int i) n=i;
B() n=1;
void f() std::cout<<"B.f() is used"<<endl;
void g() std::cout<<"B.g() is used"<<endl;
void h() std::cout<<"B.h() is used"<<endl;
virtual void j() std::cout<<"B.h() is used"<<endl;

private:
int n;
;

class C

public:
C(int i) n=i;
C() n=1;
void w() std::cout<<"C.w() is used"<<endl;

private:
int n;
;

int main()

B b;
b.k(); //b显然能看到A中的k()
A *a;
a=&b;
a->f(); //a看到的是B的f()
a->A::f(); //a看到的是A的f()
a->g(); //a看到的是A的g()
a->h(); //a看不到B的h()
a->j(); //a看不到B的虚拟函数j()
cout<<a->C::n<<endl; //看不到
a->C::w(); //看不到
while(1)




一个类的对象中是没有关于普通成员函数的指针的slot,只有成员变量还有虚表指针,类的成员函数的代码定义在PE文件的代码区,所以从程序加载时,就已经分配好了内存用于存放这些代码;代码运行时所需要的内存,比如栈、堆等等,则是代码运行时才分配的;对于某个类的所有对象来说,类成员函数只在内存中有一份拷贝,所有的对象都共享同一份成员函数的代码。同一个类的不同的对象之间的差异仅仅是通过成员变量来体现的。c++实现成员函数的时候实际上用到了一个技巧——this指针。this指针是当前调用成员函数的对象首地址,我们知道通过这个地址可以访问到对应对象的成员变量。那么成员函数如何区分this究竟指向哪个对象呢?技巧就在于,c++背后把成员函数当做普通函数一样调用,除了传入实际的参数外,还把所属对象的指针作为参数this传入,函数内部通过显示的或者隐含的方式访问对象成员。
参考技术A

通过代码调用基类虚函数。

具体代码如下:

class TrajectoryBuilder 

public:

TrajectoryBuilder() 

virtual ~TrajectoryBuilder() 

TrajectoryBuilder(const TrajectoryBuilder&) = delete;

TrajectoryBuilder& operator=(const TrajectoryBuilder&) = delete;

virtual const Submaps* submaps() const = 0

virtual const PoseEstimate& pose_estimate() const = 0;

virtual void AddSensorData(const string& sensor_id,

std::unique_ptr<sensor::Data> data) = 0;

void AddRangefinderData(const string& sensor_id, common::Time time,

const Eigen::Vector3f& origin,

const sensor::PointCloud& ranges) 

AddSensorData(sensor_id,

common::make_unique<sensor::Data>(

time, sensor::Data::Rangefinderorigin, ranges));

参考技术B

//动态联编与虚函数。

//动态绑定联编工作在程序运行时执行,在程序运行时才确定将要调用的函数。虚函数是动态绑定的基础。

//是非静态的成员函数。

//在类的声明中,在函数原型之前写virtual。

//virtual 只用来说明类声明中的原型,不能用在函数实现时。

//具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。

//本质:不是重载声明而是覆盖。

//调用方式:通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。


#include <iostream.h>


class B0 //基类B0声明


public:
virtual void display() //虚成员函数


cout<<"B0::display()"<<endl;

;

class B1: public B0 //公有派生


public:
void display()


cout<<"B1::display()"<<endl;

;

class D1: public B1 //公有派生


public:
void display()


cout<<"D1::display()"<<endl;

;


void fun(B0 *ptr) //普通函数,用一个动态指针指向一个暂时不确定的函数
 

ptr->display();  


void main() //主函数


B0 *p;   //声明基类指针,对所有继承类都适用

B0 b0;    //声明基类对象

B1 b1;   //声明派生类对象

D1 d1;   //声明派生类对象

p=&b0;

fun(p);   //调用基类B0函数成员

p=&b1;

fun(p);   //调用派生类B1函数成员

p=&d1;

fun(p);   //调用派生类D1函数成员

参考技术C #include <iostream>
using namespace std;
class A

public:
void func1()cout<<"A::func1()"<<endl;
virtual void func2()cout<<"A::func2()"<<endl;
void func3()cout<<"A::func3()"<<endl;
;
class B:public A

public:
virtual void func1()cout<<"B::func1()"<<endl;
void func2()cout<<"B::func2()"<<endl;//fun2自动成为虚函数
void func3()cout<<"B::func3()"<<endl;
;
class C: public B //C以A为间接基类

public:
void func1()cout<<"C::func1()"<<endl;//fun1自动成为虚函数
void func2()cout<<"C::func2()"<<endl;//fun2自动成为虚函数
void func3()cout<<"C::func3()"<<endl;
;
int main()

C obj;
A * pa = &obj;
B * pb = &obj;
pa->func1(); //不是虚函数,不是多态,根据类型来判断 ==>A::func1()
pa->func2(); //虚函数,多态,调用的是派生类 ==> C::func2();
pa->func3();
pb->func1(); //虚函数,多态,C::func1();
pb->func2(); //虚函数,多态,C::func2();
pb->func3();
//PS:判断是否是虚函数回到类中去看, e.g: pb是B的指针,就看class B ,B中两函数全是虚函数
obj.A::func1();
obj.A::func2();
obj.A::func3();
obj.B::func1();
obj.B::func2();
obj.B::func3();
return 0;

为啥“基类对象”不能调用它自己的虚函数? C++

【中文标题】为啥“基类对象”不能调用它自己的虚函数? C++【英文标题】:Why can't a 'Base Class object' call it's own virtual function? C++为什么“基类对象”不能调用它自己的虚函数? C++ 【发布时间】:2014-09-02 13:11:53 【问题描述】:

我已经阅读了 C++ 中的虚函数,并了解了它们如何让程序员使用基类的指针访问派生类的成员函数。 (又名多态)。

一直困扰我的问题是:

    为什么要在基类中声明一个同名的函数,如果最后必须声明为虚拟函数? (注意:我需要关于虚函数的多态性方面的答案) 在下面的代码中,如果使用基类指针(第 22 行)调用“virtual display()”,则会显示错误。为什么 C++ 中的虚函数如此严格 w.r.t.没有被基类指针调用?

.

#include <iostream>
using namespace std;

class B

    public:
       void virtual display()
          cout<<"Inside base class.\n"; 

;

class D : public B

    public:
       void display()
          cout<<"Inside derived class.\n"; 
;

int main()

    B *b;
    D d;

//Line-22    b->display();  Why can't 'b' call it's own display()?

    b = &d; 
    b->display();

    system("pause");
    return 0;

输出:

在派生类中。

【问题讨论】:

你可以在调用 display 之前调用它的基类来做到这一点:b-&gt;B::display() @CollioTV 谢谢。它有效:) @CollioTV:尽管使用无效指针执行此操作仍然是非法的(即使在此示例中,它可能看起来是偶然的)。 【参考方案1】:

b 是指针而不是对象。最初它没有指向任何东西(因此通过它间接是一个错误);在b = &amp;d之后,它指向一个D对象,所以用它调用虚函数会调用D的覆盖。

定义了虚拟调度机制,以便根据指针指向的实际对象的类型来选择函数,而不是指针的声明类型。因此,如果它指向一个B 对象,那么它将调用B::display;这里,它指向一个D 对象,所以它调用D::display

为什么要在基类中声明一个同名的函数,如果最终必须声明为虚拟函数?

它需要在基类中声明,以便在使用指向基类的指针时,编译器知道它存在。通过指针调用函数是调用基类版本,还是被派生类覆盖的版本,取决于对象的类型。

在下面的代码中,如果使用基类指针(第 22 行)调用 virtual display(),则会显示错误。

那是因为它不指向任何东西,所以使用它是一个错误。如果它指向一个B 对象,那么它将调用B 中声明的函数。

B b_obj;
b = &b_obj;
b->display();   // "Inside base class"

为什么 C++ 中的虚函数如此死板。没有被基类指针调用?

他们不是;这是通常的称呼方式。但是指针必须指向一个有效的对象,虚拟调度才能工作。

【讨论】:

感谢您在一行中解释“虚拟调度机制”。【参考方案2】:

我承认我不太明白你的问题 #1。在基类中声明虚函数允许派生类覆盖该实现。

这有很多用途(只需搜索多态性、Liskov 替换等)。作为一个简单(和人为)的例子,考虑一下:

struct File

  virtual void open()  some_code; 
  virtual void close()  some_code; 

  static std::unique_ptr<File> create();
;

struct DbgFile : File

  virtual void open()  std::clog << "Opening"; File::open(); 
  virtual void open()  std::clog << "Closing"; File::close(); 
;

std::unique_ptr<File> File::create()

#ifdef NDEBUG
  return  new File ;
#else
  return  new DbgFile ;
#endif


int main()

  auto f = File::create();
  f->open();
  f->close();

上面的main() 使用File 接口,但在调试版本中,它实际上会使用DbgFile 类型的对象,该对象会记录发生在其上的所有操作。


关于您的问题 #2,您的代码中的问题是 b 没有指向任何地方。如果你这样做,它会工作得很好:

int main()

    B *b;
    B bb;
    D d;

    b = &bb;
    b->display();  // Outputs "Inside base class."

    b = &d; 
    b->display();  // Outputs "Inside derived class."

    // In addition, you can explicitly suppress dynamic dispatch:
    b->B::display();  // Outputs "Inside base class."

    return 0;

【讨论】:

通过您的上次更新,我可以轻松地将虚函数和多态性关联起来。发送。【参考方案3】:

为什么要在基类中声明一个同名的函数,如果最终必须声明为虚拟函数? (注意:我需要关于虚函数的多态性方面的答案)

这是必要的,因为基类必须知道它需要在运行时调用哪个函数定义。它是一种界面。

In the code below, if 'virtual display()' is called with a base class pointer (Line 22), it shows an error. Why are virtual functions in C++ so rigid w.r.t. not getting called by base class pointers?

由于指针未初始化,因此会引发错误。像下面这样使用。

Base baseObj1,*basePtr;

basePtr= &baseObj1;

basePtr->Display();  //Inside base class

【讨论】:

以上是关于c++ 中如何调用基类的虚函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++子类如何调父类的虚函数

关于C++的虚函数在父类的内部调用

c++中的虚函数有啥作用?

C++中的多态

C++ 纯虚函数

深入理解C++ 虚函数表