我正在学习C ++,并且刚开始使用虚函数。
从我阅读的内容(在书中和在线上)中,虚函数是基类中的函数,您可以在派生类中重写这些函数。
但是在本书的前面,当学习基本继承时,我无需使用virtual就可以在派生类中覆盖基本函数。
那我在这里想念什么? 我知道虚函数还有很多,这似乎很重要,所以我想弄清楚到底是什么。 我只是无法在网上找到直接的答案。
#1楼
如果您知道基本的机制,它将很有帮助。 C ++正式化了C程序员使用的一些编码技术,用“覆盖”代替了“类”-具有公共标题部分的结构将用于处理不同类型但具有一些公共数据或操作的对象。 通常,覆盖图的基本结构(公共部分)具有指向功能表的指针,该功能表针对每种对象类型指向一组不同的例程。 C ++做同样的事情,但是隐藏了机制,即C ++ ptr->func(...) ,其中func是虚拟的,就像C是(*ptr->func_table[func_num])(ptr,...) ,其中发生了什么变化派生类之间是func_table内容。 [非虚拟方法ptr-> func()仅转换为mangled_func(ptr,..)。]
这样做的结果是,您只需要了解基类即可调用派生类的方法,即,如果例程了解类A,则可以将其传递给派生类B指针,则所调用的虚拟方法将是那些B而不是A的值,因为您浏览了功能表B所指向的位置。
#2楼
您需要至少1级继承和一个低级继承来演示它。 这是一个非常简单的示例:
class Animal
{
public:
// turn the following virtual modifier on/off to see what happens
//virtual
std::string Says() { return "?"; }
};
class Dog: public Animal
{
public: std::string Says() { return "Woof"; }
};
void test()
{
Dog* d = new Dog();
Animal* a = d; // refer to Dog instance with Animal pointer
std::cout << d->Says(); // always Woof
std::cout << a->Says(); // Woof or ?, depends on virtual
}
#3楼
您必须区分重载和重载。 如果没有virtual关键字,则只能重载基类的方法。 这只不过意味着躲藏。 假设您有一个基类Base和一个派生类Specialized ,它们都实现void foo() 。 现在,您有一个指向Base的指针,该指针指向Specialized的实例。 当您在其上调用foo()时,可以观察到virtual方法的不同之处:如果该方法是虚拟方法,则将使用Specialized的实现,如果缺少该方法,则将选择Base的版本。 最佳实践是永远不要从基类重载方法。 使方法成为非虚拟方法是其作者告诉您的方法,即不打算在子类中对其进行扩展。
#4楼
如果基类是Base ,派生类是Der ,则可以有一个Base *p指针,该指针实际上指向Der的实例。 当你调用p->foo(); ,如果foo 不是虚拟的,那么将执行Base的版本,而忽略p实际上指向Der的事实。 如果foo 是虚拟的, p->foo()执行的“leafmost”覆盖foo ,充分考虑到实际类指针指向的项目。 因此,虚拟与非虚拟之间的区别实际上非常关键:前者允许运行时多态 ,即OO编程的核心概念,而后者则不允许。
#5楼
没有“虚拟”,您将获得“早期约束力”。 使用哪种方法的实现是在编译时根据调用的指针类型决定的。
使用“虚拟”,您将获得“后期绑定”。 在运行时,将根据所指向对象的类型(最初构造该对象的类型)来决定使用哪种方法。 根据指向该对象的指针的类型,这不一定是您想要的。
class Base
{
public:
void Method1 () { std::cout << "Base::Method1" << std::endl; }
virtual void Method2 () { std::cout << "Base::Method2" << std::endl; }
};
class Derived : public Base
{
public:
void Method1 () { std::cout << "Derived::Method1" << std::endl; }
void Method2 () { std::cout << "Derived::Method2" << std::endl; }
};
Base* obj = new Derived ();
// Note - constructed as Derived, but pointer stored as Base*
obj->Method1 (); // Prints "Base::Method1"
obj->Method2 (); // Prints "Derived::Method2"
编辑 -看到这个问题 。
另外- 本教程介绍了C ++中的早期和晚期绑定。