让我用一个例子来证明我的问题。
让我们说我有一堆非常简单的结构,如:
struct A
{
int x;
};
struct B
{
float y;
bool z;
};
我有一个类将这些结构存储在向量中。它有一个添加新批处理结构的方法(每个结构总是有相同数量),并且它有一个通过索引访问每个结构的方法。像这样:
class Test
{
public:
void Create()
{
A a = {};
B b = {};
_vecA.push_back(a);
_vecB.push_back(b);
}
A* GetA(unsigned int i)
{
return &_vecA[i];
}
B* GetB(unsigned int i)
{
return &_vecB[i];
}
private:
std::vector<A> _vecA;
std::vector<B> _vecB;
};
现在,问题是每当我在程序中添加另一个结构时,我必须在该类中添加一堆东西。例如,如果我添加另一个结构(让我们称之为 C ),我必须添加另一个向量,另一个push_back调用,并创建 GetC()方法。
现在,我的问题是:有更好的方法吗?
这是我想到的一种方式:
class ContainerBase
{
public:
ContainerBase(){};
virtual ~ContainerBase(){};
virtual void Add(){};
};
template <typename T>
class Container : public ContainerBase
{
public:
// Singleton
static Container& Instance()
{
static Container instance;
return instance;
}
void Add()
{
T t = {};
_bag.push_back(t);
}
T* Get(unsigned int i)
{
return &_bag[i];
}
private:
Container(){};
~Container(){};
std::vector<T> _bag;
};
class Test
{
public:
template <typename T>
void Register()
{
_containers.push_back(&Container<T>::Instance());
}
void Create()
{
for (unsigned int i = 0; i < _containers.size(); i++)
_containers[i]->Add();
}
template <typename T>
T* Get(unsigned int i)
{
return Container<T>::Instance().Get(i);
}
private:
std::vector<ContainerBase*> _containers;
};
我尝试通过创建模板化的类 Container 来解决它,以管理每种类型的向量。如您所见,它涉及使用单例模式来确保特定类型的结构总是存在 Container 。结构类型只需要使用 Register 方法在类中注册,以便 Test 知道哪些 Containers 它应该添加新的结构到,创建新批次时。这个解决方案存在问题,因为它将全局状态引入容器,这样就无法创建 Test 类的两个实例,它们不会为某种类型的结构共享它们的向量。如果我没有制作容器单例,那么我无法通过 Test 的Get函数中的类型访问它们,我将不得不知道哪个索引在_ 容器 vector对应于什么类型的容器。
我觉得应该有更好的方法来做到这一点。
答案 0 :(得分:0)
使用基类,使A,B和C派生自该基类,并仅保留基类向量/向量。它被称为polymorphism。
答案 1 :(得分:0)
以下将解决您的问题(需要C ++ 11)
#if 1 // std::get<T>(tuple) is not in C++11
// Helper to retrieve index to be able to call std::get<N>(tuple)
template <typename T, typename ... Ts> struct get_index;
template <typename T, typename ... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename ... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
#endif
template <typename ... Ts>
class Data
{
public:
void Create() {
int dummy[] = {0, (getVector<Ts>().emplace_back(), 0)...};
static_cast<void>(dummy); // silent warning about unused variable
}
template <typename T>
const T* Get(std::size_t i) const { return &getVector<T>()[i]; }
template <typename T>
T* Get(std::size_t i) { return &getVector<T>()[i]; }
private:
template <typename T>
const std::vector<T>& getVector() const { return std::get<get_index<T, Ts...>::value>(items); }
template <typename T>
std::vector<T>& getVector() { return std::get<get_index<T, Ts...>::value>(items); }
private:
std::tuple<std::vector<Ts>...> items;
};
测试它:
class A{};
class B{};
int main()
{
Data<A, B> d;
d.Create();
const B* b = d.Get<B>(0);
return 0;
}
答案 2 :(得分:0)
以下是我最终解决问题的方法。
我创建了一个ContainerMapper类,它具有不同结构类型的映射(使用typeinfo获取类型),每个类型都映射到该类型的模板化容器。
std::map<const std::type_info*, ContainerBase*> _map;
我还保留了所有已注册类型的向量,因此如果需要,我可以迭代它们。
std::vector<const std::type_info*> _types;
要注册新类型,我调用 CointainerMapper 的 Register()方法:
template <typename T>
void Register()
{
_types.push_back(&typeid(T));
_map[&typeid(T)] = new Container<T>;
}
要访问它们,我使用:
template <typename T>
Container<T>* Get()
{
return (Container<T>*)_map[&typeid(T)];
}
容器代码与问题中示例中的容器代码相同。
当我想创建另一批结构时,我只是遍历已注册的类型,获取映射的容器并调用它们的Add()方法。
for (unsigned int i = 0; i < _types.size(); i++)
_map[_types[i]]->Add();
使用所有这些看起来像这样:
ContainerMapper m;
m.Register<A>();
m.Create();
// Get container
Container<A>* cA = m.Get<A>();
// Get first element
A* a = cA->Get(0);
// Set x member variable
a->x = 4;
// Or shorter way
m.Get<A>()->Get(0)->x = 4;