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的完整類型,沒辦法確定兩件事情:

  1. X有沒有自定義的析構函數(准確的說,有沒有non-trivial的析構函數)。
  2. X有沒有自定義的operator delete函數。

在不確定這兩件事情的情況下,編譯器只能按最普通的方式去處理delete x

  1. 不調用任何析構函數。
  2. 調用全局的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();
}

相關文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM