简单版

以下代码编译时会有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();
}

相关文献

C++:delete不完整类型的指针的更多相关文章

  1. C语言的不完整类型和前置声明(转)

    声明与定义(Declaration and Definition) 开始这篇文章之前,我们先弄懂变量的declaration和definition的区别,即变量的声明和定义的区别. 一般情况下,我们这 ...

  2. 为什么C++类定义中,数据成员不能被指定为自身类型,但可以是指向自身类型的指针或引用?为什么在类体内可以定义将静态成员声明为其所属类的类型呢 ?

    static的成员变量,不是存储在Bar实例之中的,因而不会有递归定义的问题. 类声明: class Screen: //Screen类的声明 1 类定义: class Screen{ //Scree ...

  3. 从默认析构函数学习c++,new,delete,内存泄漏,野指针

    默认析构函数:当系统没有显式定义析构函数,编译器同样会为对象定义一个默认析构函数,默认的析构函数只能释放普通数据成员所占用的空间,无法通过释放通过new和malloc进行申请的空间,因此避免内存泄漏, ...

  4. 不同类型的指针+1之后增加的大小不同(a,&a的地址是一样的,但意思不一样)

    main() { ]={,,,,}; ); printf(),*(ptr-)); } *(a+1)就是a[1],*(ptr-1)就是a[4], 执行结果是2, 5.&a+1不是首地址+1,系统 ...

  5. C++:不同类型的指针的本质与差异

    转自:http://blog.csdn.net/richerg85/article/details/10076365 指针的类型(The Type of a Pointer)            一 ...

  6. 直接修改托管堆栈中的type object pointer(类型对象指针)

    都知道.NET是一个强对象类型的框架. 那么对于对象类型又是怎么确定的呢. 最初的我简单认为数据的类型就是定义时字段的类型修饰决定的(回来发现这种观点是绝对错误的) 我们知道引用对象存储在托管堆栈中, ...

  7. C# CLR via 对象内存中堆的存储【类型对象指针、同步块索引】

    最近在看书,看到了对象在内存中的存储方式. 讲到了对象存储在内存堆中,分配的空间除了类型对象的成员所需的内存量,还有额外的成员(类型对象指针. 同步块索引 ),看到这个我就有点不懂了,不知道类型对象指 ...

  8. LPVOID 没有类型的指针

    可以将LPVOID类型的变量赋值给任意类型的指针,比如在参数传递时就可以把任意类型传递给一个LPVOID类型为参数的方法,然后在方法内再将这个“任意类型”从传递时的“LPVOID类型”转换回来. 示例 ...

  9. python_递归实现汉诺塔 (string类型的指针出错 未解决)

    在递归的时候,和数学的归纳法一致. void func( mode) { if(endCondition) { constExpression //基本项 } else { accumrateExpr ...

随机推荐

  1. Python中的类属性、实例属性与类方法、静态方法

    1.什么是类对象,实例对象 类对象:类名 实例对象:类创建的对象 2.类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和C++.Java中类的静态成员变量有点 ...

  2. PAT The Best Rank[未作]

    1012 The Best Rank (25)(25 分) To evaluate the performance of our first year CS majored students, we ...

  3. 企业中如何批量更改mysql中表的存储引擎?

    一.首先必须熟悉Mysql中有哪些基本的数据库,在mysql中database等价于schema,默认的基本库有四个:mysql,information_schema,performance_sche ...

  4. 使用点击二分图传导计算query-document的相关性

    之前的博客中已经介绍了Ranking Relevance的一些基本情况(Click Behavior,和Text Match):http://www.cnblogs.com/bentuwuying/p ...

  5. 测试开发-web测试要点

    参数输入考虑 参数数值包含1个.多个.很多个.null.参数值前后包含空格的2种情况   数字类型:正数.负数.0.0.0.+0.0.-0.0.指数.对数.分数.小数.复数.科学计数法的测试,全角的数 ...

  6. nginx 安装步骤

    1. 下载nginx,网址:http://nginx.org/download/ 2. 解压 tar -xzvf nginx-1.6.2.tar.gz 3. 建立映像文件:mount -t iso96 ...

  7. nginx反向代理-后端服务器组设置

    nginx服务器的反向代理时其最常用的重要功能之一,在实际工作中应用广泛,涉及的配置指令也比较多.下面会尽量详细地介绍对应的指令,及其使用状态. 反向代理一般是互联网需要向内网拉取资源,比如访问一个w ...

  8. ”MySQL索引“学习总结

    序 learn by doing 是最快的学习方式.在百度外卖研发中心,我每天工作接触数据库方面最多的就是"索引",另外面试官在面试时也一定会考察到索引. Part 1, Expl ...

  9. 20145306 《网络攻防》 MSF基础应用

    20145306张文锦<网络对抗>MSF应用 Adobe阅读器渗透攻击 两台虚拟机,其中一台为kali,一台为windows xp sp3,并保证两台虚拟机可以ping通. 实验过程 进入 ...

  10. 前端开发环境全面配置 --- mac OS

    Mac 开发配置 brew /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install ...