📜  C ++ |虚函数|问题9(1)

📅  最后修改于: 2023-12-03 14:39:38.831000             🧑  作者: Mango

C++ | 虚函数 | 问题9

在 C++ 中,虚函数是一种特殊的成员函数,可以在子类中重写以实现多态性。但是,在使用虚函数时,也会遇到一些问题。本文介绍 C++ 中关于虚函数的常见问题之一:二义性问题。

问题描述

假设我们有一个基类 Shape,它有两个虚函数 getArea()getPerimeter(),以及两个子类 RectangleCircle,它们都重写了这两个虚函数。现在我们想要创建一个新的子类 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 告诉编译器,哪个函数应该被调用。我们在 RectangleSquare 中都添加 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 重新定义函数,以避免二义性错误的发生。