为什么返回数据结构而不是指针会弄乱数据的完整性?

时间:2015-05-13 21:35:27

标签: c++ xcode visual-studio pointers dynamic-memory-allocation

我正在构建一个稀疏矩阵类,它包含两个指向双向链表(向下和向右)的指针数组(行和列)。有点像这样:

 rows
c0123456789
o1
l2
u3
m4  A-->B-->
n5  |   |
s6  |   V
 7  V   D-->
 8  C-->
 9  

两个数组都被初始化为在每个空格中都有nullptr,直到在那个地方插入了某些内容。

我有一个功能" readFile"从文本文件中读取对象并将其插入此稀疏矩阵。出于某种原因,在此函数返回之前,其中的所有数据都很好,但在我返回之后,我在数组中得到随机存储器位置。这是main.cpp

#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"

using namespace std;

class basic 
{
private:
    int x, y;
    string word;
    basic *down;
    basic *right;
public:
    basic(int x, int y, string word)
    {
        this->x = x;
        this->y = y;
        this->word = word;
        down = nullptr;
        right = nullptr;
    }
    int getX()
    {
        return x;
    }
    int getY()
    {
        return y;
    }
    basic *getRight()
    {
        return right;
    }
    void setRight(basic *newRight)
    {
        right = newRight;
    }
    basic *getDown()
    {
        return down;
    }
    void setDown(basic *newDown)
    {
        down = newDown;
    }
    void print()
    {
        cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
    }
};

sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);

int main()
{
    cout << "Working:\n\n";
    sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
    cout << "After returning, here are all the locations that are NOT nullptr:\n";
    workingMatrix->printyArray();
    cin.get();

    cout << "Not working:\n\n";
    sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
    cout << "After returning, here are all the locations that are NOT nullptr:\n";
    brokenMatrix.printyArray();
    cin.get();

    delete workingMatrix;

}

sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
    ifstream inputFile;
    inputFile.open(pathToFile);
    if (inputFile.fail())
    {
        cout << "Couldn't open " << pathToFile << "!\n";
        exit(-1);
    }

    sparseMatrix<basic> matrix(100, 100);

    while (!inputFile.eof())
    {
        int x, y;
        string word;
        inputFile >> x >> y >> word;
        basic data(x, y, word);
        matrix.insert(data);
    }
    cout << "Before returning, here are all the locations that are NOT nullptr:\n";
    matrix.printyArray();
    cout << "press ENTER to return\n";
    cin.get();
    return matrix;
}

sparseMatrix<basic> *readFile(string pathToFile)
{
    ifstream inputFile;
    inputFile.open(pathToFile);
    if (inputFile.fail())
    {
        cout << "Couldn't open " << pathToFile << "!\n";
        exit(-1);
    }

    sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);

    while (!inputFile.eof())
    {
        int x, y;
        string word;
        inputFile >> x >> y >> word;
        basic data(x, y, word);
        matrix->insert(data);
    }
    cout << "Before returning, here are all the locations that are NOT nullptr:\n";
    matrix->printyArray();
    cout << "press ENTER to return\n";
    cin.get();
    return matrix;
}

这里是sparseMatrix.h:

template <class dataType>
class sparseMatrix
{
private:
        //The dimensions of the sparse matrix.
    int width;
    int height;
        //Dynamic array of pointers to heads of linked lists.
    dataType** xArray;
    dataType** yArray;

public:
        //Constructor. Sets everything in the two arrays to nullptr.
    sparseMatrix(int height, int width)
    {
        this->width = width;
        this->height = height;
        xArray = new dataType*[width];
        yArray = new dataType*[height];
        for (int row = 0; row < height; row++)
        {
            this->yArray[row] = nullptr;
        }
        for (int col = 0; col < width; col++)
        {
            this->xArray[col] = nullptr;
        }
    }

        //Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
        //all of those. Then when it's done, it deletes the two dynamic arrays.
    ~sparseMatrix()
    {
        dataType *currentdataType;
        dataType *next;
        for (int row = 0; row < height; row++)
        {
            currentdataType = yArray[row];
            while (currentdataType != nullptr)
            {
                next = currentdataType->getRight();
                delete currentdataType;
                currentdataType = next;
            }
        }
        delete [] yArray;
        delete [] xArray;
    }


