【问题标题】:Create a C++ template class which can work like a Vector of Vectors创建一个可以像 Vector of Vectors 一样工作的 C++ 模板类
【发布时间】:2018-11-07 03:10:08
【问题描述】:

这是一个 Group 类,类似于 Vector。我可以创建一个组向量并且效果很好。我在创建组组时遇到困难。此代码编译并运行,但组的组与组向量的行为方式不同 - 请参阅输出。我觉得我在 Group 中缺少一个处理模板类型的特殊构造函数?也许是别的东西-感激地收到任何指示。

#include <vector>

template <class T>
class Group
{
  private:
    T *data;
    int current_size;
    int max_size;

  private:
    void expand();

  public:
    Group();
    Group(int size);
    ~Group();

    T operator[](int index) const;
    int count() const;
    int add_item(const T new_item);
};

template <class T>
Group<T>::Group()
{
    data = NULL;
    max_size = 0;
    current_size = 0;
}

template <class T>
Group<T>::Group(int size)
{
    if (size < 2)
        size = 2;
    data = new T[size];
    max_size = size;
    current_size = 0;
}

template <class T>
Group<T>::~Group()
{
    if (data != NULL)
        delete[] data;
    current_size = 0;
    max_size = 0;
}

template <class T>
void Group<T>::expand()
{
    if (data == NULL)
    {
        current_size = 0;
        max_size = 2;
        data = new T[2];
    }
    else
    {
        //      printf("expanding %x from %d to %d\n", this, current_size, current_size*2);

        T *tempArray = new T[max_size * 2];
        for (int i = 0; i < max_size; i++)
        {
            tempArray[i] = data[i];
        }

        delete[] data;
        data = tempArray;
        max_size = max_size * 2;
    }
}

template <class T>
int Group<T>::add_item(const T new_item)
{
    // expand the array if necessary
    while (current_size >= (max_size))
        expand();

    // add_item the new thing
    data[current_size] = new_item;
    current_size++;
    return (current_size);
}

template <class T>
inline T Group<T>::operator[](int index) const
{
    return data[index];
}

template <class T>
inline int Group<T>::count() const
{
    return current_size;
}

int main()
{
    // Vector of Groups works fine

    int numgroups = 3; // just 3 groups for testing

    // a vector of Groups
    std::vector<Group<int>> setofgroups(numgroups);

    printf("setofgroups count=%d\n", setofgroups.size());

    // some test data
    // 4 items in first group
    setofgroups[0].add_item(6);
    setofgroups[0].add_item(9);
    setofgroups[0].add_item(15);
    setofgroups[0].add_item(18);

    // one item in second
    setofgroups[1].add_item(7);

    // two items in third
    setofgroups[2].add_item(8);
    setofgroups[2].add_item(25);

    // for each group, print the member values
    for (int g = 0; g < setofgroups.size(); g++)
    {
        printf("group %d\n", g);
        for (int i = 0; i < setofgroups[g].count(); i++)
            printf("  member %d, value %d\n", i, setofgroups[g][i]);
    }

    // Group of groups doesn't seem to work

    Group<Group<int>> groupofgroups(numgroups);

    // this returns ZERO - not 3 as I expected
    printf("groupofgroups count=%d\n", groupofgroups.count());

    groupofgroups[0].add_item(6);
    groupofgroups[0].add_item(9);
    groupofgroups[0].add_item(15);
    groupofgroups[0].add_item(18);

    printf("groupofgroups[0].count=%d\n", groupofgroups[0].count()); // this returns ZERO - where did the items go?

    groupofgroups[1].add_item(7);

    // two items in third
    groupofgroups[2].add_item(8);
    groupofgroups[2].add_item(25);

    // for each group, print the member values
    for (int g = 0; g < groupofgroups.count(); g++)
    {
        printf("group 2  %d (count=%d)\n", g, groupofgroups[g].count());
        for (int i = 0; i < groupofgroups[g].count(); i++)
            printf("  member %d, value %d\n", i, groupofgroups[g][i]);
    }

    return 0;
}

输出:

       setofgroups count=3
    group 0
      member 0, value 6
      member 1, value 9
      member 2, value 15
      member 3, value 18
    group 1
      member 0, value 7
    group 2
      member 0, value 8
      member 1, value 25
    groupofgroups count=0

groupofgroups[0].count=0

