c++ - 为什么我的类模板动态分配的数组属性只能存储一项?

标签 c++ arrays class templates dynamic

我正在尝试扩展我创建的类模板的功能。以前它允许您使用任何类型的键值对,但前提是您在编译时知道数组的大小。它看起来像这样:

template <typename K, typename V, int N>
class KVList {
    size_t arraySize;
    size_t numberOfElements;
    K keys[N];
    V values[N];
public:
    KVList() : arraySize(N), numberOfElements(0) { }
    // More member functions
}

我希望能够将其用于运行时决定的动态数量的元素,因此我将代码更改为:

template <typename K, typename V>
class KVList {
    size_t arraySize;
    size_t numberOfElements;
    K* keys;
    V* values;
public:
    KVList(size_t size) : numberOfElements(0) {
        arraySize = size;
        keys = new K[size];
        values = new V[size];
    }
    ~KVList() {
        delete[] keys;
        keys = nullptr;
        delete[] values;
        values = nullptr;
    }
    // More member functions
}

新的构造函数有一个参数,它是将用于 KVList 的大小。它仍然从 0 开始 numberOfElements,因为这两种使用都会使 KVList 开始为空,但它确实将 arraySize 设置为 size 参数的值。然后它为键和值数组动态分配内存。添加的析构函数为这些数组释放内存,然后将它们设置为 nullptr。

这会编译并运行,但它只存储我尝试添加到其中的第一个键和第一个值。两者都有一个成员函数,可以将键值对添加到数组中。我使用 Visual Studio 2015 调试器对此进行了测试,发现它很好地存储了第一个键值对,然后它尝试将下一个键值对存储在下一个索引中,但数据无处可去。并且调试器只显示每个数组中的一个槽。当我尝试计算我认为存储在第二个索引中的数据时,我得到一个非常小的数字(试图存储 float 据类型),而不是我试图存储的数据。

我知道使用 vector 来完成这个可能是值得的。然而,这是我在学校 C++ 课上完成的作业的扩展,我这样做的目的是尝试完成它,并了解这样做可能会导致问题的原因,因为这对我来说是显而易见的方法以我目前所掌握的知识。

编辑:用于添加键值对的代码:

// Adds a new element to the list if room exists and returns a reference to the current object, does nothing if no room exists
KVList& add(const K& key, const V& value) {
    if (numberOfElements < arraySize) {
        keys[numberOfElements] = key;
        values[numberOfElements] = value;
        numberOfElements++;
    }

    return *this;
}

编辑:调用 add() 的代码:

// Temp strings for parts of a grade record
string studentNumber, grade;
// Get each part of the grade record
getline(fin, studentNumber, subGradeDelim); // subGradeDelim is a char whose value is ' '
getline(fin, grade, gradeDelim); // gradeDelim is a char whose value is '\n'
// Attempt to parse and store the data from the temp strings
try {
    data.add(stoi(studentNumber), stof(grade)); // data is a KVList<size_t, float> attribute
}
catch (...) {
    // Temporary safeguard, will implement throwing later
    data.add(0u, -1);
}

用于测试显示信息的代码:

void Grades::displayGrades(ostream& os) const {
    // Just doing first two as test
    os << data.value(0) << std::endl;
    os << data.value(1);
}

用于测试的主cpp文件中的代码:

Grades grades("w6.dat");
grades.displayGrades(cout);

w6.dat 的内容:

1022342 67.4
1024567 73.5
2031456 79.3
6032144 53.5
1053250 92.1
3026721 86.5
7420134 62.3
9762314 58.7
6521045 34.6

输出:

67.4
-1.9984e+18

最佳答案

问题(或至少其中之一)出在您的 pastebin 中的这一行:

 data = KVList<size_t, float>(records);

这句看似无辜的台词,其实大有作为。因为数据已经存在,默认构建了您输入 Grades 构造函数主体的实例,这将做三件事:

  1. 它将使用其构造函数在右侧构造一个 KVList。
  2. 它将调用复制赋值运算符并将我们在步骤 1 中构造的内容赋值给数据。
  3. 右侧的对象被破坏。

你可能在想:什么复制赋值运算符,我一个都没写过。好吧,编译器会自动为您生成它。实际上,在 C++11 中,不推荐使用显式析构函数(如您所用)自动生成复制赋值运算符;但它仍然存在。

问题是编译器生成的复制赋值运算符不适合你。您所有的成员变量都是普通类型:整数和指针。所以他们只是复制过来。这意味着在第 2 步之后,类刚刚以最明显的方式被复制过来。反过来,这意味着在一个简短的例子中,左右各有一个对象,它们都有指向内存中相同位置的指针。当第 3 步触发时,右边的对象实际上会继续执行并删除内存。所以数据留下了指向随机垃圾内存的指针。写入此随机内存是未定义的行为,因此您的程序可能会做(不一定是确定性的)奇怪的事情。

(老实说)您的显式资源管理类的编写方式存在很多问题,这里无法涵盖太多。我认为 Accelerated C+ 是一本非常优秀的书,它将引导您解决这些问题,并且有一整章涵盖了如何正确编写此类的每个细节。

关于c++ - 为什么我的类模板动态分配的数组属性只能存储一项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33426713/

相关文章:

c++ - 如何使用动态库在 Xcode 中为 OSX 10.4、10.5 和 10.6 构建 C++

arrays - 类模块类型数组未更新

Angular 2 ngClass - 应用多个类,带有类变量

c++ - 使用模板插入函数丢失数字

c++ - tcp 连接上的 recv() 问题

c++ - 运行时错误问题

c++ - 没有实例的基类?

java - 如何将值放入数组的每个槽中

php - Apache mod_rewrite 和 PHP GET 数组

php:使用反射获取变量类型提示