我正在努力根据通用接口在容器对象中表达包含各种派生类实例的列表。使用基类、指针和引用,将它们添加到向量中可以提供选项。使用鉴别器将它们转换为具体类型在后来被证明是有问题的。
在 C# 中,给予或接受,我会这样表达:
public interface IOrderable
{
decimal GetUnitPrice( );
}
public class BaseProduct
{
public Guid Id {get;set;}
public string Name {get;set;}
public decimal UnitPrice {get;set;}
//
public GetUnitPrice( return UnitPrice; }
}
public class Digital : BaseProduct, IOrderable
{
public int Bytes {get;set;}
}
public class Physical : BaseProduct, IOrderable
{
public int MassInGrammes {get;set}
}
public class Order
{
public List<IOrderable> listOfItems;
public int TotalBytes()
{
int t = 0;
foreach( var i in listOfItems )
{
var d = i as Digital; // <-- in C# this patterny works fine
if( d != null ) t = t + d.Bytes;
}
return t;
}
public decimal TotalMass() { ... }
}
当我尝试在 C++ 中实现这一点时,我从基类开始,并创建一个向量。
class BaseProduct
{
public:
string type; // <-- discriminator
etc.
}
class Order
{
public:
vector<BaseProduct> listOfItems;
int totalBytes() {
int t=0;
for( BaseProduct bp: listOfItems )
{
if( bp.type == "digital" )
... and this is where the wheels come off
how to go from BaseProuct to Digital?
if( bp.type == "physical" )
... etc.
}
return t;
}
}
如果我基于指针创建一个列表,我可以看到如何将 reinterpret_cast 转换为已知的派生类型。这是我最好的猜测。
我无法获得引用对象的版本 - 向量定义 - 最接近的是 wrapped_reference
我觉得这很糟糕,主要是因为 C++ 关注对象所有权/生命周期,以及指针在某种程度上是邪恶的一般建议,即使在现代 C++ 中也是如此。 (事情发生了怎样的变化!C++ 曾经是指针领域!)
对于这种 C++ 中的混合类型列表,什么是好的(不一定是最好的)模式?
(请注意,以上只是伪代码,问题出现在一个 knarly 字节级消息解析器中,其中每个消息类型有一个类。两个足以找到一个模式。)
A
std::vector<BaseObject>
仅包含 BaseObject
s。它不能容纳任何其他类型,无论它是否继承自BaseObject
。
记住,在 C++ 中变量不是指针或引用,除非你声明它们是。这与 C# 形成对比,在 C# 中,类类型的所有变量都是对动态分配对象的引用。
要在 C++ 中拥有多态性,您必须使用(智能)指针或引用。要使用从
BaseObject
派生的动态分配的对象填充容器,您可能需要 std::vector<std::unique_ptr<BaseObject>>
或 std::vector<std::shared_ptr<BaseObject>>
.
你想要哪一个取决于你的需求:
std::unique_ptr
在原始指针上基本上没有运行时开销,但仅限于唯一的所有权语义。std::shared_ptr
具有更类似于 C# 的垃圾收集引用的共享所有权语义,但需要一些额外的运行时开销。