        //Creates a copy of the data we are passed, then creates links to this copy.
    void insert(dataType data)
    {
            //Make sure the data is valid.
        if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
        {
            std::cout << "That dataType doesn't fit into the sparse matrix!\n";
            data.print();
            std::cin.get();
        }

        else
        {
                //Copy the data we were passed.
            dataType *newData = new dataType(data);

                //Easy case. If nothing is in this row, set yArray[row] to the address of this data.
            if (yArray[data.getY()] == nullptr)
            {
                yArray[data.getY()] = newData;
            }

                //Not so easy case. Move forward (right) until we find the right location, then set links.
            else
            {
                dataType *current = yArray[data.getY()];
                while (current->getRight() != nullptr)
                {
                    current = current->getRight();
                }
                current->setRight(newData);
            }

                //Easy case. If nothing is in this col, set xArray[col] to the address of this data.
            if (xArray[data.getX()] == nullptr)
            {
                xArray[data.getX()] = newData;
            }

                //Not so easy case. Move forward (down) until we find the right location, then set links.
            else
            {
                dataType *current = xArray[data.getX()];
                while (current->getDown() != nullptr)
                {
                    current = current->getDown();
                }
                current->setDown(newData);
            }
        }
    }
    void printyArray()
    {
        for (int r = 0; r < height; r++)
        {
            if (yArray[r] != nullptr)
            {
                std::cout << r << ' ';
                //yArray[r]->print();
            }
        }
    }
};

readFile从一个看起来像这样的文件中读取所有内容:

0   0   hello
5   2   world
6   8   foo
9   5   bar
...

正如所料,在返回之前,唯一的非nullptr的位置是我插入的位置。 (0,2,8和5)。但是,当函数返回时,数组中的每个SINGLE位置都不是nullptr。我添加了第二个函数,它返回一个指向动态分配的sparseMatrix对象的指针,而不是返回对象本身,这就修复了它。但是,我不明白为什么。看起来这两个函数的行为应该完全相同。

另外,对我来说最让人困惑的部分,为什么在Xcode中运行得非常好,但在Visual Studio中却没有?

3 个答案:

答案 0 :(得分:3)

tomse的答案是正确的,并给出了原因和解决方法,但对于这个问题,这是一个不必要的昂贵的解决方案。他对复制构造函数的建议也解决了许多未来的问题,比如经典为什么我的载体会占用我的数据? Dude,我的段错误在哪里?制作复制构造函数。除非你必须这样做,否则不要使用它。

我认为Andras Fekete的问题是正确的,但是他的帖子有点乱码。不过,他的解决方案仍在继续。

像这样定义你的功能:

bool readFile(string pathToFile, sparseMatrix<basic> & matrix)

删除函数内部矩阵的定义,转而使用传入的矩阵。

错误时返回false,因此您知道矩阵是错误的(或使用异常)。

在调用函数中创建矩阵并将其传递给修订的读者函数。

sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);

这会让你回到指针版本的正确位置,但没有指针,无需复制额外的数据,你也不需要复制。

答案 1 :(得分:2)

你的职能:

sparseMatrix<basic> readFileBROKEN(string pathToFile)

返回对象的副本(可以),但sparseMatrix没有定义复制构造函数,因此将使用默认生成的,只需复制返回对象内的地址即可创建浅拷贝。 但是当您离开函数时,会删除地址所指向的内存(因为调用了本地创建的对象的析构函数)。

要解决此问题,您必须在sparseMatrix中定义自己的复制构造函数,复制对象的所有内容。

sparseMatrix(const sparseMatrix& rhs) :
  width(rhs.width),
  height(rhs.height),
  xArray(nullptr),
  yArray(nullptr)
{
  ... and now copy all the content from rhs.xArray to this->xArray,
  (same for yArray)
}

答案 2 :(得分:0)

问题是你要分配&#39;矩阵&#39;在两个readFile函数中。从函数返回后,两个变量都被释放。但是,返回值(eradFile)时,矩阵将被复制到调用函数的变量中,而返回指针(readFileBROKEN)只是返回用于存储矩阵的地址。

要解决此问题,您应该分配&#39;矩阵&#39;变量,并传入对函数的引用。然后函数可以在填充矩阵的同时返回空白。