C++:delete不完整類型的指針
簡單版
以下代碼編譯時會有warning:
class X;
void foo(X* x) {
delete x;
}
在GCC4.1.2下,編譯出錯信息是:
warning: possible problem detected in invocation of delete operator:
warning: ‘x’ has incomplete type
warning: forward declaration of ‘struct X’
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
這是因為在foo
裏,編譯器看不到X
的完整類型,沒辦法確定兩件事情:
-
X
有沒有自定義的析構函數(準確的說,有沒有non-trivial的析構函數)。 -
X
有沒有自定義的operator delete
函數。
在不確定這兩件事情的情況下,編譯器隻能按最普通的方式去處理delete x
:
- 不調用任何析構函數。
- 調用全局的
operator delete
,通常來說就是直接釋放內存。
日常版
有一個我們平常會遇到的場景,就會觸發上麵這個問題。
以下是由三個文件組成的一個工程,其中用到了'pImpl'方法來隱藏實現,因此在接口類中放了一個std::auto_ptr
,很簡單:
// test.h
#include <memory>
class A {
class Impl;
public:
A();
void Func();
private:
std::auto_ptr<Impl> mImpl;
};
// test.cpp
#include "test.h"
#include <iostream>
class A::Impl {
public:
void Func() {
std::cout << "Func" << std::endl;
}
};
A::A(): mImpl(new Impl) {}
void A::Func() {
mImpl->Func();
}
// main.cpp
#include "test.h"
int main() {
A a;
a.Func();
}
看起來很正常,但編譯時有warning:
$g++ test.cpp main.cpp
In destructor ‘std::auto_ptr<_Tp>::~auto_ptr() [with _Tp = A::Impl]’:
test.h:4: instantiated from here
warning: possible problem detected in invocation of delete operator:
warning: invalid use of undefined type ‘struct A::Impl’
test.h:5: warning: forward declaration of ‘struct A::Impl’
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
和前麵說的warning信息完全一致,看起來也是在調用delete
時出的問題。但哪裏調用了delete
呢?
答案是std::auto_ptr
。
上麵的代碼中,我們沒有給class A
手動寫一個析構函數,因為編譯器自動生成的析構函數就是我們要的:析構時把mImpl
析構掉。
那麼自動生成的析構函數長什麼樣子呢?大概是:
A::~A() {
mImpl.~std::auto_ptr<Impl>();
}
展開了基本就是一句delete
:
A::~A() {
delete mImpl._M_ptr;
}
這個析構函數的位置在哪呢?C++標準裏說會把自動生成的類成員函數放在類的定義中,那麼就是在test.h中。
問題清楚了:我們在編譯main.cpp時,看不到A::Impl
的完整定義,但卻有一個自動生成的A::~A
,其中delete了一個不完整的類對象!
解法
手動寫一個A
的析構函數,位置要在能看到A::Impl
完整定義的地方,也就是test.cpp:
```cpp
// test.h
#include <memory>
class A {
class Impl;
public:
A();
~A();
void Func();
private:
std::auto_ptr<Impl> mImpl;
};
// test.cpp
#include "test.h"
#include <iostream>
class A::Impl {
public:
void Func() {
std::cout << "Func" << std::endl;
}
};
A::A(): mImpl(new Impl) {}
A::~A() {}
void A::Func() {
mImpl->Func();
}
相關文獻
- https://stackoverflow.com/questions/4325154/delete-objects-of-incomplete-type
- https://stackoverflow.com/questions/4023794/forward-declaration-just-wont-do
最後更新:2017-11-10 14:35:25