为什么我们不能在C ++中为抽象类创建对象?

时间:2010-01-19 05:56:34

标签: c++ object abstract-class

我知道在C ++中不允许这样做,但为什么呢?如果它被允许怎么办?问题是什么?

9 个答案:

答案 0 :(得分:22)

根据您的other question判断,您似乎无法理解课程的运作方式。类是对数据进行操作的函数集合。

函数本身在类中不包含任何内存。以下课程:

struct dumb_class
{
    void foo(){}
    void bar(){}
    void baz(){}
    // .. for all eternity

    int i;
};

大小为int。无论你有多少函数,这个类只会占用int上运行所需的空间。当您在此类中调用函数时,编译器将向您传递指向存储类中数据的位置的指针;这是this指针。

因此,函数位于某个地方的内存中,在程序开始时加载一次,然后等待调用数据进行操作。

虚拟功能不同。 C ++标准要求虚拟函数的行为应该如何进行,只有那种行为应该是什么。通常,实现使用所谓的虚拟表,或简称为vtable。 vtable是一个函数指针表,与普通函数一样,只能分配一次。

参加这个课程,并假设我们的实现者使用vtable:

struct base { virtual void foo(void); };
struct derived { virtual void foo(void); };

编译器需要生成两个vtable,一个用于base,一个用于派生。它们看起来像这样:

typedef /* some generic function pointer type */ func_ptr;

func_ptr __baseTable[] = {&base::foo}; 
func_ptr __derivedTable[] = {&derived::foo}; 

它如何使用此表?当您创建上面的类的实例时,编译器会滑入一个隐藏指针,该指针将指向正确的vtable。所以当你说:

derived d;
base* b = &d;
b->foo();

执行最后一行后,它会转到正确的表(在这种情况下为__derivedTable),转到正确的索引(在本例中为0),然后调用该函数。正如您所看到的那样,最终会调用derived::foo,这正是应该发生的事情。

注意,稍后,这与执行derived::foo(b)相同,将b作为this指针传递。

因此,当存在虚方法时,大小的类将增加一个指针(指向vtable的指针。)多重继承会改变这一点,但它大致相同。您可以在C++-FAQ获取更多详细信息。

现在,问你的问题。我有:

struct base { virtual void foo(void) = 0; }; // notice the = 0
struct derived { virtual void foo(void); };

base::foo没有实施。这使得base::foo成为一个纯粹的抽象函数。所以,如果我打电话给它,就像上面那样:

derived d;
base* b = &d;
base::foo(b);

我们应该期待什么样的行为?作为纯虚方法,base::foo甚至不存在。上面的代码是未定义的行为,可以做任何事情从无到有,崩溃,中间的任何事情。 (或者更糟。)

想想纯粹的抽象函数代表什么。请记住,函数不包含任何数据,它们仅描述如何操作数据。一个纯粹的抽象函数说:“我想调用这个方法并操纵我的数据。你怎么做这取决于你。”

所以,当你说“嗯,让我们称之为抽象方法”时,你会回答上述内容:“由我决定?不,你这样做。”它将回复“@#^ @#^”。告诉那些说“做这个”,“没有。”的人根本没有意义。

直接回答您的问题:

  

“为什么我们不能为抽象类创建对象?”

希望您现在看到,抽象类只定义具体类应该能够执行的功能。抽象类本身只是一个蓝图;你不住在蓝图中,住在实现蓝图的房子里。

答案 1 :(得分:5)

问题很简单:

  • 当调用抽象的方法时,程序应该怎么做?
  • 更糟糕的是:对于非空函数应该返回什么?

应用程序可能必须崩溃或引发运行时异常,因此这会导致麻烦。你不能虚拟实现每个抽象函数。

答案 2 :(得分:3)

在没有抽象方法的情况下,可以简单地将类声明为abstract。我想这可以在理论上实例化,但是类设计师不希望你这样做。它可能会产生意想不到的后果。

然而,通常抽象类具有抽象方法。它们无法实例化,原因很简单,因为它们缺少这些方法。

答案 3 :(得分:3)

因为逻辑上没有任何意义。

抽象类是不完整的描述 它表明需要填写哪些内容才能完成,但如果没有这些内容则不完整。

我的第一个例子是国际象棋游戏:
游戏中有很多不同类型的棋子(King,Queen,Pawn ......等)。

但是没有类型的实际对象,但是所有对象都是从piece派生的对象的实例。你怎么能有一个没有完全定义的东西的对象。创建一个片段对象没有意义,因为游戏不知道它是如何移动的(即抽象部分)。它知道它可以移动但不知道它是如何移动的。

答案 4 :(得分:2)

抽象类根据定义是不可实例化的。他们要求有派生的具体课程。如果抽象类没有纯虚拟(未实现)函数,那还有什么呢?

答案 5 :(得分:1)

为什么我们无法创建抽象类的对象?

简单抽象类包含抽象方法(意味着没有主体的函数),我们不能给抽象方法赋予功能。如果我们尝试为抽象方法提供功能,那么抽象类和虚拟类之间就没有区别。所以最后如果我们创建一个abstrast类的对象,那么调用无用的函数或抽象方法就没有乐趣,因为它们没有功能......所以这就是为什么任何语言都不允许我们创建抽象类的对象。

答案 6 :(得分:0)

实例化的抽象类很没用,因为你会看到更多的“纯虚函数被调用”。 :)

就像:我们都知道汽车会有3个踏板,一个方向盘和一个变速杆。现在,如果是这样,并且会有3个踏板和变速杆以及一个车轮的实例,我不会买它,我想要一辆车,比如座椅,车门,AC等,踏板实际上正在做除了存在之外的东西,这就是抽象类不承诺我的东西,那些实现的东西。

答案 7 :(得分:0)

这是同一类问题,为什么我不能更改const变量的值,为什么我不能从其他类访问私有类成员或为什么我不能覆盖最终方法。

因为这就是这些关键字的目的,以防止您这样做。因为代码的作者认为这样做是危险的,不希望的或者根本不可能由于一些抽象的原因,例如缺乏必要的功能需要由特定的子类添加。实际上并不是因为类是虚拟的,所以不是真的。无法实例化一个类将其定义为虚拟(如果一个无法实例化的类不是虚拟的,那就是一个错误。另一方面,如果给定类的实例有意义,它不应该是标记为虚拟)

答案 8 :(得分:0)

基本上,对象的创建负责为成员变量和成员函数分配内存。但是在这里,在纯虚函数中,我们在派生类中有声明和定义。因此,对象的创建会产生错误。