如何使用继承的 类 中的多态函数参数避免 C++ 中的类型转换和 typeid

How do I avoid type casting and typeid in c++ with polymorphic function arguments from inherited classes

我有一个抽象形状 class,它扩展了多个实际形状实现。需要计算不同形状的重叠。起初我想用简单的多态函数参数来做到这一点,如代码片段 1 所示。

#include <iostream>
#include <vector>
#include <typeinfo>
#include <string>

class Shape {
    public:
        virtual void getOverlap(Shape *obj) = 0;
    };

    class Square : public Shape {
        class Circle;
    public:
        virtual void getOverlap(Circle *obj) 
             { std::cout << "Square overlap with Circle" << std::endl; }    
        virtual void getOverlap(Square *obj) 
             { std::cout << "Square overlap with Square" << std::endl; }
};

class Circle : public Shape {
    class Square;
    public:
        virtual void getOverlap(Circle *obj)
            { std::cout << "Circle overlap with Circle" << std::endl; }
        virtual void getOverlap(Square *obj) 
            { std::cout << "Circle overlap with Square" << std::endl; }
};

int main() {
    std::vector<Shape*> shapes = { new Square, new Circle };
    shapes[0]->getOverlap(shapes[1]);
    shapes[1]->getOverlap(shapes[1]);
}

这当然不会编译,因为继承的 classes 不实现虚函数,而是尝试以派生的 classes 作为参数实现虚函数。不过,我希望它能说明我的意图。

第二个代码片段中显示了我在找不到适合此问题的答案后想到实施的解决方法。

#include <iostream>
#include <vector>
#include <typeinfo>
#include <string>

class Shape {
public:
    virtual void getOverlap(Shape *obj) = 0;
};

class Square : public Shape {
    class Circle;
public:
    virtual void getOverlap(Shape *obj) {
        string className = typeid(*obj).name();
        if (className.compare("class Circle") == 0){
            getOverlap((Circle*)obj);
        }
        else if (className.compare("class Square") == 0) {
            getOverlap((Square*)obj);
        }
    }

private:
    void getOverlap(Circle *obj) 
        {   std::cout << "Square overlap with Circle" << std::endl; }
    void getOverlap(Square *obj) 
        { std::cout << "Square overlap with Square" << std::endl; }
};

class Circle : public Shape {
    class Square;
public:
    virtual void getOverlap(Shape *obj) {
        string className = typeid(*obj).name();
        if (className.compare("class Circle") == 0) {
            getOverlap((Circle*)obj);
        }
        else if (className.compare("class Square") == 0) {
            getOverlap((Square*)obj);
        }
    }

private:
    void getOverlap(Circle *obj) 
         { std::cout << "Circle overlap with Circle" << std::endl; }
    void getOverlap(Square *obj) 
         { std::cout << "Circle overlap with Square" << std::endl; }
};

int main() {
    std::vector<Shape*> shapes = { new Square, new Circle };
    shapes[0]->getOverlap(shapes[1]);
    shapes[1]->getOverlap(shapes[1]);
}

我非常不喜欢对 class 的 typeid 的特定请求以及向正确指针类型的显式转换,因为对象原则上已经是正确的类型。

使用c++的多态性,这样做的好方法是什么。

您要实现的目标称为 double dispatch, and the object-oriented way to implement it is the Visitor pattern

在你的情况下,它看起来有点像这样:

class Shape
{
   public:
     virtual void getOverlap(Shape* s)=0;

     virtual void getOverlapCircle(Circle* c)=0;
     virtual void getOverlapSquare(Square* s)=0;
};

class Square : public Shape
{
   public:
     virtual void getOverlap(Shape* s)
     {
         s->getOverlapSquare(this);
     }

     virtual void getOverlapSquare(Square* s)
     {
        // code for overlapping 2 squares
     }

     virtual void getOverlapCircle(Circle* c)
     {
        // code for overlapping a circle & a square
     }
};

Circle class 显然也是如此。

这是教科书的默认方法;注意它不需要任何强制转换。

但我个人觉得它非常难看,因为它完全没有任何重叠形状的对称概念,而且不容易扩展。

然而,更好的技术需要进行一些低级别的调整,直到您自己重新实现整个 vtable 机制。