<分区>
假设在 main 中有一个派生类的实例。调用成员函数时,调用的是基类中函数的版本,还是派生类中的重载版本?此外,如果您有一个基类的指针,该指针被初始化为指向构造函数运行的派生类的实例(如果有的话)?
标签 c++ inheritance
<分区>
假设在 main 中有一个派生类的实例。调用成员函数时,调用的是基类中函数的版本,还是派生类中的重载版本?此外,如果您有一个基类的指针,该指针被初始化为指向构造函数运行的派生类的实例(如果有的话)?
最佳答案
struct Base {
virtual ~Base() {}
void nonvirtual() {std::cout << "Base::nonvirtual()\n";}
virtual void isvirtual() {std::cout << "Base::isvirtual()\n";}
};
struct Derived : public Base {
virtual ~Derived() {}
void nonvirtual() {std::cout << "Derived::nonvirtual()\n";}
virtual void isvirtual() {std::cout << "Derived::isvirtual()\n";}
};
这里看不见的是,因为virtual
成员的存在,编译器为Base
类创建了一个函数表,有两个指针(每个虚函数一个) ,并且每个指针指向其中一个虚函数。编译器为派生创建了一个几乎相同的表,其中有两个指针指向这些函数的Derived
版本。这个魔法还为每个类实例添加了一个“虚函数指针”,它指向其中一个表。
int main() {
Derived d;
d.nonvirtual(); //this prints Derived::nonvirtual()
d.isvirtual(); //this prints Derived::isvirtual()
Base b;
b.nonvirtual(); //this prints Base::nonvirtual()
b.isvirtual(); //this prints Base::isvirtual()
}
这为 main 中的每个变量创建空间,并为每个变量调用默认构造函数。构造函数不可见地将不可见虚函数指针设置为指向指向正确函数的表。显而易见的事情的所有功能。
int main() {
Derived d;
Derived* dptr = &d;
dptr->nonvirtual(); //this prints Derived::nonvirtual()
dptr->isvirtual(); //this prints Derived::isvirtual()
Base* bptr = &d;
bptr->nonvirtual(); //this prints Base::nonvirtual()
bptr->isvirtual(); //this prints Derived::isvirtual() !!!!!!!
}
指针和引用是棘手的地方。如果我们创建一个 Derived*
并将其指向一个 Derived
实例,它会继续表现得如此明显。当 Base*
指向 Derived
实例时,不 立即显而易见的是会发生什么。如果您调用非虚拟成员函数,编译器会发现您正在使用 Base
指针,并使用该函数的 Base
版本。比较直接。如果 Base
中的成员函数是 virtual
,那么编译器就会发挥作用。它检查不可见的虚拟指针成员,并检查它指向的表。在这种情况下,它指向具有 Derived
成员函数的表。编译器知道 isvirtual
是第二个虚拟成员,因此它调用该表中的第二个函数,即 Derived::isvirtual
函数。
出于相关原因,如果一个类可以用作基类,您应该几乎总是给它一个虚拟析构函数。
关于c++ - 继承(C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29785643/