今天在写图的相关的程序,写着写着就出现了一个问题,如标题。
1. 问题代码
这里我们的目标主要是想要是利用邻接矩阵创建一个图,主要代码主要是参考这篇博客【C++】图的定义及性质
#include <iostream>
#include <climits>
enum GraphKind {DG, UDG, DN, UDN};
/* directed graph, undirected graph, directed netword, undirected network*/
class AdjGraph {
public:
int *vertex; // vertex array
int **matrix; // adjoin matrix
int vertexNum; // total vertex num
int edgeNum; // total edgeNum
enum GraphKind kind; // graph kinds
AdjGraph(int Vertex); // constructor
// AdjGraph(const AdjGraph&) = delete;
// AdjGraph &operator=(const AdjGraph&) = delete;
~AdjGraph(); // destructor
void setEdge(AdjGraph& G, int start, int end); // set graph edge
void setEdge(AdjGraph& G, int start, int end, int weight); // set network edge
void setEdgeNum(int EdgeNum); // set gprah edge num
int getEdgeNum() const; // get graph edge num
void creteGraph(AdjGraph& G, enum GraphKind kind); // create graph/network
void clearGraph(AdjGraph& G); // clear the graph
void printGraph(AdjGraph G) const; // print the graph
};
AdjGraph::AdjGraph(int Vertex) {
vertexNum = Vertex;
edgeNum = 0;
}
AdjGraph::~AdjGraph() {
edgeNum = 0;
delete vertex;
vertex = nullptr;
for (int i = 0; i < vertexNum; ++i) {
delete [] matrix[i];
matrix[i] = nullptr;
}
delete [] matrix;
matrix = nullptr;
vertexNum = 0;
}
void AdjGraph::setEdge(AdjGraph& G, int start, int end) {
// Edge e(start, end);
if (G.kind == UDG) {
G.matrix[start - 1][end - 1] = 1;
G.matrix[end - 1][start - 1] = 1;
} else if (G.kind == DG) {
G.matrix[start - 1][end - 1] = 1;
}
}
void AdjGraph::setEdge(AdjGraph& G, int start, int end, int weight) {
// Edge e(start, end, weight);
if (G.kind == UDN) {
G.matrix[start - 1][end - 1] = weight;
G.matrix[end - 1][start - 1] = weight;
} else if (G.kind == DN) {
G.matrix[start - 1][end - 1] = weight;
}
}
void AdjGraph::setEdgeNum(int EdgeNum) {
edgeNum = EdgeNum;
}
int AdjGraph::getEdgeNum() const {
return edgeNum;
}
void AdjGraph::creteGraph(AdjGraph& G, enum GraphKind kind) {
G.vertex = new int[G.vertexNum]; // create graph/network vertex
for (int i = 0; i < vertexNum; ++i) {
G.vertex[i] = i + 1;
}
G.matrix = new int*[G.vertexNum];
for (int i = 0; i < vertexNum; ++i) {
G.matrix[i] = new int[G.vertexNum];
}
G.kind = kind; // get our kind
for (int i = 0; i < G.vertexNum; ++i) {
for (int j = 0; j < G.vertexNum; ++j) {
if (G.kind == UDG || G.kind == DG) {
G.matrix[i][j] = 0;
} else if (G.kind == UDN || G.kind == DN) {
G.matrix[i][j] = INT_MAX;
} else {
std::cout << "useless GraphKind parameters" << std::endl;
break;
}
}
}
}
void AdjGraph::clearGraph(AdjGraph& G) {
G.edgeNum = 0;
delete G.vertex;
G.vertex = nullptr;
for (int i = 0; i < G.vertexNum; ++i) {
delete G.matrix[i];
G.matrix[i] = nullptr;
}
delete G.matrix;
G.matrix = nullptr;
G.vertexNum = 0;
}
void AdjGraph::printGraph(AdjGraph G) const {
if (G.vertexNum != 0) {
for (int i = 0; i < G.vertexNum; ++i) {
for (int j = 0; j < G.vertexNum; ++j) {
if (G.matrix[i][j] == INT_MAX) {
std::cout << "∞" << " ";
} else {
std::cout << G.matrix[i][j] << " ";
}
}
std::cout << "\n";
}
} else {
std::cout << "graph is empty!" << std::endl;
}
}
int main() {
AdjGraph G1 ( 5 );
// G1.setEdgeNum ( 6 );
G1.creteGraph ( G1, UDG ); // undirected graph
G1.setEdge ( G1, 1, 2 );
G1.setEdge ( G1, 1, 4 );
G1.setEdge ( G1, 2, 3 );
G1.setEdge ( G1, 3, 4 );
G1.setEdge ( G1, 3, 5 );
G1.setEdge ( G1, 2, 5 );
G1.printGraph ( G1 );
return 0;
}
看着错误是挺吓人的,但是主要问题就是double free or corruption (fasttop)
错误。
2. 解决
遇到问题我们首先是google呗,先贴一下网上的这些问题大概解决。
- double free or corruption (fasttop)
- C++ pointer “error: double free or corruption (out)”
- 实例介绍利用valgrind定位内存异常释放问题(double free 和wrong free)
看到这个输出栏发现结果是正确的,然后我们猜测我们使用了指针,后面又进行了delete
操作,可能是析构函数有点问题。首选我们在析构函数中添加一句测试代码,变成这样:
AdjGraph::~AdjGraph() {
edgeNum = 0;
delete vertex;
vertex = nullptr;
std::cout << "hello" << std::endl;
for (int i = 0; i < vertexNum; ++i) {
delete [] matrix[i];
matrix[i] = nullptr;
}
delete [] matrix;
matrix = nullptr;
vertexNum = 0;
}
然后输出这个:
然后我们竟然发现析构函数竟然执行了2次!why? 继续:
我们在Google一下,为什么析构函数会执行2次,结果真给我找到了相关的问题为什么会两次调用析构函数,哇!竟然如出一辙,这篇博客给出的解答是:
发现原来是系统调用默认拷贝构造函数的结果。在返回对象和按值传递参数时,要生成临时对象,生成临时对象要调用默认拷贝构造函数。通过这个例子更让我加深了对Effective C++的理解。只要类里有指针变量就得自己写拷贝构造函数和赋值函数,但是你确定用不着这些函数时,可以把这些函数做private声明而不去实现它,这就防止了会有人去调用它们,也防止了编译器去生。
一开始看这句话的时候有点懵逼,说的是啥,没办法,只能把《C++ Primer》拿出来翻一翻:
这里的话主要重点:当返回对象是按值传递参数时候,会调用默认的拷贝构造函数!
所以了话,我们话就按照它的方法将函数显示设置为阻止拷贝,在类中添加阻止拷贝和阻止赋值
AdjGraph(const AdjGraph&) = delete;
AdjGraph &operator=(const AdjGraph&) = delete;
然后运行代码发现:
什么使用了已经删除了函数,好吧,阻止拷贝让我们使用了已经删除的函数~~等一下,突然我意识到了什么,我好像printGraph
形参传入的是值啊,所以之前的话会调用系统默认的拷贝构造函数,然后程序结束的时候会调用二次析构函数,这样就会二次delete
指针,然后就会出现double free or corruption (fasttop)
错误,好像分析的有点道理。
原来定义的printGraph
函数:
void AdjGraph::printGraph(AdjGraph G) const {
if (G.vertexNum != 0) {
for (int i = 0; i < G.vertexNum; ++i) {
for (int j = 0; j < G.vertexNum; ++j) {
if (G.matrix[i][j] == INT_MAX) {
std::cout << "∞" << " ";
} else {
std::cout << G.matrix[i][j] << " ";
}
}
std::cout << "\n";
}
} else {
std::cout << "graph is empty!" << std::endl;
}
}
既然找到了原因那我们对原来的printGraph
进行修改,将其形参变成引用类型,这样就是传引用而不是值,也就不会调用系统默认的拷贝构造函数,也就只会发生一次析构了。对于阻止拷贝和阻止赋值,去掉和保留暂时对本程序影响不大。
#include <iostream>
#include <climits>
enum GraphKind {DG, UDG, DN, UDN};
/* directed graph, undirected graph, directed netword, undirected network*/
class AdjGraph {
public:
int *vertex; // vertex array
int **matrix; // adjoin matrix
int vertexNum; // total vertex num
int edgeNum; // total edgeNum
enum GraphKind kind; // graph kinds
AdjGraph(int Vertex); // constructor
AdjGraph(const AdjGraph&) = delete;
AdjGraph &operator=(const AdjGraph&) = delete;
~AdjGraph(); // destructor
void setEdge(AdjGraph& G, int start, int end); // set graph edge
void setEdge(AdjGraph& G, int start, int end, int weight); // set network edge
void setEdgeNum(int EdgeNum); // set gprah edge num
int getEdgeNum() const; // get graph edge num
void creteGraph(AdjGraph& G, enum GraphKind kind); // create graph/network
void clearGraph(AdjGraph& G); // clear the graph
void printGraph(AdjGraph& G) const; // print the graph
};
AdjGraph::AdjGraph(int Vertex) {
vertexNum = Vertex;
edgeNum = 0;
}
AdjGraph::~AdjGraph() {
edgeNum = 0;
delete vertex;
vertex = nullptr;
for (int i = 0; i < vertexNum; ++i) {
delete [] matrix[i];
matrix[i] = nullptr;
}
delete [] matrix;
matrix = nullptr;
vertexNum = 0;
}
void AdjGraph::setEdge(AdjGraph& G, int start, int end) {
// Edge e(start, end);
if (G.kind == UDG) {
G.matrix[start - 1][end - 1] = 1;
G.matrix[end - 1][start - 1] = 1;
} else if (G.kind == DG) {
G.matrix[start - 1][end - 1] = 1;
}
}
void AdjGraph::setEdge(AdjGraph& G, int start, int end, int weight) {
// Edge e(start, end, weight);
if (G.kind == UDN) {
G.matrix[start - 1][end - 1] = weight;
G.matrix[end - 1][start - 1] = weight;
} else if (G.kind == DN) {
G.matrix[start - 1][end - 1] = weight;
}
}
void AdjGraph::setEdgeNum(int EdgeNum) {
edgeNum = EdgeNum;
}
int AdjGraph::getEdgeNum() const {
return edgeNum;
}
void AdjGraph::creteGraph(AdjGraph& G, enum GraphKind kind) {
G.vertex = new int[G.vertexNum]; // create graph/network vertex
for (int i = 0; i < vertexNum; ++i) {
G.vertex[i] = i + 1;
}
G.matrix = new int*[G.vertexNum];
for (int i = 0; i < vertexNum; ++i) {
G.matrix[i] = new int[G.vertexNum];
}
G.kind = kind; // get our kind
for (int i = 0; i < G.vertexNum; ++i) {
for (int j = 0; j < G.vertexNum; ++j) {
if (G.kind == UDG || G.kind == DG) {
G.matrix[i][j] = 0;
} else if (G.kind == UDN || G.kind == DN) {
G.matrix[i][j] = INT_MAX;
} else {
std::cout << "useless GraphKind parameters" << std::endl;
break;
}
}
}
}
void AdjGraph::clearGraph(AdjGraph& G) {
G.edgeNum = 0;
delete G.vertex;
G.vertex = nullptr;
for (int i = 0; i < G.vertexNum; ++i) {
delete G.matrix[i];
G.matrix[i] = nullptr;
}
delete G.matrix;
G.matrix = nullptr;
G.vertexNum = 0;
}
void AdjGraph::printGraph(AdjGraph& G) const {
if (G.vertexNum != 0) {
for (int i = 0; i < G.vertexNum; ++i) {
for (int j = 0; j < G.vertexNum; ++j) {
if (G.matrix[i][j] == INT_MAX) {
std::cout << "∞" << " ";
} else {
std::cout << G.matrix[i][j] << " ";
}
}
std::cout << "\n";
}
} else {
std::cout << "graph is empty!" << std::endl;
}
}
int main() {
AdjGraph G1 ( 5 );
// G1.setEdgeNum ( 6 );
G1.creteGraph ( G1, UDG ); // undirected graph
G1.setEdge ( G1, 1, 2 );
G1.setEdge ( G1, 1, 4 );
G1.setEdge ( G1, 2, 3 );
G1.setEdge ( G1, 3, 4 );
G1.setEdge ( G1, 3, 5 );
G1.setEdge ( G1, 2, 5 );
G1.printGraph ( G1 );
std::cout << std::endl;
AdjGraph G2 ( 4 );
G2.setEdgeNum ( 4 );
G2.creteGraph ( G2, DG );
G2.setEdge ( G2, 1, 2 );
G2.setEdge ( G2, 1, 3 );
G2.setEdge ( G2, 4, 1 );
G2.setEdge ( G2, 3, 4 );
G2.printGraph ( G2 );
std::cout << std::endl;
AdjGraph G3 ( 6 );
G3.setEdgeNum ( 9 );
G3.creteGraph ( G3, DN );
G3.setEdge ( G3, 1, 2, 50 );
G3.setEdge ( G3, 2, 3, 40 );
G3.setEdge ( G3, 4, 3, 50 );
G3.setEdge ( G3, 5, 4, 50 );
G3.setEdge ( G3, 6, 5, 10 );
G3.setEdge ( G3, 6, 1, 30 );
G3.setEdge ( G3, 1, 4, 70 );
G3.setEdge ( G3, 4, 6, 60 );
G3.setEdge ( G3, 3, 6, 90 );
G3.printGraph ( G3 );
return 0;
}
好吧,遇到问题还是需要多思考,多Google,可能会有意想不到收获。