多个类型的对象的集合 [英] Collection of objects of more than one type

查看:152
本文介绍了多个类型的对象的集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有任何非可怕的方法有一个多个类型的对象的集合?我非常高兴从一个共同的基础获得每种类型。我需要合理的语义,所以这个集合可以被复制,分配,等等。



显然,我不能使用一个向量或基类的列表。对象将被切片,并且复制将根本不工作。使用指针或智能指针的向量或列表可以工作,但是你不会得到正确的复制语义。



要获得复杂的语义,你需要使用Boost的 ptr_vector 。但这需要一个痛苦和容易出错的基础设施。基本上,你不能从基类派生一个新类,因为如果它进入集合,它将不会被正确复制。



这似乎是这样的一个常见的事情和所有的解决方案,我知道是如此可怕。看起来C ++基本上缺少一种方法来创建与给定实例相同的对象的新实例 - 即使该类型具有复制构造函数。并且使克隆重复函数需要在每个派生类中仔细重载。如果你在创建一个派生自基础的新类(或从该基础派生的任何其他类)时失败,你的收藏就会中断。



真的没有更好的方法吗?

解决方案

可以使用 std :: vector< boost :: any> 做我想做的大部分。

  #includeboost / any.hpp
#include< vector>
#include< iostream>

//简单类,所以我们可以看到发生了什么
class MM {
public:
MM(){std :: cout< <<<<< std :: endl; }
MM(const MM& o){std :: cout<<Copy<<& o< - ><<<<< std :: endl; }
〜MM(){std :: cout<<Destroy @<< this<< std :: endl; }
};

int main()
{
//用一些东西填充一个向量
std :: vector& boost :: any> v;
v.push_back(0);
v.push_back(0);
v.push_back(0);
v.push_back(0);

//用我们的对象之一覆盖一个条目。
v [0] = MM();

std :: cout<<复制向量<< std :: endl;
std :: vector< boost :: any> w;
w = v;

std :: cout<<Done<< std :: endl;
}

为此我得到输出:

 创建@ 0xbffff6ae 
复制0xbffff6ae - > 0x100154
Destroy @ 0xbffff6ae
复制向量
复制0x100154 - > 0x100194
Done
Destroy @ 0x100194
Destroy @ 0x100154



符合您的要求,能够对待会员作为一些常见的基本类型,你需要一个非常类似于 boost :: any 的东西,这很幸运是一个相对简单的类。

 模板< typename BASE> 
class any_with_base
{
// ...成员身份为boost :: any

类占位符
{
virtual BASE * as_base ()= 0;

//其他成员如boost :: any :: placeholder
};

template< typename ValueType>
class holder:public placeholder
{
virtual BASE * as_base(){return(BASE *)& held; }
//其他成员如boost :: any :: holder< T>
};

BASE * as_base(){return content?content-> as_base():0; }
}

现在您应该能够:

  vector< any_with_base< Base> > v; 
v.push_back(DerivedA());
v.push_back(DerivedB());

v [0] .as_base() - > base_fn();
v [1] .as_base() - > base_fn();

any_cast< DerivedA>(v [0]) - > only_in_a();

我实际上不喜欢any_cast的语法,并且会使用这个机会添加一个as成员函数。 。所以我可以写最后一行为:

  v [0] .as< DerivedA>() - > only_in_a (); 


Is there any non-awful way to have a collection of objects of more than one type? I'm perfectly happy to derive each type from a common base. I need sensible semantics so the collection can be copied, assigned, and so on.

Obviously, I can't just use a vector or list of the base class. Objects will be sliced and copying won't work at all. Using vectors or lists of pointers or smart pointers works, but you don't get sane copy semantics.

To get sane copy semantics, you need to use something like Boost's ptr_vector. But this requires a painful and error-prone infrastructure. Essentially, you can't just derive a new class from the base class because if it ever goes into the collection, it will not be properly copied.

This seems like such a common thing to do and all the solutions I know of are so awful. It seems like C++ is fundamentally missing a way to create a new instance of an object identical to a given instance -- even if that type has a copy constructor. And making a clone or duplicate function requires careful overloading in every derived class. If you fail to do it when creating a new class derived from the base (or any other class derived from that base) -- boom, your collection breaks.

Is there really no better way?

解决方案

You can use std::vector<boost::any> to do most of this I think.

#include "boost/any.hpp"
#include <vector>
#include <iostream>

//Simple class so we can see what's going on
class MM {
  public:
    MM()               { std::cout<<"Create @ "<<this<<std::endl; }
    MM( const MM & o ) { std::cout<<"Copy "<<&o << " -> "<<this<<std::endl; }
    ~MM()              { std::cout<<"Destroy @ "<<this<<std::endl; }
};

int main()
{
  //Fill a vector with some stuff
  std::vector<boost::any> v;
  v.push_back(0);
  v.push_back(0);
  v.push_back(0);
  v.push_back(0);

  //Overwrite one entry with one of our objects.
  v[0] = MM();

  std::cout<<"Copying the vector"<<std::endl;
  std::vector<boost::any> w;
  w = v;

  std::cout<<"Done"<<std::endl;
}

For which I get the output:

Create @ 0xbffff6ae
Copy 0xbffff6ae -> 0x100154
Destroy @ 0xbffff6ae
Copying the vector
Copy 0x100154 -> 0x100194
Done
Destroy @ 0x100194
Destroy @ 0x100154

Which is what I expect to see.

EDIT:

In line with your requirements to be able to treat members as some common base-type you'll need something very similar to boost::any, which thankfully is a relatively simple class.

template<typename BASE>
class any_with_base
{
    // ... Members as for boost::any

    class placeholder
    {
        virtual BASE * as_base() = 0;

        //Other members as in boost::any::placeholder
    };

    template<typename ValueType>
    class holder : public placeholder
    {
        virtual BASE * as_base() { return (BASE*)&held; }
        //Other members as in boost::any::holder<T>
    };

    BASE* as_base() { return content?content->as_base():0; }
}

Now you should be able to do this:

vector< any_with_base<Base> > v;
v.push_back( DerivedA() );
v.push_back( DerivedB() );

v[0].as_base()->base_fn();
v[1].as_base()->base_fn();

any_cast<DerivedA>(v[0])->only_in_a();

I actually dislike the syntax of any_cast and would use this opportunity to add an "as" member function.. so that I could write the last line as:

v[0].as<DerivedA>()->only_in_a();

这篇关于多个类型的对象的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