C++ 虚拟函数
在本教程中,我们将通过示例了解 C++ 虚函数及其使用。
虚函数是我们期望在派生类中重新定义的基类中的成员函数。
基本上,在基类中使用虚函数以确保函数被派生类覆盖。这尤其适用于基类指针指向派生类对象的情况。
例如,考虑下面的代码:
class Base {
public:
void print() {
// code
}
};
class Derived : public Base {
public:
void print() {
// code
}
};
之后,如果我们创建一个 Base
类型的指针指向 Derived
类的对象并调用 print()
函数,它就会调用 Base
类的 print()
函数。
换句话说, Base
的成员函数没有被覆盖。
int main() {
Derived derived1;
Base* base1 = &derived1;
// calls function of Base class
base1->print();
return 0;
}
为了避免这种情况,我们使用关键字 virtual
将 Base
类的 print()
函数声明为虚函数。
class Base {
public:
virtual void print() {
// code
}
};
虚函数是 C++ 中多态性的一个组成部分。
示例 1:C++ 虚函数
#include <iostream>
using namespace std;
class Base {
public:
virtual void print() {
cout << "Base Function" << endl;
}
};
class Derived : public Base {
public:
void print() {
cout << "Derived Function" << endl;
}
};
int main() {
Derived derived1;
// pointer of Base type that points to derived1
Base* base1 = &derived1;
// calls member function of Derived class
base1->print();
return 0;
}
输出
Derived Function
在这里,我们已经在 Base
类中声明了 virtual
函数 print()
。
因此,即使我们使用指向 Derived
对象的 Base
类型指针,此函数也会被 derived1
覆盖。
C++ override
标识符
C++ 11 为我们提供了一个新标识符 override
,它对于避免使用虚函数时出现错误非常有用。
该标识符指定了派生类的成员函数覆盖了基类的成员函数。
例如,
class Base {
public:
virtual void print() {
// code
}
};
class Derived : public Base {
public:
void print() override {
// code
}
};
如果我们在 Derived
类中使用函数原型并在类外定义该函数,那么我们使用以下代码:
class Derived : public Base {
public:
// function prototype
void print() override;
};
// function definition
void Derived::print() {
// code
}
使用 C++ 覆盖
使用虚函数时,在声明派生类的成员函数时可能会出错。
当出现这些错误时,使用 override
标识符会提示编译器显示错误消息。
否则,程序将简单地编译但不会覆盖虚函数。
其中一些可能的错误是:
- 名称不正确的函数: 例如,如果基类中的虚函数名为
print()
,但我们不小心将派生类中的覆盖函数命名为pint()
。 - 具有不同返回类型的函数: 如果虚函数是
void
类型,但派生类中的函数是int
类型。 - 具有不同参数的函数: 如果虚函数的参数与派生类中的函数不匹配。
- 基类中没有声明虚函数。
C++ 虚函数的使用
假设我们有一个基类 Animal
以及派生类 Dog
和 Cat
.
假设每个类都有一个名为 type
的成员变量,假设这些变量是通过它们各自的构造函数初始化的。
class Animal {
private:
string type;
... .. ...
public:
Animal(): type("Animal") {}
... .. ...
};
class Dog : public Animal {
private:
string type;
... .. ...
public:
Animal(): type("Dog") {}
... .. ...
};
class Cat : public Animal {
private:
string type;
... .. ...
public:
Animal(): type("Cat") {}
... .. ...
};
现在,让我们假设我们的程序要求我们为每个类创建两个 public
函数:
getType()
返回type
的值print()
打印type
的值
我们可以在每个类中分别创建这两个函数并覆盖它们,这将是漫长而乏味的。
或者我们可以在 Animal
类中创建虚拟函数 getType()
,然后创建一个单独的 print()
函数,该函数接受一个 Animal
类型指针作为其参数。然后我们可以使用这个单一的函数来覆盖虚函数。
class Animal {
... .. ...
public:
... .. ...
virtual string getType {...}
};
... .. ...
... .. ...
void print(Animal* ani) {
cout << "Animal: " << ani->getType() << endl;
}
这将使代码更短、更清晰、重复更少。
示例 2:C++ 虚拟函数演示
#include <iostream>
#include <string>
using namespace std;
class Animal {
private:
string type;
public:
// constructor to initialize type
Animal() : type("Animal") {}
// declare virtual function
virtual string getType() {
return type;
}
};
class Dog : public Animal {
private:
string type;
public:
// constructor to initialize type
Dog() : type("Dog") {}
string getType() override {
return type;
}
};
class Cat : public Animal {
private:
string type;
public:
// constructor to initialize type
Cat() : type("Cat") {}
string getType() override {
return type;
}
};
void print(Animal* ani) {
cout << "Animal: " << ani->getType() << endl;
}
int main() {
Animal* animal1 = new Animal();
Animal* dog1 = new Dog();
Animal* cat1 = new Cat();
print(animal1);
print(dog1);
print(cat1);
return 0;
}
输出
Animal: Animal
Animal: Dog
Animal: Cat
在这里,我们使用了虚函数 getType()
和一个 Animal
指针 ani
以避免在每个类中重复定义 print()
函数。
void print(Animal* ani) {
cout << "Animal: " << ani->getType() << endl;
}
在 main()
,我们已经创建了 3 个 Animal
指针,以动态创建 Animal
, Dog
和 Cat
类的对象。
// dynamically create objects using Animal pointers
Animal* animal1 = new Animal();
Animal* dog1 = new Dog();
Animal* cat1 = new Cat();
然后我们使用这些指针调用 print()
函数:
- 当
print(animal1)
被调用时,指针指向一个Animal
对象。因此, 在print()
函数中执行了Animal
类中的虚函数. - 当
print(dog1)
被调用时,指针指向一个Dog
对象。因此,虚函数被Dog
覆盖。 - 当
print(cat1)
被调用时,指针指向一个Cat
对象。因此,虚函数被Cat
覆盖。