Answers:
从维基百科的虚拟功能 ...
在面向对象的编程中,在诸如C ++和Object Pascal之类的语言中,虚函数或虚方法是可继承且可重写的函数或方法,因此有助于进行动态调度。这个概念是面向对象编程(OOP)的(运行时)多态性部分的重要组成部分。简而言之,虚函数定义了要执行的目标函数,但是在编译时可能不知道该目标。
与非虚拟函数不同,重写虚拟函数时,派生最广泛的版本将用于类层次结构的所有级别,而不仅仅是创建它的级别。因此,如果基类的一个方法调用虚拟方法,则将使用派生类中定义的版本而不是基类中定义的版本。
这与非虚拟函数相反,非虚拟函数仍可以在派生类中重写,但“新”版本将仅由派生类及其以下版本使用,而根本不会更改基类的功能。
而..
纯虚函数或纯虚方法是如果派生类不是抽象的,则派生类需要实现的虚函数。
当存在纯虚拟方法时,该类是“抽象”的,并且无法单独实例化。而是必须使用实现纯虚拟方法的派生类。在基类中根本没有定义纯虚拟,因此派生类必须对其进行定义,否则该派生类也是抽象的,无法实例化。只能实例化没有抽象方法的类。
虚拟提供了一种重写基类功能的方法,而纯虚拟需要它。
pure
关键字,但是Bell Labs即将发布C ++的主要版本,并且他的经理在那个后期不允许这样做。添加关键字很重要。
我想评论一下Wikipedia对虚拟的定义,这里有几个人对此进行了重复。[在编写此答案时,] Wikipedia将虚拟方法定义为可以在子类中覆盖的虚拟方法。[幸运的是,此后已对Wikipedia进行了编辑,现在可以正确解释。]这是不正确的:子类中可以覆盖任何方法,而不仅仅是虚拟方法。虚函数的作用是为您提供多态性,即在运行时选择方法中最派生的覆盖的能力。
考虑以下代码:
#include <iostream>
using namespace std;
class Base {
public:
void NonVirtual() {
cout << "Base NonVirtual called.\n";
}
virtual void Virtual() {
cout << "Base Virtual called.\n";
}
};
class Derived : public Base {
public:
void NonVirtual() {
cout << "Derived NonVirtual called.\n";
}
void Virtual() {
cout << "Derived Virtual called.\n";
}
};
int main() {
Base* bBase = new Base();
Base* bDerived = new Derived();
bBase->NonVirtual();
bBase->Virtual();
bDerived->NonVirtual();
bDerived->Virtual();
}
该程序的输出是什么?
Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.
派生会覆盖Base的每种方法:不仅是虚拟方法,还包括非虚拟方法。
我们看到,当您拥有派生基指针(bDerived)时,调用NonVirtual会调用基类实现。这在编译时已解决:编译器发现bDerived是Base *,NonVirtual不是虚拟的,因此它对Base类进行解析。
但是,调用Virtual会调用Derived类实现。由于关键字为virtual,因此方法的选择在运行时而不是编译时发生。在编译时发生的情况是,编译器看到这是一个Base *,并且正在调用一个虚拟方法,因此它插入了对vtable的调用,而不是Base类。此vtable在运行时实例化,因此将运行时解析为最派生的替代。
我希望这不会太令人困惑。简而言之,任何方法都可以被覆盖,但是只有虚拟方法才能为您提供多态性,即在运行时选择最派生的覆盖。但是,实际上,重写非虚拟方法被认为是不好的做法,很少使用,因此许多人(包括撰写Wikipedia文章的人)认为只能重写虚拟方法。
Derived*
具有相同功能的调用以将指针归位可能会有所帮助。否则,很好的答案
virtual关键字使C ++具有支持多态的能力。当您拥有某个类的对象的指针时,例如:
class Animal
{
public:
virtual int GetNumberOfLegs() = 0;
};
class Duck : public Animal
{
public:
int GetNumberOfLegs() { return 2; }
};
class Horse : public Animal
{
public:
int GetNumberOfLegs() { return 4; }
};
void SomeFunction(Animal * pAnimal)
{
cout << pAnimal->GetNumberOfLegs();
}
在这个(愚蠢的)示例中,GetNumberOfLegs()函数根据被调用对象的类返回适当的数字。
现在,考虑功能“ SomeFunction”。只要它是从Animal派生的,它都不关心传递给它什么动物对象。编译器会自动将任何动物派生的类强制转换为动物,因为它是基类。
如果我们这样做:
Duck d;
SomeFunction(&d);
它会输出“ 2”。如果我们这样做:
Horse h;
SomeFunction(&h);
它会输出“ 4”。我们不能这样做:
Animal a;
SomeFunction(&a);
因为由于GetNumberOfLegs()虚拟函数是纯函数而无法编译的,这意味着它必须通过派生类(子类)来实现。
纯虚函数通常用于定义:
a)抽象类
这些是基类,您必须从中派生然后实现纯虚函数。
b)接口
这些是“空”类,其中所有功能都是纯虚拟的,因此您必须派生然后实现所有功能。
在C ++类中,virtual是关键字,它表示可以将方法重写(即由子类实现)。例如:
class Shape
{
public:
Shape();
virtual ~Shape();
std::string getName() // not overridable
{
return m_name;
}
void setName( const std::string& name ) // not overridable
{
m_name = name;
}
protected:
virtual void initShape() // overridable
{
setName("Generic Shape");
}
private:
std::string m_name;
};
在这种情况下,子类可以重写initShape函数来做一些专门的工作:
class Square : public Shape
{
public:
Square();
virtual ~Square();
protected:
virtual void initShape() // override the Shape::initShape function
{
setName("Square");
}
}
术语纯虚拟是指需要由子类实现但尚未由基类实现的虚拟功能。通过使用virtual关键字并在方法声明的末尾添加= 0,可以将方法指定为纯虚方法。
因此,如果您想使Shape :: initShape成为纯虚拟的,则可以执行以下操作:
class Shape
{
...
virtual void initShape() = 0; // pure virtual method
...
};
通过向您的类中添加纯虚拟方法,可以使该类成为抽象基类 ,这对于将接口与实现分开非常方便。
m_name
。什么m_
意思
“虚拟”表示该方法可以在子类中重写,但在基类中具有可直接调用的实现。“纯虚拟”表示它是一种虚拟方法,没有直接可调用的实现。这种方法必须在继承层次结构中至少被重写一次-如果类具有任何未实现的虚拟方法,则无法构造该类的对象,并且编译将失败。
@quark指出纯虚拟方法可以有一个实现,但是由于必须重写纯虚拟方法,因此不能直接调用默认实现。这是带有默认值的纯虚拟方法的示例:
#include <cstdio>
class A {
public:
virtual void Hello() = 0;
};
void A::Hello() {
printf("A::Hello\n");
}
class B : public A {
public:
void Hello() {
printf("B::Hello\n");
A::Hello();
}
};
int main() {
/* Prints:
B::Hello
A::Hello
*/
B b;
b.Hello();
return 0;
}
根据评论,编译是否会失败是特定于编译器的。至少在GCC 4.3.3中,它不会编译:
class A {
public:
virtual void Hello() = 0;
};
int main()
{
A a;
return 0;
}
输出:
$ g++ -c virt.cpp
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note: because the following virtual functions are pure within ‘A’:
virt.cpp:3: note: virtual void A::Hello()
虚拟关键字如何工作?
假设人是基本阶级,印第安人是人衍生的。
Class Man
{
public:
virtual void do_work()
{}
}
Class Indian : public Man
{
public:
void do_work()
{}
}
将do_work()声明为虚拟只是意味着:仅在运行时确定要调用的do_work()。
假设我愿意
Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.
如果未使用virtual,则取决于调用的对象,编译器将静态确定或静态绑定相同对象。因此,如果Man的对象调用do_work(),则Man的do_work()甚至被认为指向印度对象
我认为,投票最多的答案具有误导性-任何方法(无论虚拟方法)都可以在派生类中具有重写的实现。具体参考C ++,正确的区别是关联函数的运行时(使用虚拟时)绑定和编译时(不使用虚拟但重写方法且将基指针指向派生对象时)绑定。
似乎还有另一种误导性的评论说:
“ Justin,'pure virtual'只是一个术语(不是关键字,请参阅下面的答案),用于表示“该函数不能由基类实现。”
这是错误的!纯粹的虚拟功能也可以具有主体,并且可以实现!事实是抽象类的纯虚函数可以被静态调用!Bjarne Stroustrup和Stan Lippman是两位非常出色的作家。
纯虚函数
试试这个代码
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()=0;
};
class anotherClass:aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"hellow World";
}
};
int main()
{
//aClassWithPureVirtualFunction virtualObject;
/*
This not possible to create object of a class that contain pure virtual function
*/
anotherClass object;
object.sayHellow();
}
在类anotherClass中,删除函数sayHellow并运行代码。因为一个类包含一个纯虚函数,所以无法从该类创建任何对象,并且该对象被继承,则其派生类必须实现该函数。
虚拟功能
尝试另一个代码
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()
{
cout<<"from base\n";
}
};
class anotherClass:public aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"from derived \n";
}
};
int main()
{
aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
baseObject->sayHellow();///call base one
baseObject=new anotherClass;
baseObject->sayHellow();////call the derived one!
}
这里的sayHellow函数在基类中被标记为虚函数,它表示尝试在派生类中搜索该函数并实现该函数的编译器,如果找不到,则执行基类。