C++:模板类的向量

12

我有一个名为Cell的模板类,代码如下:

template<class T>class Cell
{
    string header, T data;
}

现在我想要另一个名为Row的类。Row将有一个名为Cells的向量,以便我可以将Cell和Cell类型的元素添加到该向量中。这是可能的吗?

如果是这样,我该如何做呢? 提前致谢。


你想要一个行来容纳例如Cell<int>、Cell<float>和任何其他单元格类型在同一个vector<>中吗? - Skizz
是的,你说得对。我确切地想要那个。 - Jakir Hossain
1
这是指您需要多态容器吗?您只能使用动态多态性(继承),而不能使用静态多态性(模板)。 - Elazar
5
我有一个名为Cell的模板类。不,你没有。那是一个类模板,这意味着它不是一个类。Cell<int>是一个类。Cell<float>也是一个类,Cell<Cell<std::vector<double>>>也是一个类。但是它们是三个不同且独立的类,彼此之间没有任何相似之处。 - Arne Mertz
1
向量的所有元素必须是相同类型的,因此您不能将 Cell<float>Cell<int> 放在一起。您需要使用指针和继承,并且必须知道自己在做什么。很容易创建一个无法使用的基类,而所有实质内容都在子类中,然后您就没有访问它的方法了。您需要在基类中放置实质内容。这意味着它必须具有可用的接口,不依赖于 T。 - n. m.
显示剩余2条评论
4个回答

25

根据您提供的详细信息,前两个答案都不可行。您需要的是一种称为单元格变量的类型,然后您可以拥有这些类型的向量。例如:

enum CellType
{
  Int,
  Float,
  // etc
};

class Cell
{
  CellType type;
  union
  {
    int i;
    float f;
    // etc
  };
};

class Vector
{
  vector <Cell> cells;
};

然而,这种方法需要大量的代码维护,添加新类型非常繁琐。另一种选择是使用单元格模板和共同的基类:-
class ICell
{
  // list of cell methods
};

template <class T>
class Cell : public ICell
{
  T data;
  // implementation of cell methods
};

class Vector
{
  vector <ICell *> cells;
};

这种方法可能更有效,因为你最初需要更新较少的代码来添加新的单元格类型,但是你必须在单元格向量中使用指针类型。如果你按值存储单元格 vector <ICell>,那么由于对象切片,你将会丢失数据。


2
联合体不仅仅是痛苦,它也不是类型安全的,这正是模板的整个目的。 - Elazar
1
非常感谢。我将使用第二种方法。感谢所有尝试帮助我的人。 - Jakir Hossain
还有一件事,您能否提供使用迭代器添加元素和访问元素的示例代码?先谢谢了。 - Jakir Hossain
1
@JakirHossain:为了遵循单一职责原则,多问一些问题(如插入向量、迭代等),而不是扩展这一个。 - Skizz
2
Icell不能用于访问封装数据。而且你不能在基类中定义虚函数来提供对数据的访问,因为这样你会将“typename T”上移类层次结构,然后你会再次面临同样的问题,即定义不允许的vector<Icell<T>> cells;。因此,我认为这不是正确的答案。 - umbersar

13

在C++中不可能实现以下操作,但在Java/Python中是可以的,原因是:在C++向量中,STL容器的存储(由vector::data()返回)按顺序打包了所有对象实例化。每个元素必须具有相同的大小,这使得寻址快速方便。因此,假设您定义了一个模板类A,

template <class T>
class A{
  int id;
  T obj;
};

它的大小将取决于模板变量"T obj"。将不同模板类型T的相同类A推入向量中将使每个元素具有不同的大小,因此这是不可能的。唯一的方法是使用基类的shared_ptr或unique_ptr向量。C++11和Boost都支持shared_ptr和unique_ptr。每个派生类元素可以有不同的模板类型。这样,当基类指针的析构函数被调用时,派生类的析构函数将被调用。例如,

#include <memory>
#include <vector>
#include <iostream>
#include <string>

using namespace std;

class A{};

template <class T>
class AImpl : public A{
public:
    T obj;
    AImpl(T _obj):obj(_obj){}
    ~AImpl(){
        cout << "Deleting " << obj << endl;
    }
};

int main(int argc, char** argv)
{
    AImpl <string>* a1 = new AImpl <string> ("string1234");
    AImpl <int>* a2 = new AImpl <int> (1234);
    AImpl <double>* a3 = new AImpl <double> (1.234);
    vector <shared_ptr<A>> As;
    As.push_back(shared_ptr<A>(a1));
    As.push_back(shared_ptr<A>(a2));
    As.push_back(shared_ptr<A>(a3));
}

记得编译时加上-std=c++11来启用C++11。

输出:

Deleting string1234
Deleting 1234
Deleting 1.234

您能得到您想要的! :)

在Java/Python中,每个类对象变量实际上是一个指针,因此,Java数组A或Python列表A等价于C++ A指针数组。因此,您可以获得基本相同的功能而无需显式创建shared_ptrs。


你的程序显示错误:cpp:25:13: error: 'shared_ptr' was not declared in this scope - Praveen Vinny
1
@PraveenVinny,这是因为你没有使用-std=c++11编译。shared_ptr仅适用于C++11的<memory>库。 - xuancong84

6
其他回答很好,但你可能想要的是:
template<class T>
class Row
{
private:
    class Cell {
        string header;
        T data;
    }

    std::vector<Cell> cells;
    ...
}

5
像这样的内容吗?
template<class T>
class Row
{
private:
   std::vector<Cell<T> > cells;
};

好的,这个答案是不正确的。

所以,如果你想在一个 vector 中存储不同的单元格 - 你应该使用一些动态类型识别(你可以使用一个基类,在向量中存储指向它的指针,只使用虚函数,这些虚函数在所有派生类中被覆盖,你可以存储像boost::any这样的东西,并为每个插入的元素保存一些类型标识,以将它们转换为真实类型并与其一起工作)。


Jakir - 注意在最后一个">"之前加上空格,以避免与">>"运算符混淆。不过,C++11不需要这样做。 - Elazar
可能会出问题。因为在创建行类型对象时,我必须提供类型参数。而矢量将是该类型的。 - Jakir Hossain
@JakirHossain 然后您可以添加一个模板参数。并将其用于行特定的事物,并使用 T 用于单元格。 - stardust
1
@JakirHossain 在一个向量中不能推入不同类型的元素,除非使用一些基类或其他东西,使您能够知道每个元素的确切类型。 - ForEveR
@JakirHossain - 我想详细说明你所要求的问题:vector是一个数组,因此其中所有元素应该具有相同的静态类型,但Cell<int>Cell<char>是不相关的类型(不像Java中的泛型类)。 - Elazar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接