很久以前写在百度空间的这篇文章:
[百度空间] [原] 全局operator delete重载到DLL
首先,纠正一个词“重载”,operator new/delete是替换(replacement),不是重载。只要任意一个编译单元定义了替换函数,并不需要全局声明,不用包含头文件,就会替换掉全局的默认operator new/delete, 这个行为非常像gcc的strong symbol/weak symbol。
最近翻看了C++03的标准:
3. The program’s definitions are used instead of the default versions supplied by the implementation (18.6). Such replacement occurs prior to program startup(3.2, 3.6). The program’s definitions shall not be specified as inline. No diagnostic is required.
也就是说标准是不允许inline operator new/delete。原因也很简单, new/delete是replacement,是链接级别的符号替换。如过是inline的话,很有可能没有生成符号,也就无法实现replacement,导致只有包含该头文件,引用声明的编译单元才会有replacement, 这样的行为与“全局替换”相悖。
然而标准又说No diagnostic is required, 估计是因为兼容性的原因,毕竟这个标准出来的时候,已经有很多编译器支持这么做了。所以MSVC最多会给一个警告,Clang后面也改成了可关闭的警告。
所以使用inline operator new/delete是不符合标准的,可能产生问题的,当然,有把握(运气)的话,并不会出错。比如我之前一直在用, 一些有名的开源项目也在用。
但这个方式给我一个很大的便利,就是可以fall back to default operator new:
默认情况下,只要任何一个文件定义了operator new/delete, 就会replacment,所以默认的new/delete已经被覆盖了,已经没有办法可以访问。
在使用了inline operator delete以后, 在自定义的operator delete里,仍然可以调用默认的operator delete。
//global.h
extern void __declspec( dllexport ) BaldeGlobalDelete(void* ptr); inline void operator delete(void* ptr)
{
return BaldeGlobalDelete(ptr);
} //mem.cxx
#include <global.h> extern void DefaultDelete(void* ptr); void BaldeGlobalDelete(void* ptr)
{
//note this is a demo code that not used in practice
int* p = (int*)ptr-;
int magic = *p;
if(magic == BLADE_MEM_MAGIC)
BladeInternalFree(p);
else
DefaultDelete(ptr);
} //defaultdelete.cxx
//this file doesn't include global.h
//inline operator delete is not visible and no replacement happens!
//if define operator delete without inline(and in .cpp), replacement always happens, whether its decl is visible or not (correct/std conformed behavior)
void DefaultDelete(void* ptr)
{
//call the built in delete
return ::operator delete(ptr);
}
如过不加inline(并把定义写在cpp里)的话,replacement必然发生,所以DefaultDelete不会调用默认delete,而会递归调用自定义的operator delete,BaldeGlobalDelete,这样栈就溢出了。
有了inline(MSVC的debug build还需要改优化选项启用inline), 诡异的事情就发生了,实际上没有真正的replacment, 只要不包含global.h头文件,就能fall back到默认的delete。
这个是非常tricky的方式, 主要为了保证new/delete的一致性,兼容三方默认的new。其实可移植性和稳定性都不好,算是反面教材。最近为了standards-conformation,去掉了这种hack,使用了更标准的方式, 去掉了global operator delete 的替换。
关于DLL boundary的问题。
有很多方式可以解决这个问题,比如类COM的方式 (virutal destroy), 或者factory + deleter,或者使用统一的allocation routine (比如LocalAlloc之类).
Blade使用以下规范来保证:
1.跨DLL类 必须是纯虚基类, 或者,虚析构+类内自定义operator new/delete, 这样的便利是,可以直接跨DLL delete, C++标准保证。
基础类型的分配和释放,比如new int[count], 不允许跨DLL管理,实际上,不允许跨函数管理。个人认为strdup这种需要用户调用free的设计不是良好的设计。
使用shared_ptr + deleter是最好的方式,但目前暂不考虑,因为现在仍然基于C++03.
2.公共头文件不允许包含<vector> <string>等容器, 以避免不同版本stl(或者相同版本下debug、release库)对象的内存分布问题。 具体方法可以用纯虚类接口,或者Pimpl。
对于跨DLL的情况,不管用以上的哪种方式,对于DLL可卸载的情况,仍然会有问题,比如DLL/so卸载以后, 不管是virtual destory还是deleter,因为析构函数的二进制可执行代码已经unmap出进程地址空间不可访问,所以需要良好的
生命周期管理和cleanup routine。也可以偷懒,禁止卸载,比如使用GET_MODULE_HANDLE_EX_FLAG_PIN/RTLD_NODELETE。
[原] inline operator delete & DLL boundary的更多相关文章
-
[百度空间] [原] 全局operator delete重载到DLL
由于很久没有搞内存管理了,很多细节都忘记了今天项目要用到operator delete重载到DLL,发现了问题,网上搜索以后,再对比以前写的代码,发现了问题:原来MSVC默认的operator new ...
-
Effective C++ 第二版 10) 写operator delete
条款10 写了operator new就要同时写operator delete 写operator new和operator delete是为了提高效率; default的operator new和o ...
-
operator new和operator delete
从STL源码剖析中看到了operator new的使用 template<class T> inline void _deallocate(T* buffer) { ::operator ...
-
条款十: 如果写了operator new就要同时写operator delete
为什么有必要写自己的operator new和operator delete? 答案通常是:为了效率.缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也 ...
-
C++中的::operator new, ::operator delete
一般在使用new 和 delete的时候,做了两件事情,一是空间的配置( new 是分配,delete是回收),而是调用对象的析构函数 但是也有办法将这两个过程分开 那就是显式的调用::operat ...
-
C++ new operator, delete operator, operator new, operator delete, new placement
http://www.younfor.com/cpp-new-placement-new-operator-new.html http://www.cnblogs.com/luxiaoxun/arch ...
-
C++中的new/delete与operator new/operator delete
new operator/delete operator就是new和delete操作符,而operator new/operator delete是函数. new operator(1)调用opera ...
-
微信SDK导入报错 Undefined symbols for architecture i386:";operator delete[](void*)";, referenced from:
异常信息: Undefined symbols for architecture i386: "operator delete[](void*)", referenced fro ...
-
Effective C++ 第二版 8) 写operator new 和operator delete 9) 避免隐藏标准形式的new
条款8 写operator new 和operator delete 时要遵循常规 重写operator new时, 函数提供的行为要和系统缺省的operator new一致: 1)正确的返回值; 2 ...
随机推荐
-
Nginx-解读内置非默认模块 ngx_http_stub_status_module
1.Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...
-
css通用小笔记02——浮动、清除(三个例子)
css中通常会用到浮动与清除,也是一个必须掌握的知识点,概念性的东西不多说,下面举几个例子,来说明它的用法:1.文字环绕效果 2.多个div并排显示 3.清除浮动(默认显示) 一.文字环绕效果: h ...
-
SQL中的日期时间函数
之所以把日期时间函数单独拿出来回顾一下,是因为这一部分的内容比较独立,C#中也有类似的日期时间函数,趁着想得起来,写个标题先.
-
Mysql 5.1升级为mysql 5.6遇到的问题及解决方式
yum是不可行的.因为yum源没更新,我已经使用了163网易的源,但是还是不行.最新版仍然不是5.6.没办法,mysql分区是5.5之后的功能,要使用分区功能,就必须升级.. 去官网下载地址:http ...
-
ecshop 2.x 3.x sql injection/rce payload
首先,感谢ringk3y的分析:http://ringk3y.com/2018/08/31/ec ... %E6%89%A7%E8%A1%8C/ 大家跟一遍代码基本上都能弄明白漏洞的原理,整个漏洞的构 ...
-
关于SQL配置管理工具无法打开0x8004100e问题!
今天犯了个很“2”得问题,因为在远程数据库可以访问,并且也在安装程序中有看到装有SQLserver Mamngement Studio及其它程序,所以想在本地使用数据库应该可以但没想却总是报SQL配置 ...
-
C#保留小数点后几位
String.Format("{0:N1}", a) 保留小数点后一位 String.Format("{0:N2}", a) 保留小数点后两位 String.F ...
-
C_C++变量命名规则
变量命名规则是为了增强代码的可读性和容易维护性.以下为C++必须遵守的变量命名规则: 变量名只能是字母(A-Z,a-z)和数字(0-9)或者下划线(_)组成. 第一个字母必须是字母或者下划线开头. 不 ...
-
jquery中ajax使用error调试错误的方法
JQuery使我们在开发Ajax应用程序的时候提高了效率,减少了许多兼容性问题,我们在Ajax项目中,遇到ajax异步获取数据出错怎么办,我们可以通过捕捉error事件来获取出错的信息. jquery ...
-
python--第十二天总结(Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy)
Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...