具有值语义的运行时多态类
Run-time polymorphic class with value semantics
我想要一个类Value
,它既具有运行时多态行为,又具有值语义。例如,我希望能够做这样的事情:
// create polymorphic data
Value v1 = IntValue(42);
Value v2 = DoubleValue(12.3);
// copy-by-value semantics
Value v3 = v1;
v3.increments();
Value v4;
v4 = v2;
v4.increments();
// possibly put them in my favourite container
MyList<Value> l;
l << v1 << v2 << v3 << v4;
// print them: "Int(42) Double(12.0) Int(43) Double(13.0) "
for(int i=0; i<l.size(); i++) l[i].print();
可能吗?如果可能,怎么可能?
注意:不希望像这里那样使用boost或c++ 11智能指针:它们使调用者代码冗长,使用->
而不是.
,并且没有复制构造函数或赋值操作符实现真值语义。另外,这个问题并不是专门针对容器的
polymorphic_value
已被提议用于标准化,并且具有您需要的一些语义。但是,您必须定义自己的operator <<
。
polymorphic_value<T>
可以保存从T公开派生的类的A对象,复制多态值将复制派生类型的对象。
polymorphic_value<T>
通过类型擦除实现,并使用编译器生成的派生对象的复制构造函数来正确地复制存储为polymorphic_value<BaseType>
的对象。
clone
方法。
在短暂的:
template <class T>
struct control_block
{
virtual ~control_block() = default;
virtual T* ptr() = 0;
virtual std::unique_ptr<control_block> clone() const = 0;
};
template <class T>
class polymorphic_value {
std::unique_ptr<control_block<T>> cb_;
T* ptr_ = nullptr;
public:
polymorphic_value() = default;
polymorphic_value(const polymorphic_value& p) :
cb_(p.cb_->clone())
{
ptr_ = cb_->ptr();
}
T* operator->() { return ptr_; }
const T* operator->() const { return ptr_; }
T& operator*() { return *ptr_; }
const T& operator*() const { return *ptr_; }
// Some methods omitted/deferred.
};
控制块的特化允许定义其他构造函数。
此处讨论动机和设计:
https://github.com/jbcoe/polymorphic_value/blob/master/talks/2017_1_25_cxx_london.md和
https://github.com/jbcoe/polymorphic_value/blob/master/draft.md带测试的完整实现可以在这里找到:
https://github.com/jbcoe/polymorphic_value很难知道您在这里要实现什么,但乍一看,(即将到来的)Boost Type Erasure库可能适合您?
any<
mpl::vector<
copy_constructible<>,
typeid_<>,
incrementable<>,
ostreamable<>
>
> x(10);
++x;
std::cout << x << std::endl; // prints 11
是的,这是可能的,但是当然必须是一些隐藏的指针,并且实际的数据必须存储在堆上。原因是在编译时无法知道数据的实际大小,因此不能在堆栈上。
这个想法是通过一个多态类ValueImpl
的指针来存储实际实现,它提供了你需要的任何虚拟方法,如increments()
或print()
,以及一个方法clone()
,这样你的类Data
就能够实现值语义:
class ValueImpl
{
public:
virtual ~ValueImpl() {};
virtual std::unique_ptr<ValueImpl> clone() const { return new ValueImpl(); }
virtual void increments() {}
virtual void print() const { std::cout << "VoidValue "; }
};
class Value
{
private:
ValueImpl * p_; // The underlying pointer
public:
// Default constructor, allocating a "void" value
Value() : p_(new ValueImpl) {}
// Construct a Value given an actual implementation:
// This allocates memory on the heap, hidden in clone()
// This memory is automatically deallocated by unique_ptr
Value(const ValueImpl & derived) : p_(derived.clone()) {}
// Destruct the data (unique_ptr automatically deallocates the memory)
~Value() {}
// Copy constructor and assignment operator:
// Implements a value semantics by allocating new memory
Value(const Value & other) : p_(other.p_->clone()) {}
Value & operator=(const Value & other)
{
if(&other != this)
{
p_ = std::move(other.p_->clone());
}
return *this;
}
// Custom "polymorphic" methods
void increments() { p_->increments(); }
void print() { p_->print(); }
};
所包含的指针存储在c++ 11 std::unique_ptr<ValueImpl>
中,以确保在销毁或分配新值时释放内存。
派生的实现最终可以按以下方式定义:
class IntValue : public ValueImpl
{
public:
IntValue(int k) : k_(k) {}
std::unique_ptr<IntValue> clone() const
{
return std::unique_ptr<IntValue>(new IntValue(k_));
}
void increments() { k_++; }
void print() const { std::cout << "Int(" << k_ << ") "; }
private:
int k_;
};
class DoubleValue : public ValueImpl
{
public:
DoubleValue(double x) : x_(x) {}
std::unique_ptr<DoubleValue> clone() const
{
return std::unique_ptr<DoubleValue>(new DoubleValue(k_));
}
void increments() { x_ += 1.0; }
void print() const { std::cout << "Double(" << x_ << ") "; }
private:
int x_;
};
这足以使问题中的代码片段无需任何修改即可工作。这提供了带有值语义的运行时多态性,而不是c++语言内置的带有指针语义的传统运行时多态性。事实上,多态性的概念(根据其真正的"类型"处理行为不同的泛型对象)与指针的概念(能够通过使用对象的地址共享内存和优化函数调用)是独立的,我个人认为,多态性在c++中只通过指针提供了更多的实现细节。上面的代码是在"哲学上不需要"使用指针时利用多态性的一种变通方法,从而简化了内存管理。
注意:感谢CaptainObvlious的贡献和他的改进代码,我在这里部分集成。未集成的有:
- 为了简化派生实现的创建,您可能需要创建一个中间模板化类
- 你可能更喜欢使用抽象接口而不是我的非抽象基类
- 为什么我们实际上需要运行时多态性?
- 运行时多态性和dynamic_cast需要澄清
- 运行时多态性 - 箭头运算符访问了错误的成员?
- C ++中的方法覆盖:是编译时还是运行时多态性?
- 运行时与编译时多态性:更好的可读性与编译时错误检查,更重要的是
- 两个不同类的运行时多态性
- 考虑 .(点)应用于应该是运行时多态性的东西时
- 编译时多态性运行时多态性是这样的吗
- CRTP的运行时多态性设计和策略
- 查询C++中的运行时多态性
- 需要使用模板解决运行时多态性的帮助
- 为什么不能在编译时解决运行时多态性?
- c++与java中运行时多态性的成本比较
- 具有值语义的运行时多态类
- 为什么为模板实例化声明运行时多态性会导致链接器错误
- 编译时与运行时多态性在c++中的优缺点
- 允许运行时和编译时多态性的灵活方式
- c++中通过虚函数实现的运行时多态性
- 运行时多态与编译时有什么不同
- 虚拟函数是在C++中实现运行时多态性的唯一途径吗