void* 指针的C++替代品(不是模板)
C++ alternatives to void* pointers (that isn't templates)
看起来我对C++有一个根本的误解:<
我喜欢多态容器解决方案。谢谢你,引起我的注意:)
因此,我们需要创建一个相对通用的容器类型对象。它还恰好封装了一些与业务相关的逻辑。然而,我们需要在这个容器中存储本质上任意的数据——从原始数据类型到复杂类的所有数据。
因此,人们会立即想到模板类的概念并完成它。然而,我注意到C++多态性和模板不能很好地结合在一起。由于我们必须处理一些复杂的逻辑,我宁愿只使用模板或多态性,而不是试图通过同时使用这两种方法来对抗C++。
最后,考虑到我想做其中一个,我更喜欢多态性。我发现,像"这个容器包含可比较的类型"这样的约束表示起来要容易得多——一个la java。
让我来谈谈这个问题:在最抽象的情况下,我想象我可以有一个"Container"纯虚拟接口,它类似于"push(void*data)和pop(void*data)"(记录在案,我实际上并没有试图实现堆栈)。
然而,我并不喜欢顶级的void*,更不用说每次我想为具体容器可以使用的数据类型添加约束时,签名都会发生变化。
总结:我们有相对复杂的容器,它们有各种检索元素的方法。我们希望能够改变对可以进入容器的元素的约束。元素应该与多种容器一起使用(只要它们满足特定容器的约束)。
编辑:我还应该提到容器本身需要是多态的。这是我不想使用模板化C++的主要原因。
那么,我应该放弃对Java类型接口的热爱,转而使用模板吗?我应该使用void*并静态投射所有内容吗?或者我应该使用一个空的类定义"Element",它什么都不声明,并将其用作"Element"层次结构中的顶级类?
我喜欢堆栈溢出的原因之一是,许多响应提供了一些我甚至没有考虑过的其他方法的有趣见解。因此,提前感谢您的见解和评论。
如果您将真正任意的数据存储到容器中,则可以考虑使用boost::any的标准容器。
听起来更像是你想要一个类似boost::ptr_container的东西,其中可以存储在容器中的任何东西都必须从某个基类型派生,而容器本身只能为你提供对基类型的引用。
简单的事情是定义一个名为Container
的抽象基类,并为您可能希望存储的每种项目将其子类化。然后,您可以使用任何标准集合类(std::vector
、std::list
等)来存储指向Container
的指针。请记住,由于要存储指针,因此必须处理它们的分配/释放。
然而,您需要一个单独的集合来存储如此不同类型的对象,这表明您的应用程序的设计可能存在问题。在实现这个超级通用容器之前,最好重新访问业务逻辑。
如果使用得当,多态性和模板在一起确实很好。
无论如何,我理解您希望在每个容器实例中只存储一种类型的对象。如果是,请使用模板。这将防止您错误地存储错误的对象类型。
至于容器接口:根据您的设计,也许您也可以将它们模板化,然后它们将具有类似void push(T* new_element)
的方法。当你想把对象添加到一个容器(未知类型)中时,想想你对它的了解。这个物体最初从哪里来?返回void*
的函数?你知道它会是可比的吗?至少,如果所有存储的对象类都在代码中定义,那么可以让它们都继承自一个共同的祖先,比如Storable
,并使用Storable*
而不是void*
。
现在,如果您看到对象总是通过像void push(Storable* new_element)
这样的方法添加到容器中,那么将容器作为模板实际上不会有任何附加值。但你会知道它应该存储Storables。
你能不能没有一个包含元素的根容器类:
template <typename T>
class Container
{
public:
// You'll likely want to use shared_ptr<T> instead.
virtual void push(T *element) = 0;
virtual T *pop() = 0;
virtual void InvokeSomeMethodOnAllItems() = 0;
};
template <typename T>
class List : public Container<T>
{
iterator begin();
iterator end();
public:
virtual void push(T *element) {...}
virtual T* pop() { ... }
virtual void InvokeSomeMethodOnAllItems()
{
for(iterator currItem = begin(); currItem != end(); ++currItem)
{
T* item = *currItem;
item->SomeMethod();
}
}
};
然后,这些容器可以通过多形态传递:
class Item
{
public:
virtual void SomeMethod() = 0;
};
class ConcreteItem
{
public:
virtual void SomeMethod()
{
// Do something
}
};
void AddItemToContainer(Container<Item> &container, Item *item)
{
container.push(item);
}
...
List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();
这为您提供了一种类型安全的通用Container接口。
如果你想为可以包含的元素类型添加约束,你可以这样做:
class Item
{
public:
virtual void SomeMethod() = 0;
typedef int CanBeContainedInList;
};
template <typename T>
class List : public Container<T>
{
typedef typename T::CanBeContainedInList ListGuard;
// ... as before
};
首先,模板和多态性是正交的概念,它们在一起确实很好。接下来,你为什么想要一个特定的数据结构?STL或boost数据结构(特别是指针容器)对您不起作用。
考虑到你的问题,听起来你在这种情况下会滥用继承权。可以对容器中的内容创建"约束",尤其是在使用模板的情况下。这些约束可能超出编译器和链接器所能提供的范围。事实上,对于继承这类事情来说更为尴尬,错误更有可能留给运行时。
使用多态性,基本上只剩下容器的基类和数据类型的派生类。基类/派生类可以在两个方向上都具有所需的虚拟函数。
当然,这意味着您还需要将原始数据类型封装在派生类中。如果你想重新考虑模板的整体使用,这就是我使用模板的地方。从作为模板的基中生成一个派生类,并将其用于基元数据类型(以及其他不需要模板提供的更多功能的类型)。
不要忘记,通过为每个模板化类型进行typedef,您可能会让您的生活更轻松——尤其是当您以后需要将其中一个类型转换为类时。
您可能还想查看Boost概念检查库(BCCL),该库旨在为模板类的模板参数提供约束,在这种情况下是您的容器。
只是重申一下其他人所说的,我从来没有遇到过混合多态性和模板的问题,我已经用它们做了一些相当复杂的事情。
您不必放弃类似Java的接口,也不必使用模板。Josh关于通用基本模板Container的建议肯定会允许您以多态的方式传递Container及其子级,但另外,您当然可以将接口实现为抽象类,作为包含的项。你没有理由不能像你建议的那样创建一个抽象的IComparable类,这样你就可以拥有如下的多态函数:
class Whatever
{
void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}
这个方法现在可以接受Container的任何子级,该子级包含实现IComparable的任何类,因此它将非常灵活。
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- 数组的指针从不分段故障
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 何时在引用或唯一指针上使用移动语义
- QMetaObject invokeMethod的基于函数指针的语法
- 如何从 std::atomic 中提取指针 T<T>?
- 如何在 C# 中映射双 C 结构指针?
- C++将浮点指针值舍入为小数位数
- 为什么++(*p)更改指针值
- 调整大小后指向元素值的指针unordered_map有效?
- 正在将指针转换为范围
- 使用指向成员的指针将成员函数作为参数传递
- 智能指针作为 QObject::d eleteLater() 的替代品
- void* 指针的C++替代品(不是模板)