我犹豫要问这个问题,因为它很简单。除了我看不到解决方案。
我最近尝试编写一个简单的程序,该程序在某种程度上忽略了引擎呈现其UI的方式。
一切在纸面上看起来都很棒,但是实际上,理论并没有使我走得太远。
假设我的工具关心使用IWindow
和IContainer
托管一个ILabel
和IButton
。这是4个UI元素。放弃其中的每一项都是一项琐碎的任务。我可以使用Qt,Gtk和主题创建这些元素中的每一个-命名。
我知道,为了使实现(例如带有QtWindow
的QtContainer
)起作用,抽象(IWindow
和IContainer
)也必须起作用:IWindow
需要能够接受IContainer
作为其子元素:
那是完全解决抽象问题的理论。实践(或实施)是另外一回事。为了使实现与抽象一起工作-以我的方式看,我可以
dynamic_cast<>()
)。 ISomething
)添加一个map<IElement*, QtElement*>()
实例的全局映射池,这有点像强制转换,但由我自己完成。 所有这些看起来都很丑陋。我在这里看不到其他选择-这是数据抽象概念实际上失败的地方吗?铸造是这里唯一的选择吗?
编辑
我花了一些时间尝试提出最佳解决方案,而且似乎这是用C++不能简单完成的事情。不是没有转换,也没有模板。
我最终想出的解决方案(在弄乱了很多接口(interface)以及如何定义接口(interface)之后)如下所示:
1.需要一个参数化的基本接口(interface)来定义调用
基本接口(interface)(将其称为Container的
TContainerBase
和元素的TElementBase
)指定了预期由容器或元素实现的方法。这部分很简单。该定义需要遵循以下内容:
template <typename Parent>
class TElementBase : public Parent {
virtual void DoSomething() = 0;
};
template <typename Parent>
class TContainerBase : public Parent {
virtual void AddElement(TElementBase<Parent>* element) = 0;
};
2.必须有一个指定继承的模板。
这就是分离的第一阶段(引擎与用户界面)。在这一点上,什么类型的后端驱动渲染都无关紧要。这是有趣的部分:就我而言,成功实现此功能的唯一语言是Java。模板必须遵循以下原则:
一般:
template<typename Engine>
class TContainer : public TContainerBase<Parent> {
void AddElement(TElementBase<Parent>* element) {
// ...
}
};
template<typename Engine>
class TElement : public TElementBase<Parent> {
void DoSomething() {
// ...
}
};
3. UI需要能够仅接受
TContainers
或TElements
也就是说,将不得不忽略这些元素的来源。那是分离的第二阶段。毕竟,它关心的只是
TElementBase
和TContainerBase
接口(interface)。在Java中已通过引入问号解决了。就我而言,我可以在UI中简单使用:TContainer<?> some_container;
TElement<?> some_element;
container.AddElement(&element);
vtable中的虚拟函数调用没有问题,因为它们正是编译器期望的位置。唯一的问题是在这两种情况下确保模板参数相同。假设后端是单个库-可以正常工作。
以上三个步骤使我可以完全(和安全地)不考虑后端地编写代码,而后端可以实现几乎任何需要的东西。
我尝试了这种方法,结果变得非常理智。唯一的限制是编译器。实例化类并在此处来回转换它们是违反直觉的,但不幸的是,这是必需的,主要是因为使用模板继承,您不能仅提取基类本身,也就是说,您不能说以下任何一种:
class IContainerBase {};
template <typename Parent>
class TContainerBase : public (IContainerBase : public Parent) {}
也不
class IContainerBase {};
template <typename Parent>
typedef class IContainerBase : public Parent TContainerBase;
(请注意,在上述所有解决方案中,仅依靠
TElementBase
和TContainerBase
感觉非常自然和理智-如果将TElementBase<Foo>
转换为TElementBase<Bar>
,则生成的代码可以很好地工作-因此,这只是语言限制)。无论如何,这些最终语句(从B继承的A类的typedef和从B继承的基类A的X类的typedef)只是C++中的垃圾(并且会使该语言变得比原来更难),因此,唯一的出路是遵循我非常感谢提供的解决方案之一。
多谢您的协助。
最佳答案
您正在尝试在此处使用“对象定向”。 OO有一种实现通用代码的特殊方法:按类型擦除。 IWindow
基类接口(interface)删除确切的类型,在您的示例中为QtWindow
。在C++中,您可以通过RTTI获取一些已删除的类型信息,即dynamic_cast
。
但是,在C++中,您也可以使用模板。不实现IWindow
和QtWindow
,但实现Window<Qt>
。这使您可以声明Container<Foo>
接受任何可能的Window<Foo>
窗口库的Foo
。编译器将强制执行此操作。
关于c++ - 真正允许在C++中将实现与用户隔离的数据抽象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33250148/