在GUI设计中避免使用dynamic_cast

时间:2013-06-29 07:58:05

标签: c++ oop rtti dynamic-cast

我正在设计一个GUI,但我遇到了问题,因为我无法避免使用dynamic_casting。

我的课程:

  class Widget; //base class for all widgets

  class Container //contains widgets
  {
     std::map<std::string, Widget*> m_widgets;
     public:
     template <class T> T* get(const std::string &name)
     {
         return dynamic_cast<T*>(m_widgets.at(name)); //I need casting here
     }
   }

如何避免动态投射?我无法承担每个窗口小部件类型的容器,因为我的GUI必须使用用户定义的窗口小部件。此外,我必须为每个小部件都有一个容器,这样用户就不必自己存储小部件。

为什么我需要施法?

 class TextBox : public Widget
 { 
      public:
      std::string getText(); //I can't have it in Widget class, because it's object-specific
      //also, my gui must work with user-defined widgets so I can't provide 
      //empty virtual functions for everything in Widget
 }

4 个答案:

答案 0 :(得分:3)

我认为您不需要容纳所有小部件的容器。这些类可以包含指向他们需要使用的窗口小部件的具体实例的指针。您可以将指针作为参数传递给构造函数;另一个选择是使用依赖注入(例如wallaroo可以用于您的问题)。

答案 1 :(得分:2)

扩展我的评论......

根据你的代码,我想你打算让我为每个添加到Container的子窗口小部件指定一个字符串名称,然后再按名称访问子窗口小部件。所以你希望我写下这样的东西:

class LoginController {

    Container *container;
    static const char *kUsernameKey = "username";
    static const char *kPasswordKey = "password";

public:

    LoginController() :
        container(new Container())
    {
        container->addChild(kUsernameKey, new TextBox());
        container->addChild(kPasswordKey, new TextBox());
        container->addChild("button", new Button("Log In"));
        container->get<Button>("button")->setAction([](){
            this->login();
        })
    }

    void login() {
        string username = container->get<TextBox>(kUsernameKey)->getText();
        string password = container->get<TextBox>(kPasswordKey)->getText();
        sendLoginRequest(username, password);
    }

};

以这种方式设计Container会在运行时进行查找和类型检查,但这些查找和类型检查可以在编译时完成。

相反,设计API以便在我自己的变量中保留我自己的,特定类型的对子项的引用。查找子项只是变得使用变量,不需要进行转换。查找和类型检查在编译时完成。代码如下所示:

class LoginController {

    Container *container;
    TextBox *usernameBox;
    TextBox *passwordBox;

public:

    LoginController() :
        container(new Container()),
        usernameBox(new TextBox()),
        passwordBox(new TextBox())
    {
        container->addChild(username);
        container->addChild(password);

        Button *button = new Button("Log In");
        container->addChild(button);
        button->setAction([](){
            this->login();
        })
    }

    void login() {
        string username = usernameBox->getText();
        string password = passwordBox->getText();
        sendLoginRequest(username, password);
    }

};

答案 2 :(得分:1)

  

我到处都读到RTTI很糟糕,应该避免

我怀疑你到处读那个因为那是纯粹的废话。

RTTI是一个非常好的功能。虽然绝对应该避免哪里有更好的工具。一个好的层次结构被设计为只能通过虚函数访问基类接口。在这些情况下,你不需要任何演员表,只需调用虚函数,它就会做正确的事情。

即使你的GetText可能是一个公平的候选人,默认实现返回一个空字符串。可能在功能查询工具的公司中报告实际文本的存在。所以大多数客户都可以打电话给最后满意的空字符串,其他人可能会做检查。

那些对一些罕见界面感兴趣的人只需要一个具体的类就可以调用dynamic_cast。收集更好只是保持简单,限制收集。也许为真正常见的Widget系列添加一些特殊形式。

答案 3 :(得分:0)

这里的问题是你的设计是倒退的。您想渲染一个Widget,但Widget没有具体的可渲染功能。你必须要求Widget渲染自己,它可以渲染文本/图像/它想要的任何东西。