關於GCC編譯程式報出警告:note: neither the destructor nor the class-specific operator delete will be called...的問題及解決方案
在任意Linux系統,GCC 4.1.1或4.1.2環境下,建立以下工程:
Makefile
test: test.o agg.o
g++ -o test test.o agg.o
agg.o: agg.cpp agg.h base.h
g++ -g -c -Wall -o agg.o agg.cpp
test.o: test.cpp agg.h base.h
g++ -g -c -Wall -o test.o test.cpp
test.cpp
#include "agg.h"
int main(int, char**)
{
CAggretive obj;
return 0;
}
base.h
#include <stdio.h>
class CBase
{
public:
CBase() { printf("CBase/n"); }
~CBase() { printf("~CBase/n"); }
};
agg.h
#include <memory>
class CBase;
class CAggretive
{
public:
CAggretive();
~CAggretive() { printf("~CAggretive/n"); }
private:
std::auto_ptr<CBase> m_obj;
};
agg.cpp
#include "agg.h"
#include "base.h"
CAggretive::CAggretive() : m_obj(new CBase())
{
printf("CAggretive/n");
};
編譯後得到目標可執行檔案test。編譯時得到警告資訊:
g++ -g -c -Wall -o test.o test.cpp
/usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/memory: In destructor ‘std::auto_ptr<_Tp>::~auto_ptr() [with _Tp = CBase]’:
agg.h:9: instantiated from here
/usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/memory:259: warning: possible problem detected in invocation of delete operator:
/usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/memory:259: warning: invalid use of undefined type ‘struct CBase’
agg.h:3: warning: forward declaration of ‘struct CBase’
/usr/lib/gcc/i386-redhat-linux/4.1.1/../../../../include/c++/4.1.1/memory:259: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
g++ -o test test.o agg.o
執行可執行檔案test,程式輸出:
CBase
CAggretive
~CAggretive
可以發現CBase的解構函式沒有被呼叫,在實際應用中將造成記憶體和資源洩漏。
這是因為編譯test.cpp時,包含檔案樹中沒有base.h,所以編譯器在實體化auto_ptr<CBase>類時不知道如何執行delete CBase型資料結構的方法。
在test.cpp最開頭加上一句#include "base.h"即可解決此問題。編譯agg.cpp時沒有給出警告便是此原因。
(那麼,為什麼編譯器知道如何new CBase型資料結構呢?那是因為CAggretive的建構函式是寫在agg.cpp中,因此它的實現在agg.o中已經編譯進去了)
因為像agg.h這樣前置宣告CBase,然後在類成員中使用它是一種不好的習慣,任何沒有包含base.h而直接使用agg.h的cpp檔案在編譯時都會產生洩漏。
實際應用中可能會出現比本例更為複雜的引用情況,修改方式可能不那麼簡單。但總體思路是一樣的,即是讓編譯器在編譯此檔案時確實包含用作auto_ptr的模板引數的類。