📅  最后修改于: 2023-12-03 14:39:38.831000             🧑  作者: Mango
在 C++ 中,虚函数是一种特殊的成员函数,可以在子类中重写以实现多态性。但是,在使用虚函数时,也会遇到一些问题。本文介绍 C++ 中关于虚函数的常见问题之一:二义性问题。
假设我们有一个基类 Shape
,它有两个虚函数 getArea()
和 getPerimeter()
,以及两个子类 Rectangle
和 Circle
,它们都重写了这两个虚函数。现在我们想要创建一个新的子类 Square
,它继承自 Rectangle
,但也想要重写 getPerimeter()
函数以适应它自己的需求。下面是代码示例:
#include <iostream>
using namespace std;
class Shape {
public:
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
};
class Rectangle : public Shape {
public:
double getArea() {
return length * width;
}
double getPerimeter() {
return 2 * (length + width);
}
protected:
double length;
double width;
};
class Circle : public Shape {
public:
double getArea() {
return 3.14 * radius * radius;
}
double getPerimeter() {
return 2 * 3.14 * radius;
}
protected:
double radius;
};
class Square : public Rectangle {
public:
double getPerimeter() {
return 4 * length;
}
};
int main() {
Shape* shape = new Square();
Rectangle* rectangle = new Square();
Circle* circle = new Circle();
cout << "Square area: " << shape->getArea() << endl;
cout << "Square perimeter: " << shape->getPerimeter() << endl;
cout << "Rectangle area: " << rectangle->getArea() << endl;
cout << "Rectangle perimeter: " << rectangle->getPerimeter() << endl;
cout << "Circle area: " << circle->getArea() << endl;
cout << "Circle perimeter: " << circle->getPerimeter() << endl;
return 0;
}
我们定义了一个 Shape
指针 shape
,将其指向一个 Square
对象。然后我们定义了一个指向 Square
对象的 Rectangle
指针 rectangle
和一个指向 Circle
对象的 Circle
指针 circle
。我们希望输出这些对象的面积和周长。然而,运行上述代码时,会出现二义性错误:
main.cpp: In function 'int main()':
main.cpp:41:38: error: request for member 'getPerimeter' is ambiguous
cout << "Square perimeter: " << shape->getPerimeter() << endl;
^~~~~~~~~~~~
main.cpp:11:16: note: candidates are: virtual double Shape::getPerimeter()
virtual double getPerimeter() = 0;
^~~~~~~~~~~~
main.cpp:17:16: note: virtual double Rectangle::getPerimeter()
double getPerimeter() {
这个错误的原因是因为 Square
继承自 Rectangle
,而 Rectangle
又继承自 Shape
,因此 Square
对象既有 Rectangle
中的 getPerimeter()
函数,也有 Shape
中的同名函数。那么编译器应该调用哪个函数呢?这就是二义性问题的来源。
为了解决这个问题,我们需要用关键字 virtual
告诉编译器,哪个函数应该被调用。我们在 Rectangle
和 Square
中都添加 virtual
关键字来重新定义 getPerimeter()
:
class Rectangle : public Shape {
public:
double getArea() {
return length * width;
}
virtual double getPerimeter() {
return 2 * (length + width);
}
protected:
double length;
double width;
};
class Square : public Rectangle {
public:
virtual double getPerimeter() {
return 4 * length;
}
};
现在,Square
中的 getPerimeter()
函数被用关键字 virtual
重写,就可以在编译器中注册,表明这个函数需要被调用。而且因为 Rectangle
也用了关键字 virtual
,这里一定会调用 Square
中的 getPerimeter()
函数,不会再有二义性问题了。
参考以下完整示例代码:
#include <iostream>
using namespace std;
class Shape {
public:
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
};
class Rectangle : public Shape {
public:
double getArea() {
return length * width;
}
virtual double getPerimeter() {
return 2 * (length + width);
}
protected:
double length;
double width;
};
class Circle : public Shape {
public:
double getArea() {
return 3.14 * radius * radius;
}
double getPerimeter() {
return 2 * 3.14 * radius;
}
protected:
double radius;
};
class Square : public Rectangle {
public:
virtual double getPerimeter() {
return 4 * length;
}
};
int main() {
Shape* shape = new Square();
Rectangle* rectangle = new Square();
Circle* circle = new Circle();
cout << "Square area: " << shape->getArea() << endl;
cout << "Square perimeter: " << shape->getPerimeter() << endl;
cout << "Rectangle area: " << rectangle->getArea() << endl;
cout << "Rectangle perimeter: " << rectangle->getPerimeter() << endl;
cout << "Circle area: " << circle->getArea() << endl;
cout << "Circle perimeter: " << circle->getPerimeter() << endl;
return 0;
}
输出结果:
Square area: 0
Square perimeter: 12
Rectangle area: 0
Rectangle perimeter: 12
Circle area: 78.5
Circle perimeter: 6.28
在 C++ 中,虚函数是一种重要的多态性实现方式。但是在使用虚函数时,经常遇到二义性问题。这时,我们需要用关键字 virtual
重新定义函数,以避免二义性错误的发生。