如果从抽象类派生,则无法使用具体类指针调用函数

Posted

技术标签:

【中文标题】如果从抽象类派生,则无法使用具体类指针调用函数【英文标题】:Not able to call function with concrete class pointer, if it is derived from abstract class 【发布时间】:2015-08-28 05:28:08 【问题描述】:

我正在使用“g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3”来运行我的代码。两者都是程序编译。

(1.) 以下程序运行时出现分段错误

#include <iostream>
using namespace std;
class Abstract  
  public:
    virtual void func() = 0;
;

class A : public Abstract 
  public:
    void func()  cout << "func()" << endl; 
;

int main() 
  A *ao;
  ao->func(); //--> Segmentation fault`<br/>
/*
if we do
A *ao = new A;
ao->func();
then its working
*/
  return 0;

当 A 现在是具体类时,为什么会观察到这种行为,因为它定义了 Abstracts 方法?

(2) 如果类不是从抽象类派生的,那么它就可以工作了。

class A 
  public:
    void func()  cout << "func()" << endl; 
;

int main() 
  A *ao;
  ao->func();
  return 0;

【问题讨论】:

你所有的例子都有未定义的行为。 *** 上的问题是使用 Markdown 编写的。请学习使用 MarkDown 并正确格式化代码示例 - 这并不难。 【参考方案1】:

当你使用时,

A *ao;
ao->func(); //--> Segmentation fault

ao 未初始化为指向有效对象。在这样的指针上调用成员函数会导致未定义的行为。在您的情况下,这表现为分段错误。

它在没有抽象基类时起作用的事实是未定义行为的意外结果,在这种情况下看似正常的行为。

实际上,看似正常的行为是因为您在调用中没有使用A 的任何成员变量。在抽象基类的情况下,使用虚拟表。由于指针未初始化为指向有效对象,因此尝试访问虚拟表会导致分段错误。

【讨论】:

@πάντα ῥεῖ:就显示希望的文本和表面上没有做任何意外的事情而言,它可能确实是“工作” - 这与编码不同这样可以保证它“不会做任何意外的事情”,甚至可以保证下次运行时它会做同样的事情。假装不理解 elinus 打算“工作”的方式无助于追根溯源......【参考方案2】:

您有未定义的行为,因为您使用的是未初始化的指针,就好像它已被分配了有效对象的地址。编译器不会为您检查 - 由您将其指向合理的地方。

你所做的有点像把一封信丢到邮箱里却没有写地址——谁知道会发生什么?

您可能会从第一次遇到分段错误,但不是第二次,但 C++ 标准并不能保证每次运行程序时都会发生这种情况,更不用说重新编译它、更改编译器参数、修改代码、尝试其他编译器等。

实际上,当函数不是虚拟函数时,您碰巧“逃脱”它的原因可能是被调用函数func 没有尝试通过传递的虚假this 指针访问任何对象数据到它(它将采用来自ao 的值):所以指针的垃圾并不重要。当函数为virtual 时,它会尝试使用this 指针来查找指向虚拟分派表的指针,但随后垃圾指针实际上会咬人并且您的程序崩溃。如上所述,这两种行为都无法保证 - 它是 undefined

【讨论】:

【参考方案3】:
A *ao;
ao->func(); //--> Segmentation fault

ao 将有一些垃圾值,当您尝试 ao->func() 时,您正在尝试访问 ao 指向的内存,因为很可能您没有访问该内存分段错误的权限。

A *ao = new A;
ao->func();
then its working

new 运算符将为 A 的实例分配内存并返回指向 ao 的有效指针,因此它可以正常工作。

【讨论】:

以上是关于如果从抽象类派生,则无法使用具体类指针调用函数的主要内容,如果未能解决你的问题,请参考以下文章

派生类指针如何调用基类函数

指向抽象类的指针

如何从基类调用派生赋值运算符?

如何从包含基类指针的容器中调用派生类函数(基于其类型)?

基类与派生类的指针和成员函数调用原理

多态和抽象