为什么返回数据结构而不是指针会影响数据的完整性?
Why does returning a data structure rather than a pointer mess with the integrity of my data?
我正在构建一个稀疏矩阵 class,它包含两个指向双向链表(向下和向右)的指针数组(行和列)。有点像这样:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
两个数组都初始化为在每个 space 中有 nullptr
,直到在那个地方插入了东西。
我有一个函数 "readFile",它从文本文件中读取对象并将它们插入到这个稀疏矩阵中。出于某种原因,在这个函数 return 之前,其中的所有数据都很好,但是在我 return 之后,我在我的数组中获得了随机内存位置。这里是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
...
正如预期的那样,在 returning 之前,唯一不是 nullptr 的位置是我插入的位置。 (0、2、8 和 5)。但是,当函数 returns 时,数组中的每个位置都不是 nullptr。我添加了第二个函数,它 return 是指向动态分配的 sparseMatrix 对象的指针,而不是 returning 对象本身,这修复了它。但是,我不明白为什么。看起来这两个函数的行为应该相同。
此外,最让我困惑的部分是,为什么 运行 在 Xcode 中完全正常,但在 Visual Studio 中却不行?
问题是您在两个 readFile 函数中分配了 'matrix'。从函数 returning 后,两个变量都被释放。但是,returning 矩阵的值 (eradFile) 被复制到调用函数的变量中,而 returning 指针 (readFileBROKEN) 只是 returning 矩阵所在的地址用于存储。
要解决此问题,您应该分配 'matrix' 变量,并传入对函数的引用。然后函数可以 return 一个空的同时正确填充矩阵。
你的函数:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns 对象的副本(可以),但是 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)
}
tomse 的回答是正确的,并给出了原因和修复方法,但对于这个问题来说,这是一个不必要的昂贵修复方法。他对复制构造函数的建议也解决了许多未来的问题,例如经典 Why did my vector eat my data? 和 Dude, Where's my segfault?制作复制构造函数。除非必须,否则不要使用它。
我认为 Andras Fekete 做对了问题,但他的 post 有点乱码。不过,他的解决方案很成功。
像这样定义你的函数:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
删除函数内部矩阵的定义,取而代之的是传入的定义。
Return 出错时为 false,因此您知道矩阵是错误的(或使用异常)。
在调用函数中创建矩阵并将其传递给修改后的 reader 函数。
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
这让您回到使用指针版本的位置,但没有指针,也无需执行复制不需要复制的数据的额外工作。
我正在构建一个稀疏矩阵 class,它包含两个指向双向链表(向下和向右)的指针数组(行和列)。有点像这样:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
两个数组都初始化为在每个 space 中有 nullptr
,直到在那个地方插入了东西。
我有一个函数 "readFile",它从文本文件中读取对象并将它们插入到这个稀疏矩阵中。出于某种原因,在这个函数 return 之前,其中的所有数据都很好,但是在我 return 之后,我在我的数组中获得了随机内存位置。这里是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
...
正如预期的那样,在 returning 之前,唯一不是 nullptr 的位置是我插入的位置。 (0、2、8 和 5)。但是,当函数 returns 时,数组中的每个位置都不是 nullptr。我添加了第二个函数,它 return 是指向动态分配的 sparseMatrix 对象的指针,而不是 returning 对象本身,这修复了它。但是,我不明白为什么。看起来这两个函数的行为应该相同。
此外,最让我困惑的部分是,为什么 运行 在 Xcode 中完全正常,但在 Visual Studio 中却不行?
问题是您在两个 readFile 函数中分配了 'matrix'。从函数 returning 后,两个变量都被释放。但是,returning 矩阵的值 (eradFile) 被复制到调用函数的变量中,而 returning 指针 (readFileBROKEN) 只是 returning 矩阵所在的地址用于存储。
要解决此问题,您应该分配 'matrix' 变量,并传入对函数的引用。然后函数可以 return 一个空的同时正确填充矩阵。
你的函数:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns 对象的副本(可以),但是 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)
}
tomse 的回答是正确的,并给出了原因和修复方法,但对于这个问题来说,这是一个不必要的昂贵修复方法。他对复制构造函数的建议也解决了许多未来的问题,例如经典 Why did my vector eat my data? 和 Dude, Where's my segfault?制作复制构造函数。除非必须,否则不要使用它。
我认为 Andras Fekete 做对了问题,但他的 post 有点乱码。不过,他的解决方案很成功。
像这样定义你的函数:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
删除函数内部矩阵的定义,取而代之的是传入的定义。
Return 出错时为 false,因此您知道矩阵是错误的(或使用异常)。
在调用函数中创建矩阵并将其传递给修改后的 reader 函数。
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
这让您回到使用指针版本的位置,但没有指针,也无需执行复制不需要复制的数据的额外工作。