从您的other question 来看,您似乎不了解课程的运作方式。类是对数据进行操作的函数的集合。
函数本身在类中不包含内存。以下类:
struct dumb_class
{
void foo(){}
void bar(){}
void baz(){}
// .. for all eternity
int i;
};
大小为int。不管你有多少功能,这个类只会占用在int上运行所需的空间。当你在这个类中调用一个函数时,编译器会给你一个指向类中数据存储位置的指针;这是this 指针。
因此,该函数位于内存中的某个位置,在程序开始时加载一次,然后等待调用数据以进行操作。
虚函数不同。 C++ 标准没有规定虚函数的行为应该如何进行,只是该行为应该是什么。通常,实现使用所谓的虚拟表,或简称为 vtable。 vtable 是一个函数指针表,与普通函数一样,只分配一次。
学习这个类,并假设我们的实现者使用 vtables:
struct base { virtual void foo(void); };
struct derived { virtual void foo(void); };
编译器将需要创建两个 vtable,一个用于基础,一个用于派生。它们看起来像这样:
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 甚至不存在。上面的代码是未定义的行为,可以做任何事情,从什么都不做到崩溃,介于两者之间。 (或者更糟。)
想想纯抽象函数代表什么。请记住,函数不接受数据,它们只描述如何操作数据。一个纯抽象函数说:“我想调用这个方法并让我的数据被操纵。你怎么做这取决于你。”
因此,当您说“好吧,让我们调用一个抽象方法”时,您是在回复上面的内容:“取决于我?不,你来做。”它将回复“@#^@#^”。告诉那些说“这样做”、“不”的人根本没有意义。
直接回答您的问题:
“为什么我们不能为抽象类创建对象?”
希望您现在看到,抽象类只定义了具体类应该能够执行的功能。抽象类本身只是一个蓝图;你不是住在蓝图里,而是住在执行蓝图的房子里。