最近在改一個C++程式的時候碰到一條警告資訊,警告資訊為:“
刪除指向不完整“Q2DTorusNode”型別的指標;沒有呼叫解構函式
1> c:userslxwdesktopdragonfly第二階段實驗最終的實驗版本實驗目錄dragonfly_modifysrcQ2DTorus.h(6) : 參見“Q2DTorusNode”的宣告
”
警告資訊很是奇怪,其實出於強迫症的原因想要解決掉這個警告資訊,而且從警告資訊來看,程式也應該存在記憶體洩露的問題,因為警告直接明白告訴你了,沒有呼叫解構函式,接下來就是我解決的過程。我會搭建一個簡單的程式來模擬這個錯誤,因為程式是在有些多~
警告的來源:
一個標頭檔案A.h包含class A的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef AH #define AH class B; class A { B *memb; A() { } ~A() { delete memb; } }; #endif |
一個標頭檔案B.h包含class B的程式碼如下:
1 2 3 4 5 6 |
#ifndef BH #define BH class B { }; #endif |
此時編譯就會產生類似上面的警告資訊:warning C4150: 刪除指向不完整“B”型別的指標;沒有呼叫解構函式。
原因分析:
因為class A中B的宣告依賴於class B的前置宣告,而不是#include “B.H”,所以B的定義對A來說不可見,所以無法呼叫解構函式,導致記憶體洩露。
程式的變化
此時如果class A和class B相互保持對方型別的成員會如何呢?
A.h的程式碼:
1 2 3 4 5 6 7 |
#ifndef AH #define AH class B; class A { B b; }; #endif |
B.h的程式碼:
1 2 3 4 5 6 7 |
#ifndef BH #define BH #include "A.h" class B { A a; }; #endif |
這段程式碼存在問題,因為如果靜態定義物件A,B,此時必定存在一個物件的定義對於另外一個物件的定義不可見,所以定義失敗。如果均是利用#include對方,取決於編譯器的順序必定一個定義不可見。然而前置宣告不能定義物件。
解決方案:
此種狀況的解決利用前置宣告定義的那個類中的保持另外一個類的引用定義為指標,定義指標時不需要對那個類的定義可見。
另外的問題:
A.h
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef AH #define AH class B; class A { B* b; void setB() { b->haha(); } ~A() { delete b; } }; #endif |
B.h
1 2 3 4 5 6 7 8 9 10 |
#ifndef BH #define BH #include "A.h" class B { A a; void haha() { } }; #endif |
但是利用前置宣告導致定義指標成員的類會出現最開始說的warning警告,因為定義不可見的原因。
“warning C4150: 刪除指向不完整“B”型別的指標;沒有呼叫解構函式”
而且另外的一個問題是在該.h檔案中不能使用該指標呼叫這個類的成員,原因也是定義不可見。
“error C2227: “->haha”的左邊必須指向類/結構/聯合/泛型型別”
解決方案:
此時需要將A.h的所有成員函式實現重新定義一個.cpp檔案,然後該.cpp檔案去#include 指標成員類的標頭檔案宣告,此時定義可見,即可定義解構函式,呼叫指標的類成員了。
A.h
1 2 3 4 5 6 7 8 9 10 |
#ifndef AH #define AH class B; class A { public: B* b; void setB(); ~A(); }; #endif |
B.h
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef BH #define BH #include "A.h" class B { public: A a; void haha() { } }; #endif |
A.cpp
1 2 3 4 5 6 7 8 |
#include "A.h" #include "B.h" A::~A() { delete b; } void A::setB() { b->haha(); } |
問題到此就解決完畢了~