今天在寫圖的相關的程序,寫着寫着就出現了一個問題,如標題。
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,可能會有意想不到收穫。