为什么我的动态分配数组没有保存正确的值?

时间:2017-04-29 04:49:33

标签: c++ arrays class dynamic

我们正在学校的课程主题。我应该创建一个名为Student的类,我们可以使用它来在动态分配的数组中保存不同的测试分数。我正在努力弄清楚为什么当我打印出这个类的特定实例的数组时,它给了我完全不正确的值。我知道我的类的setter函数正在设置正确的值,但每当我打印它们时它们都是完全错误的。以下是我的代码。 main()中注释掉的部分仅用于测试makeArray()函数的逻辑。

#include <iostream>

using namespace std;


class Student {

private:

    string name;
    int id;
    int* testptr;
    int num;

    void makeArray() {
        int array[num];
        testptr = &array[num];

        for (int i = 0; i < num; i++) {
            array[i] = 0;
        }
    }

public:

    Student() {
        setName("None");
        setID(10);
        num = 3;
        makeArray();
    }

    Student(int n) {
        setName("None");
        setID(10);
        if (n > 0)
            num = n;
        else
            num = 3;
        makeArray();
    }

    Student(string nm, int i, int n) {
        setName(nm);
        setID(i);
        if (n > 0)
            num = n;
        else
            num = 3;
        makeArray();
    }

    void setName(string nm) {
        name = nm;
    }

    void setID(int i) {
        if (i >= 10 && i <= 99){
            id = i;
        }
        else {
            cout << "Error: Cannot set id to " << i << " for " << getName();
            id = 10;
        }
    }

    void setScore(int i, int s) {
        if ((i >= 0 && i <= num) && (s >= 0 && s <= 100)) {
            //Here is where I set the values for the array. They come out correct in the console.
            testptr[i] = s;
            cout << testptr[i] << endl;
        } else {
            cout << "The test " << i << " cannot be set to " << s << " for " << getName() << endl;
        }
    }

    string getName() const {
        return name;
    }

    int getID() const {
        return id;
    }

    void showScore() {
        //This is where I print out the values of the array. They come out incorrect here.
        for (int i = 0; i < num; i++) {
            cout << "Test " << i << " had a score of " << testptr[i] << endl;
        }
    }

    void display() {
        cout << "Calling the display function" << endl;
        cout << "The Name: " << getName() << endl;
        cout << "The ID: " << getID() << endl;
        showScore();
    }

    ~Student() {

    }

};

int main() {
    Student c("Joe", 40, 5);

    c.setScore(0, 90);
    c.setScore(1, 91);
    c.setScore(2, 92);
    c.setScore(3, 93);
    c.setScore(4, 94);


    c.display();

    /**int* testpointer;
    int num = 3;
    int array[num];
    testpointer = &array[0];
    for (int i = 0; i < num; i++) {
        testpointer[i] = i;
        cout << testpointer[i] << endl;
    }**/


}

3 个答案:

答案 0 :(得分:1)

问题(实际上是一组问题)在这个函数中

void makeArray() {
    int array[num];
    testptr = &array[num];

    for (int i = 0; i < num; i++) {
        array[i] = 0;
    }
}

第一个语句int array[num],因为num不是编译时常量,所以它不是有效的C ++。如果您的编译器支持它,它是编译器特定的扩展。 (从技术上讲,这是一个可变长度数组,它是1999 C标准中C的一个特性,从2011年开始在C中可选,但在任何标准或草案中从未成为C ++的一部分。)。

其次,如果您的编译器支持该构造,则会创建一个自动存储持续时间的数组,该函数在函数返回时不再存在。这意味着testptr包含一个悬空指针。函数返回后,testptr的任何解除引用(例如访问数组元素)都会给出未定义的行为。

如果您确实必须使用动态内存分配,请使用“新表达式”,例如testptr = new int[num]。请记住,有必要在类析构函数中执行delete [] testptr。还要查找“三个规则”,因为在创建类的其他实例时,您需要编写复制构造函数和赋值运算符来正确管理数组。

更好的是,#include <vector>,并将testptr更改为std::vector<int>类型。阅读标准矢量类的文档,了解如何使用它,包括管理其大小。

此外,#include <string>也是如此。没有它,您的代码不需要使用std::string类型进行编译 - 您很幸运,通过实施,<iostream>包括<string>。这不是C ++标准所要求的,实际上,它与某些编译器有关,而不是其他编译器。

您的代码中还存在其他问题,但上述内容应足以让您入门。

答案 1 :(得分:1)

您的代码中存在多个问题,但请先从问题开始。 问题是,在当前实现中,您不使用动态数组,而只使用指向内存中某些随机数据的指针。使用new运算符创建动态分配的对象。在您的情况下,您在堆栈上分配对象:

void makeArray() {
    int array[num];         // creates local object on stack
    testptr = &array[num];  // stores pointer to local object

    for (int i = 0; i < num; i++) {
        array[i] = 0;
    }
} // stack is being destroyed here along with array object
  // testptr points to memory that should no be addressed anymore

在C ++中返回/存储指针或引用声明对象范围之外的本地对象是一个很常见的错误。初始化指针的正确方法是:

void makeArray() {
    // note parenthesis at the end,
    // default zero-initializer will be called 
    testptr = new int[num]();
}

请注意,在这种情况下,您必须记住在不再需要使用时释放已分配的内存。实现这一目标的最佳方法是将delete调用放入析构函数:

   ~Student() {
       delete[] array;
   }

然后当Student对象超出范围时,将释放内存。

但我严格鼓励您熟悉&#34;智能指针&#34;这是专门为减少手动内存管理工作而设计的。在现代C ++&#34; raw&#34;指针被认为是非常糟糕的做法,因为它可能导致很多错误。 更好的方法是根本不使用指针,而是使用一个标准C ++容器,如std::vector<int>

答案 2 :(得分:1)

为了让类中的函数共享数据,您可以在类范围内声明变量。

class Foo
{
public:
  MyFunc1() { _n = 1; }
  MyFunc2() { _n = 2; }
private:
  int32_t n; // can be used by all member functions
};

在你的情况下,你在函数范围声明了一个数组,一旦你离开函数就会被销毁。

此外,您无法在堆栈上声明具有可变大小的数组,而是需要在堆上分配数组

class Foo
{
public:
  Foo():_array(nullptr),_size(0) {}
  void AllocateArray(int size) { _array = new int32_t[size]; _size = size; }
  void DestroyArray() { delete [] _array; _size = 0; }
private:
  int32_t* _array;
  int8_t _size;
};

通常你会使用其中一个C ++容器(例如std :: vector)而不是使用原始指针。