【问题讨论】:

  • 在您的 Group 构造函数中,您将 current_size 设置为 0,并且您只需在 add_item() 中更改它,您永远不会为外部 Group 调用它。
  • 两个提示。 (1) operator T() 通常最好返回一个引用。 (2) 查找“三规则”、“五规则”、“零规则”。
  • std::vector 使用 placement new 运算符,因此 T 在展开时不需要是默认可构造和可复制的。如果您想要与std::vector 相同的行为,请考虑使用它。

标签: c++ arrays templates vector


【解决方案1】:

有两件事需要解决:

  • 按照评论中的建议,您需要在构造函数中设置 current_size
  • 您需要返回一个引用 operator[] 否则所有修改都将应用于元素的临时副本

link to the code

【讨论】:

  • 太棒了! - 非常感谢你们所有人。有了这些变化,效果很好。
  • 嗯,它运行,但它不正确。这样做的设计是 current_size 指示有多少数组项是有效的,即您可以初始化一个大小为 10 的 Group,但 current_size 指示没有一个有效。如果您添加 4 个项目,当前大小将为 4,并且只有 [0]-[3] 有效。
  • 当可用空间用完时,空间翻倍。所以 - 尽管在构造函数中将当前大小设置为大小在这里可以工作,但在其他地方使用时会搞砸。当前大小是有效/设置项目的数量,而不是最大可用空间。
  • @rapcoder 当您的大小为 4 时,仅包含 0 到 3 的元素有效,这是完全正常的。如果 current_size 是您测试的元素数,则应在设置值之前检查 max_size 而不是 current_size
【解决方案2】:

您的班级中有一个重要的析构函数。这意味着默认的复制构造函数和默认的复制赋值会做错事。

这是一个简单的例子:

Group<int> a(4);  // ok, ready to add some elements in the freshly allocated a.data
a.add_item(1);
int count = a.add_item(2);   // ok a contains 2 items 1 and 2

if (count == 2) {
    Group<int> b = a;        // the default copy ctor make b.data == a.data...
    std::cout << "b contains " << b.count() << " items" << std::endl; // nice up to here
}             // horror: b reaches end of life and destructor calls delete[](b.data) !

// a.data is now a dangling pointer...

所以你必须要么实现复制构造函数和复制赋值操作符,要么将它们标记为显式删除——这是规则 3(更多细节here

如果您不实现它们,编译器将隐含地提供一个移动 ctor 和一个移动赋值运算符,它们的工作量不会比复制版本大得多。因此,您必须实现它们或将它们标记为显式删除 - 这是 5 的规则。

容器推荐的operator []——标准库使用的——是:

T& operator[] (size_t index);              // used for  a[i] = ...
const T& operator[] (size_t index) const;  // used for  x = a[i];

使用引用可以避免不必要的大对象副本...

【讨论】:

  • 原始代码有其他东西,但我在这个例子中删除了它(认为问题出在其他地方)。我有: Group(const Group& copyfrom); // 复制构造函数 void operator = (const Group& copyassign); // 复制赋值构造函数
  • 这些是我理解的复制和复制分配(当然我可能错了)。如果我将这些添加到此示例中,它仍然不起作用。有没有办法发布额外的代码,而不是在这些小评论框中?
  • template&lt;class T&gt; inline void Group&lt;T&gt;::operator= (const Group&lt;T&gt;&amp; copyassign ) { int newSize = copyassign.count(); if (max_size &lt; newSize) { if (data != 0) delete[] data; data = new T[newSize]; max_size = newSize; } T* caArray = copyassign.data; for (int i = 0; i &lt; newSize; i++) { data[i] = caArray[i]; } current_size = newSize; }
  • template&lt;class T&gt; Group&lt;T&gt;::Group(const Group&lt;T&gt;&amp; copyfrom) { data = 0; max_size = 0; current_size = 0; int newSize = copyfrom.count(); if (max_size &lt; newSize) { if (data != 0) delete[] data; data = new T[newSize]; max_size = newSize; } T* copyArray = copyfrom.data; for (int i = 0; i &lt; newSize; i++) { data[i] = copyArray[i]; } current_size = newSize; }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 1970-01-01
  • 1970-01-01
  • 2013-05-07
  • 1970-01-01
  • 2018-12-07
相关资源
最近更新 更多