C++虚拟函数
- 虚函数基本概念:虚函数是基类中的成员函数,预期在派生类中重新定义。当基类指针指向派生类对象时,虚函数确保调用的是派生类中的函数。
- 声明和使用虚函数:在基类中使用
virtual
关键字声明虚函数。这使得派生类可以覆盖基类中的同名函数,实现多态。 - override标识符:C++11新增了
override
标识符,用于明确指出派生类中的函数覆盖了基类的虚函数。这有助于防止在派生类中不正确声明虚函数时产生的错误。
虚函数是基类中的成员函数,我们期望在派生类中重新定义。
基本上,虚函数是在基类中用来确保该函数被覆盖的。这尤其适用于基类的指针指向派生类的对象的情况。
例如,考虑以下代码:
class Base {
public:
void print() {
// 代码
}
};
class Derived : public Base {
public:
void print() {
// 代码
}
};
后来,如果我们创建一个 Base
类型的指针指向 Derived
类的对象并调用 print()
函数,它会调用 Base
类的 print()
函数。
换句话说,Base
的成员函数没有被覆盖。
int main() {
Derived derived1;
Base* base1 = &derived1;
// 调用 Base 类的函数
base1->print();
return 0;
}
为了避免这种情况,我们使用 virtual
关键字声明 Base
类的 print()
函数为虚函数。
class Base {
public:
virtual void print() {
// 代码
}
};
虚函数是 C++ 多态性的一个不可或缺的部分。要了解更多,请查看我们的教程 C++ 多态性。
示例 1:C++ 虚函数
#include <iostream>
using namespace std;
class Base {
public:
virtual void print() {
cout << "基类函数" << endl;
}
};
class Derived : public Base {
public:
void print() {
cout << "派生类函数" << endl;
}
};
int main() {
Derived derived1;
// 创建 Base 类型的指针指向 derived1
Base* base1 = &derived1;
// 调用派生类的成员函数
base1->print();
return 0;
}
输出
派生类函数
这里,我们已将 Base
的 print()
函数声明为 virtual
。
所以,即使我们使用指向 Derived
对象 derived1 的 Base
类型的指针,这个函数也会被覆盖。
C++ override 标识符
C++ 11 为我们提供了一个新的标识符 override
,在使用虚函数时非常有用,以避免出现错误。
这个标识符指定派生类的成员函数覆盖基类的成员函数。
例如,
class Base {
public:
virtual void print() {
// 代码
}
};
class Derived : public Base {
public:
void print() override {
// 代码
}
};
如果我们在 Derived
类中使用函数原型并在类外定义该函数,则使用以下代码:
class Derived : public Base {
public:
// 函数原型
void print() override;
};
// 函数定义
void Derived::print() {
// 代码
}
C++ override 的使用
在使用虚函数时,声明派生类的成员函数时可能会出错。
使用 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 << "动物: " << ani->getType() << endl;
}
这将使代码更简短、干净和重复性更少。
示例 2:C++ 虚函数演示
// C++ 程序演示虚函数的使用
#include <iostream>
#include <string>
using namespace std;
class Animal {
private:
string type;
public:
// 构造函数初始化 type
Animal() : type("Animal") {}
// 声明虚函数
virtual string getType() {
return type;
}
};
class Dog : public Animal {
private:
string type;
public:
// 构造函数初始化 type
Dog() : type("Dog") {}
string getType() override {
return type;
}
};
class Cat : public Animal {
private:
string type;
public:
// 构造函数初始化 type
Cat() : type("Cat") {}
string getType() override {
return type;
}
};
void print(Animal* ani) {
cout << "动物: " << 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
动物: Dog
动物: Cat
在这里,我们使用了虚函数 getType()
和一个 Animal
指针 ani 来避免在每个类中重复 print()
函数。
void print(Animal* ani) {
cout << "动物: " << ani->getType() << endl;
}
在 main()
中,我们创建了 3 个 Animal
指针动态创建 Animal
、Dog
和 Cat
类的对象。
// 使用 Animal 指针动态创建对象
Animal* animal1 = new Animal();
Animal* dog1 = new Dog();
Animal* cat1 = new Cat();
接着,我们使用这些指针调用 print()
函数:
- 当调用
print(animal1)
时,指针指向一个Animal
对象。所以,Animal
类中的虚函数在print()
内部执行。 - 当调用
print(dog1)
时,指针指向一个Dog
对象。因此,虚函数被重写,Dog
的函数在print()
内部执行。 - 当调用
print(cat1)
时,指针指向一个Cat
对象。因此,虚函数被重写,Cat
的函数在print()
内部执行。