C++教程-C++虚函数vs纯虚函数
在C++中,虚函数(Virtual Function)和纯虚函数(Pure Virtual Function)之间存在一些差异。
在理解它们之间的差异之前,我们需要了解C++中的虚函数和纯虚函数。
什么是虚函数?
虚函数是在基类中声明的成员函数,可以由派生类重新定义。
通过以下示例进行理解:
#include <iostream>
using namespace std;
class base
{
public:
void show()
{
std::cout << "Base class" << std::endl;
}
};
class derived1 : public base
{
public:
void show()
{
std::cout << "Derived class 1" << std::endl;
}
};
class derived2 : public base
{
public:
void show()
{
std::cout << "Derived class 2" << std::endl;
}
};
int main()
{
base *b;
derived1 d1;
derived2 d2;
b = &d1;
b->show();
b = &d2;
b->show();
return 0;
}
在上述代码中,我们没有使用虚方法。我们创建了一个名为base的基类,其中包含show()函数。还创建了两个类derived1和derived2,它们继承了基类的属性。derived1和derived2类都对show()函数进行了重新定义。在main()方法中,声明了指针变量'b',其类型为base类。类derived1和derived2的对象分别为d1和d2。尽管'b'包含d1和d2的地址,但在调用show()方法时,它总是调用基类的show()方法,而不是调用derived1和derived2类的函数。
为了解决上述问题,我们需要在基类中将该方法声明为虚函数。在这里,虚函数意味着该方法在外观上存在,但实际上并不存在。我们可以通过在函数之前简单地添加virtual关键字将该函数声明为虚函数。在上面的程序中,我们需要将show()函数的声明修改为如下所示:
virtual void show()
{
std::cout << "Base class" << std::endl;
}
进行以上更改后,输出结果如下:
Derived class 1
Derived class 2
虚函数的重要点:
- 它是一种运行时多态性(runtime polymorphism)。
- 基类和派生类具有相同的函数名,将基类的指针赋值为派生类对象的地址时,指针将执行基类函数。
- 如果将函数声明为虚函数,那么编译器将根据指针分配给基类的地址在运行时确定要执行的函数。
什么是纯虚函数?
纯虚函数是在类中没有定义的虚函数。通过以下示例来理解纯虚函数的概念。
在上图中,shape是基类,rectangle、square和circle是派生类。由于我们没有为虚函数提供任何定义,因此它会自动转换为纯虚函数。
纯虚函数的特点:
- 纯虚函数是一个“什么也不做”的函数。这里的“什么也不做”意味着它只提供了模板,派生类实现该函数。
- 纯虚函数可以看作是一个空函数,也就是说,相对于基类而言,纯虚函数没有任何定义。
- 程序员需要在派生类中重新定义纯虚函数,因为在基类中它没有定义。
- 含有纯虚函数的类不能用来直接创建对象。这意味着如果类包含任何纯虚函数,我们就不能创建该类的对象。这种类型的类被称为抽象类(abstract class)。
语法:
有两种创建纯虚函数的方式:
virtual void display() = 0;
或者
virtual void display() {}
通过以下示例来理解:
上述代码展示了如何使用纯虚函数创建抽象类和派生类。以下是代码的解释:
#include <iostream>
using namespace std;
// 抽象类
class Shape
{
public:
// 纯虚函数
virtual float calculateArea() = 0;
};
// 正方形类,继承自抽象类Shape
class Square : public Shape
{
float a;
public:
Square(float l)
{
a = l;
}
// 实现抽象类中的纯虚函数
float calculateArea()
{
return a * a;
}
};
// 圆形类,继承自抽象类Shape
class Circle : public Shape
{
float r;
public:
Circle(float x)
{
r = x;
}
// 实现抽象类中的纯虚函数
float calculateArea()
{
return 3.14 * r * r;
}
};
// 矩形类,继承自抽象类Shape
class Rectangle : public Shape
{
float l;
float b;
public:
Rectangle(float x, float y)
{
l = x;
b = y;
}
// 实现抽象类中的纯虚函数
float calculateArea()
{
return l * b;
}
};
int main()
{
Shape *shape;
Square s(3.4);
Rectangle r(5, 6);
Circle c(7.8);
// 将shape指针指向不同的派生类对象
shape = &s;
int a1 = shape->calculateArea();
shape = &r;
int a2 = shape->calculateArea();
shape = &c;
int a3 = shape->calculateArea();
// 输出计算出的面积
std::cout << "Area of the square is " << a1 << std::endl;
std::cout << "Area of the rectangle is " << a2 << std::endl;
std::cout << "Area of the circle is " << a3 << std::endl;
return 0;
}
虚函数和纯虚函数之间的差异如下:
虚函数(Virtual Function):
- 虚函数是基类中的成员函数,可以在派生类中重新定义。
- 包含虚函数的类不是抽象类(abstract class)。
- 在虚函数中,函数的定义在基类中提供。
- 包含虚函数的基类可以被实例化。
- 如果派生类不重新定义基类的虚函数,则对编译过程没有影响。
- 所有派生类可以选择性地重新定义虚函数。
纯虚函数(Pure Virtual Function):
- 纯虚函数是类中没有定义的虚函数。
- 包含纯虚函数的类是抽象类(abstract class)。
- 在纯虚函数中,函数的声明在基类中提供,而在派生类中实现。
- 包含纯虚函数的基类成为抽象类,无法实例化。
- 如果派生类不定义基类的纯虚函数,不会引发任何错误,但派生类也成为抽象类。
- 所有派生类必须定义纯虚函数。
以上是虚函数和纯虚函数之间的一些差异。通过使用虚函数,我们可以实现多态性,并在派生类中重新定义基类的函数。而纯虚函数则允许我们创建抽象类,其中某些函数在基类中只提供了声明,而在派生类中必须实现。