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();
}
相关文献
- http://stackoverflow.com/questions/4325154/delete-objects-of-incomplete-type
- http://stackoverflow.com/questions/4023794/forward-declaration-just-wont-do
C++:delete不完整类型的指针的更多相关文章
- C语言的不完整类型和前置声明(转)
声明与定义(Declaration and Definition) 开始这篇文章之前,我们先弄懂变量的declaration和definition的区别,即变量的声明和定义的区别. 一般情况下,我们这 ...
- 为什么C++类定义中,数据成员不能被指定为自身类型,但可以是指向自身类型的指针或引用?为什么在类体内可以定义将静态成员声明为其所属类的类型呢 ?
static的成员变量,不是存储在Bar实例之中的,因而不会有递归定义的问题. 类声明: class Screen: //Screen类的声明 1 类定义: class Screen{ //Scree ...
- 从默认析构函数学习c++,new,delete,内存泄漏,野指针
默认析构函数:当系统没有显式定义析构函数,编译器同样会为对象定义一个默认析构函数,默认的析构函数只能释放普通数据成员所占用的空间,无法通过释放通过new和malloc进行申请的空间,因此避免内存泄漏, ...
- 不同类型的指针+1之后增加的大小不同(a,&a的地址是一样的,但意思不一样)
main() { ]={,,,,}; ); printf(),*(ptr-)); } *(a+1)就是a[1],*(ptr-1)就是a[4], 执行结果是2, 5.&a+1不是首地址+1,系统 ...
- C++:不同类型的指针的本质与差异
转自:http://blog.csdn.net/richerg85/article/details/10076365 指针的类型(The Type of a Pointer) 一 ...
- 直接修改托管堆栈中的type object pointer(类型对象指针)
都知道.NET是一个强对象类型的框架. 那么对于对象类型又是怎么确定的呢. 最初的我简单认为数据的类型就是定义时字段的类型修饰决定的(回来发现这种观点是绝对错误的) 我们知道引用对象存储在托管堆栈中, ...
- C# CLR via 对象内存中堆的存储【类型对象指针、同步块索引】
最近在看书,看到了对象在内存中的存储方式. 讲到了对象存储在内存堆中,分配的空间除了类型对象的成员所需的内存量,还有额外的成员(类型对象指针. 同步块索引 ),看到这个我就有点不懂了,不知道类型对象指 ...
- LPVOID 没有类型的指针
可以将LPVOID类型的变量赋值给任意类型的指针,比如在参数传递时就可以把任意类型传递给一个LPVOID类型为参数的方法,然后在方法内再将这个“任意类型”从传递时的“LPVOID类型”转换回来. 示例 ...
- python_递归实现汉诺塔 (string类型的指针出错 未解决)
在递归的时候,和数学的归纳法一致. void func( mode) { if(endCondition) { constExpression //基本项 } else { accumrateExpr ...
随机推荐
- 浅谈Android View滑动冲突
引言 上一篇文章我们从源码的角度介绍了View事件分发机制,这一篇文章我们就通过介绍滑动冲突的规则和一个实例来更加深入的学习View的事件分发机制. 1.外部滑动方向和内部滑动方向不一致 考虑这样一种 ...
- linux命令:linux权限管理命令
权限管理命令 文件的权限只有你两个人可以更改,一个是root,一个是文件所有者. 命令名称:chmod 命令英文原意:change the permissions mode of a file ...
- select,radio,checkbox兼容性
- Codeforces Round #440 (Div. 2, based on Technocup 2018 Elimination Round 2) C. Maximum splitting
地址: 题目: C. Maximum splitting time limit per test 2 seconds memory limit per test 256 megabytes input ...
- Qt 学习之路 2(55):数据库操作
Qt 提供了 QtSql 模块来提供平台独立的基于 SQL 的数据库操作.这里我们所说的“平台独立”,既包括操作系统平台,又包括各个数据库平台.另外,我们强调了“基于 SQL”,因为 NoSQL 数据 ...
- MATERIALIZED VIEW-物化视图
Oracle的实体化视图提供了强大的功能,可以用在不同的环境中,实体化视图和表一样可以直接进行查询.实体化视图可以基于分区表,实体化视图本身也可以分区. 主要用于预先计算并保存表连接或聚集等耗时较多 ...
- Socket和ServletSocket的交互
ServerSocket(int port) 是服务端绑定port端口,调accept()监听等待客户端连接,它返回一个连接队列中的一个socket. Socket(InetAddress addre ...
- ACM题目————困难的串
题目描述 如果一个字符串包含两个相邻的重复子串,则称他是“容易的串”,其他串称为"困难的串".例如,BB,ABCDACABCAB,ABCDABCD都是容易的串,而D,DC,ABDA ...
- python中的property
提示:这篇博文参考了两个博客,第一篇博文地址为:https://www.cnblogs.com/Lambda721/p/6132206.html,另一篇博文地址如下:关于python的property ...
- 函数对象与仿函数(function object and functor)
part 1. 仿函数在STL组件中的关系 如下图: # 仿函数配合算法完成不同的策略变化. # 适配器套接仿函数. part 2. 仿函数介绍 传递给算法的“函数型实参”不一定得是函数,可以是行为类 ...