【问题标题】:Handling interactions between different derived class objects处理不同派生类对象之间的交互
【发布时间】:2020-11-16 21:41:44
【问题描述】:

我有一个程序,其中有一个指向基类的指针向量,其中存储了多个不同派生类的实例。所有这些对象根据它们的类型以不同的方式相互交互,我想以一种智能的方式实现这些交互。我希望有一个中介类来尽可能多地保留与这些交互相关的代码。

这是我想要实现的简化版本:

#include <iostream>
#include <vector>

class Gesture {};

class Rock : public Gesture {};

class Paper : public Gesture {};

class Scissors : public Gesture {};

class Game {
    public:
        static void play(Rock* first, Rock* second) {
            std::cout << "Rock ties with rock." << std::endl;
        }
        static void play(Rock* first, Paper* second) {
            std::cout << "Rock is beaten by paper." << std::endl;
        }
        static void play(Rock* first, Scissors* second) {
            std::cout << "Rock beats scissors." << std::endl;
        }
        static void play(Paper* first, Rock* second) {
            std::cout << "Paper beats rock." << std::endl;
        }
        static void play(Paper* first, Paper* second) {
            std::cout << "Paper ties with paper." << std::endl;
        }
        static void play(Paper* first, Scissors* second) {
            std::cout << "Paper is beaten by scissors." << std::endl;
        }
        static void play(Scissors* first, Rock* second) {
            std::cout << "Scissors are beaten by rock." << std::endl;
        }
        static void play(Scissors* first, Paper* second) {
            std::cout << "Scissors beat paper." << std::endl;
        }
        static void play(Scissors* first, Scissors* second) {
            std::cout << "Scissors tie with scissors." << std::endl;
        }
};

int main()
{   
    Rock rock;
    Paper paper;
    Scissors scissors;
    
    std::vector<Gesture*> gestures;
    gestures.push_back(&rock);
    gestures.push_back(&paper);
    gestures.push_back(&scissors);
    
    for(int i = 0; i < 3; ++i) {
        for(int j = 0; j < 3; ++j) {
            // alas, downcasting doesn't happen automagically...
            Game::play(gestures[i], gestures[j]);
        }
    }

    return 0;
}

有没有一种简单的方法来实现向下转换,而无需编译时知道哪些派生类有问题?这个问题的公认答案:Polymorphism and get object type in C++ 似乎解决了我的问题,但我想知道是否可以避免更改派生类(如果可能,我想将相关代码收集到中介类中)。我想我可以通过 switch 语句找出派生类,该语句尝试向下转换到所有可能的派生类,并检查哪个不返回 nullptr,但这似乎不是特别“聪明”。

有什么想法吗?

【问题讨论】:

    标签: c++ inheritance downcast mediator


    【解决方案1】:

    我对这种情况的典型反应是在enum 类型的基类中添加一个字段。

    typedef enum {rockType,paperType,scissorsType} fred;
    
    class Gesture
    {
        virtual fred Type() = 0;
    }
    
    class Rock : public Gesture
    {
        fred Type() { return typeRock;}
    }
    

    等等。然后你可以调用每个对象的Type() 函数并使用switch 语句来调度每个对象。

    【讨论】:

    • 你是说fred Type() { return typeRock;}
    • 嗯,在课程中介绍enums 并不太具有侵略性——这很好。但我不喜欢嵌套switch 语句的想法。实际上,我目前有五个不同的派生类(不,另外两个不是“Lizard”和“Spock”​​),这将导致相当长的switch 声明。因此,虽然这是对我的 C 计划的改进,但我仍然会选择 B 计划 (Polymorphism and get object type in C++)。
    • 没关系,我要么没有阅读,要么没有充分考虑链接背后的描述——它并不能完全解决我的问题。编辑:我的意思是我仍然需要一个 switch 声明或其他东西。
    【解决方案2】:

    由于我似乎无法避免使用丑陋的switch 语句或if-else if 的链,我想我将只做类似以下的事情,我不需要介绍@ 987654324@s 到类或以其他方式修改它们。

    #include <iostream>
    #include <vector>
    
    class Gesture {
        virtual void foo() {}
    };
    
    class Rock : public Gesture {
        virtual void foo() {}
    };
    
    class Paper : public Gesture {
        virtual void foo() {}
    };
    
    class Scissors : public Gesture {
        virtual void foo() {}
    };
    
    class Game
    {
        public:
            static void rock_vs_rock() {
                std::cout << "Rock ties with rock." << std::endl;
            }
            static void rock_vs_paper() {
                std::cout << "Rock is beaten by paper." << std::endl;
            }
            static void rock_vs_scissors() {
                std::cout << "Rock beats scissors." << std::endl;
            }
            static void paper_vs_rock() {
                std::cout << "Paper beats rock." << std::endl;
            }
            static void paper_vs_paper() {
                std::cout << "Paper ties with paper." << std::endl;
            }
            static void paper_vs_scissors() {
                std::cout << "Paper is beaten by scissors." << std::endl;
            }
            static void scissors_vs_rock() {
                std::cout << "Scissors are beaten by rock." << std::endl;
            }
            static void scissors_vs_paper() {
                std::cout << "Scissors beat paper." << std::endl;
            }
            static void scissors_vs_scissors() {
                std::cout << "Scissors tie with scissors." << std::endl;
            }
            static void play(Gesture* first, Gesture* second)
            {
                // I've never really liked switch statements...
                if(dynamic_cast<Rock*>(first) && dynamic_cast<Rock*>(second))
                {
                    rock_vs_rock();
                }
                else if(dynamic_cast<Rock*>(first) && dynamic_cast<Paper*>(second))
                {
                    rock_vs_paper();
                }
                else if(dynamic_cast<Rock*>(first) && dynamic_cast<Scissors*>(second))
                {
                    rock_vs_scissors();
                }
                else if(dynamic_cast<Paper*>(first) && dynamic_cast<Rock*>(second))
                {
                    rock_vs_rock();
                }
                else if(dynamic_cast<Paper*>(first) && dynamic_cast<Paper*>(second))
                {
                    paper_vs_paper();
                }
                else if(dynamic_cast<Paper*>(first) && dynamic_cast<Scissors*>(second))
                {
                    paper_vs_scissors();
                }
                else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Rock*>(second))
                {
                    scissors_vs_rock();
                }
                else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Paper*>(second))
                {
                    scissors_vs_paper();
                }
                else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Scissors*>(second))
                {
                    scissors_vs_scissors();
                }
            }
    };
    
    int main()
    {   
        Rock rock;
        Paper paper;
        Scissors scissors;
        
        std::vector<Gesture*> gestures;
        gestures.push_back(&rock);
        gestures.push_back(&paper);
        gestures.push_back(&scissors);
        
        for(int i = 0; i < gestures.size(); ++i)
        {
            for(int j = 0; j < gestures.size(); ++j)
            {
                Game::play(gestures[i], gestures[j]);
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 当然,如果这是性能关键部分,那么检查enums 肯定会比dynamic_casts 快得多。也可以将std::vector&lt;std::pair&lt;enum Gesture_type, Gesture*&gt;&gt; gestures 作为enum 成员的替代品。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多