c++ std容器-没有指针的多态性.这可能吗?
C++ std containers - polymorphism without pointers. Is it possible?
是否有可能在任何 std c++容器中保持派生类的知识,而不使用指针,从容器动态转换返回值?我知道我可以创建一个向量,比如说,一些基类类型的指针让它们保留它们的子类。但问题是我一定要用指针吗?
的例子:
struct A {
int x = 0, y = 0, z = 0;
virtual void foo() { cout << "A" << endl; };
};
struct B : public A {
int a = 1, b = 1, c = 1;
virtual void foo() { cout << "B" << endl; };
};
int main() {
<SOMECONTAINER><A> a(2);
a[0] = A();
a[1] = B();
B * p;
B& n = dynamic_cast<B&>(a[1]); // Always throws?
p = dynamic_cast<B*>(&a[1]); // Always zero?
cout << p << endl;
}
是的,你必须使用指针。否则,试图将B
放入A
的容器会导致切片:B
被切成A
(这不仅限于容器,如果您执行A a = B()
或将B
传递给期望A
的函数,则会发生完全相同的事情)。
当你稍后把它拿出来的时候,它是一个完全不知道它的血统包括B
类型的杰出祖先的A
——无论你用什么方式看待A
,你都不能把它变成B
。
我将忽略对齐,或者假设指针后的数据已充分对齐。
template<class T, unsigned N>
struct poly_anna;
template<class T,unsigned N>
struct poly_bob {
typedef poly_anna<T,N> poly_anna_;
T*(*get)(poly_anna_*) = nullptr;
void(*destroy)(poly_anna_*) = nullptr;
void(*move_to)(poly_anna_ *,poly_anna_*) = nullptr;
void(*copy_to)(poly_anna_ const*, poly_anna_*)=nullptr;
};
template<class T, unsigned N>
struct poly_anna {
private:
poly_bob<T,N> const*bob=nullptr;
char buff[N];
public:
template<class U> static poly_bob<T,N> const* get_bob() {
static poly_bob<T,N> b={
[](poly_anna*a)->T&{ return *(U*)&a->buff[0]; },
[](poly_anna*a){ ((U*)&a->buff[0])->~U(); a->bob = nullptr; },
[](poly_anna*s,poly_anna*d){
if (s->bob==d->bob){
*((U*)&d->buff[0])=std::move(*((U*)&d->buff[0]));
return;
}
if (d->bob != nullptr) {
d->bob->destroy(b);
}
d->store( std::move( *(U*)&s->buff[0] ) );
},
[](poly_anna const* s, poly_anna*d){
if (d->bob == s->bob){
*(U*)&d->buff[0] = *(U const*)&s->buff[0];
return;
}
if (d->bob){ d->bob->destroy(d); }
d->store( *(U const*)*s->buff[0] );
}
};
return &b;
};
template<class U_>
void store(U_&& u){
typedef typename std::decay<U_>::type U;
static_assert( sizeof(U)<=N, "N not large enough" );
if (bob) bob->destroy( this );
bob = get_bob<U>();
new (&buff[0]) U( std::forward<U_>(u) );
}
void reset(){ if (bob) bob->destroy(this); }
T& get() {
return bob->get(this);
}
T const& get() const {
return bob->get(const_cast<poly_anna*>(this));
}
poly_anna( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
}
poly_anna( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
}
poly_anna&operator=( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna&operator=( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna()=default;
~poly_anna(){if(bob)bob->destroy(this);}
explicit operator bool()const{return bob;}
};
这是我对多态变体的尝试。它存储T
和T
的子节点,只要它们不大于N
,并且可以存储在std
容器中。
需要指针来分派虚拟成员函数
仔细想想,如果没有指针,就只剩下"按值"了。值语义和多态性在一起并没有真正意义。
值只有一个上下文/类型。它是原子和简单的。所见即所得,可以这么说。当然,你可以投射它,但是你有……另一个值。
有一句经常被引用的程序员谚语:没有什么问题是不能通过额外的间接层来解决的,除非间接层太多。
付诸实践,看看boost::variant
。
如果你的容器存储boost::variant
允许所有你想要存储的(子)类,你可以避免使用指针。
这可能是一个胜利,但不一定。
我想这里实际上有两个问题:
- 是否有可能在STL容器中获得多态语义而不使用指针和相关的动态分配?
- 是否可以保留存储在STL容器中的多态对象的具体类型?
1的答案是肯定的,需要一些努力。正如一些人所提到的,这样做的一种方法是使用像boost::variant这样的变体类型。这种方法的问题是,您失去了与存储在变量中的对象自然交互的能力,而必须编写访问者,这有很多语法开销。
如果类层次结构是有界的,那么更好的方法可能是使用boost::variant的变体(没有双关语的意思),专门用于保留多态语义及其相关语法。一个例子可能是安置装置。正如上面dyp的评论所指出的,"放置者"是一个受限制的变体。
对于问题2,我不知道如果不使用typeid()或手卷类型系统,有什么方法可以做到这一点。
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 具有智能指针的多态性
- 如何使用静态多态性在 int 和指针类型之间进行转换?
- 无法初始化已知大小的矢量指针,该大小不会因多态性而更改
- 如何调用指针类型的方法(禁用多态性)?
- 创建基类指针的向量并将派生类对象传递给它(多态性)
- 如何避免指针超出范围(多态性)的C++分段错误
- C++ 被此代码与多态性、指针和对象切片混淆
- 在同时处理基类的多个指针时如何处理多态性?
- 如果基类指针无法访问派生类成员函数,那么多态性有什么方便的呢?
- 与智能指针和矢量C 的多态性有关的问题
- C - 删除多态性指针
- 载体包含指向多态性类别的指针
- 集合中的智能指针多态性
- 多态性和STL容器.指针是不必要的
- 由支持多态性的值池存储,如何使用智能指针
- 如果我需要多态性,我应该使用原始指针而不是unique_ptr
- 没有指针的多态性
- 如何在使用动态多态性时避免指针
- 方法重写(没有虚拟方法或指针)是否被认为是多态性的一部分