我正在设计一个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
}
答案 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渲染自己,它可以渲染文本/图像/它想要的任何东西。