1. ホーム
  2. C++

c++は、ダブルフリーまたは破損(fasttop)が表示されます。

2022-02-21 01:47:38
<パス <ブロッククオート

今日、グラフに関連するプログラムを書いていて、タイトルのように書いていて困ったことがありました。

1. 質問コード

ここでの目的は、主に隣接行列を使ったグラフの作成で、主なコードは主にこのブログを参考にしています。 C++】グラフの定義と性質

#include 

#include 


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::concreteGraph(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.concreteGraph ( 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. 解決する

まずは問題をググって、これらの問題のおおよその解決策をネットに掲載します。

  1. ダブルフリーまたはコラプション(fasttop)
  2. C++ポインタ "エラー: ダブルフリーまたは破損(out)"。
  3. メモリ例外解放の問題(二重解放と間違った解放)を見つけるためにvalgrindを使用した例

この出力欄を見て、結果が正しいことがわかり、ポインタを使ったと推測され、その後に続く 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回実行されました。なぜ?続けてください。

理由をググっています デストラクタが2回実行される と、本当に関連する質問を与えてくれました なぜデストラクタが2回呼ばれるのですか? すごい!驚いたことに、同じ答えがこのブログで紹介されています。

<ブロッククオート

これは、システムがデフォルトのコピーコンストラクタを呼び出した結果であることが判明しています。オブジェクトを返すときや引数を値で渡すときに、一時的なオブジェクトが生成され、その一時的なオブジェクトを生成するためにデフォルトのコピー・コンストラクタが呼び出されるのです。この例で、Effective C++の理解が深まりました。クラス内にポインタ変数がある限り、コピーコンストラクタや代入関数は自分で書かなければならないが、それらが必要ないことが確認できたら、実装せずにプライベート宣言することで、誰かが呼び出すのを防ぎ、コンパイラが生成するのを防ぐことができるのである。

この文章を読んだとき、何が書いてあるのか最初は少し混乱し、仕方なくC++ Primerを出してきてパラパラと読んでみました:the



ここで注目の言葉 デフォルトのコピーコンストラクタは、返されたオブジェクトに値で引数が渡されたときに呼び出されます!

そこで、コピーされないようにするための機能表示を、そのまま、クラスに ブロックコピー ブロックアサイン

     AdjGraph(const AdjGraph&) = delete;
     AdjGraph &operator=(const AdjGraph&) = delete;


そして、コードを実行すると、次のようになります。

削除された機能を使用するものは、よく ブロックコピー 削除された機能を使ってみよう~~~ちょっと待てよ、急に気がついたことがあるんだが、そういえば printGraph フォームパラメータは値として渡されたので、前の文はシステムのデフォルトを呼び出したことになる コピーコンストラクタ で、プログラムの最後に、2番目の デストラクタ というように、2番目の 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 のように、値の代わりに参照が渡され、デフォルトのコピーコンストラクタは呼ばれず、デストラクトが1回だけ発生するようにします。ブロッキングコピーとブロッキング代入については、削除しても残しても今のところプログラムにはほとんど影響がありません。

#include 

#include 


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::concreteGraph(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) {
       

まあ、それでも問題にぶつかったらもっと考えてググる必要があるし、思わぬ結果が出るかもしれない。