在GUI设计中避免dynamic_cast
Avoiding dynamic_cast in GUI design
我正在设计一个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
}
我认为您不需要一个容纳所有小部件的容器。这些类可以保存一个指针,指向它们需要使用的小部件的具体实例。您可以将指针作为构造函数上的参数进行传递;另一种选择是使用依赖注入(例如wallaroo可以用于您的问题)。
要扩展我的评论…
根据您的代码,我想您打算让我为添加到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);
}
};
我到处都读到RTTI不好,应该避免
我怀疑你在任何地方都读过,因为那纯粹是胡说八道。
RTTI是一个非常好的功能。尽管这肯定应该避免,因为有更好的工具。一个好的层次结构被设计为可以通过虚拟函数访问基类接口。在这种情况下,您不需要任何强制转换,只需调用虚拟函数,它就会做正确的事情。
即使您的GetText也可能是一个不错的候选者,默认实现返回一个空字符串。也许是在一个功能查询工具的公司,报告实际文本的存在。因此,大多数客户端可以直接调用并对空字符串感到满意,而其他客户端可能会进行检查。
那些对一些罕见的接口感兴趣的人,只需要一个具体的类,就可以永远调用dynamic_cast。收藏最好保持简单,仅限于收藏。也许可以为真正常见的Widget族添加一些特殊的窗体。
这里的问题是您的设计是向后的。您想要渲染一个Widget,但该Widget没有具体的可渲染功能。您必须要求Widget呈现自己,在那里它可以呈现文本/图像/任何它想要的东西。
- 如何理解C++标准N3337中的expr.const.cast子句8
- C++Cast运算符过载
- 在成员dynamic_bitset上使用 boost::from_block_range 时出错,但在本地dynamic
- C++类中的二维"dynamic"数组?
- 错误:"cast"未命名类型void setCastDescription(std::string
- 通过使用 const-cast 的非常量引用来延长临时的寿命
- "(void) cast"与功能有什么区别 "__attributes__"来沉默未使用的参数警告?
- protobuf in C++ with dynamic binding for google::protobuf::M
- 警告的原因是什么:"when type is in parentheses, array cannot have dynamic size"?
- C++:"Expected '(' for function-style cast or type construction"错误
- 为什么选择 g++ 给予者:"error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]"
- CUDA 错误:"dynamic initialization is not supported for __device__, __constant__ and __shared__ variabl
- Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误
- 如何修复'The procedure entry point SDL_RWclose could not be located in the dynamic link library'
- Shared_ptr cast vs static_cast speed
- 在 iOS 上使用 Aruco 构建 OpenCV 时"Functional-style cast from id to double is not allowed"
- "The ordinal 344 could not be located in the dynamic link library"
- 覆盖 CAST 运算符(我认为它被称为向下转换)
- Dynamic Cast C++ Fail
- dynamic-cast-c++dynamic_cast错误处理