CComBSTR的生命期

CComBSTR会自动管理字符串的内存空间,在析构时释放空间。由于C++对象在出其作用域时会进行析构。所以有一些情形下,使用CComBSTR容易犯下错误。
来看以下代码:

    BSTR bstr1 = CComBSTR(L"hello");
    BSTR bstr2 = CComBSTR(L"world");

    TRACE(L"%s, %s\n", bstr1, bstr2);

本来可能预期输出的hello, world
而实际输出是world, world

这是因为在赋值语句完成之后,CComBSTR(L”hello”)就析构了释放了空间,bstr1指向了这块被释放的内存。在随后的赋值语句中,CComBSTR(L”world”)又利用了这块内存。然后bstr1和bstr2就指向了同一块内存。

修改代码如下:

    BSTR bstr1 = CComBSTR(L"hello");
    BSTR bstr2 = CComBSTR(L"world");

    TRACE(L"%s, %s\n", bstr1, bstr2);
    TRACE(L"bstr1[%x] %s\n", bstr1, bstr1);
    TRACE(L"bstr2[%x] %s\n", bstr2, bstr2);

输出结果为:
world, world
bstr1[6f8b94] world
bstr2[6f8b94] world

从其汇编码也可以看出:

BSTR bstr1 = CComBSTR(L"hello");
011B76D2  mov         eax,120DDB0h
011B76D7  test        eax,eax
011B76D9  jne         testBSTR+44h (11B76E4h)
011B76DB  mov         dword ptr [ebp-38h],0
011B76E2  jmp         testBSTR+75h (11B7715h)
011B76E4  mov         esi,esp
011B76E6  push        120DDB0h
011B76EB  call        dword ptr [[email protected]4 (16EF3DCh)]
011B76F1  cmp         esi,esp
011B76F3  call        _RTC_CheckEsp (11F1FF0h)
011B76F8  mov         dword ptr [ebp-38h],eax
011B76FB  xor         ecx,ecx
011B76FD  cmp         dword ptr [ebp-38h],0
011B7701  sete        cl
011B7704  movzx       edx,cl
011B7707  test        edx,edx
011B7709  je          testBSTR+75h (11B7715h)
011B770B  push        8007000Eh
011B7710  call        ATL::AtlThrowImpl (11B1109h)
011B7715  lea         eax,[ebp-38h]
011B7718  mov         dword ptr [ebp-88h],eax
011B771E  mov         ecx,dword ptr [ebp-88h]
011B7724  mov         edx,dword ptr [ecx]
011B7726  mov         dword ptr [bstr1],edx
011B7729  mov         esi,esp
011B772B  mov         eax,dword ptr [ebp-38h]
011B772E  push        eax
011B772F  call        dword ptr [[email protected]4 (16EF3D8h)]
011B7735  cmp         esi,esp
011B7737  call        _RTC_CheckEsp (11F1FF0h)
    BSTR bstr2 = CComBSTR(L"world");

在bstr2赋值语句之前就已经调用__imp_SysFreeString释放空间了。

解决办法是将CComBSTR作用域加大,比如构造作用域在函数范围内的两个变量str1和str2。

代码如下:

    CComBSTR str1(L"hello");
    CComBSTR str2(L"world");

    TRACE(L"%s, %s\n", str1, str2);
    TRACE(L"str1[%x] %s\n", str1, str1);
    TRACE(L"str2[%x] %s\n", str2, str2);

输出结果如下:
hello, world
str1[107bd5c] hello
str2[107bd34] world

从反汇编可以看出__imp__SysFreeString是在函数退出后调用。

结论:

在使用CComBSTR时,尽量不要做复杂操作,而且不要让其出现在等号右边。CA2T之类的也是。MSDN上说的比较详细,还有关于CComBSTR导致内存泄露的,具体参见MSDN。

CComBSTR的生命期

时间: 2024-10-07 07:04:13

CComBSTR的生命期的相关文章

面向服务开发中三层架构中事务单元的生命期管理

    经典的三层分层结构,控制层(Control),服务层(Service),持久层(Repository)应用广泛,在面向服务(SOA)的架构中,配合DI.IOC实现开放灵活的技术架构.     SOA中,Respository面向数据访问,提供访问数据库.文件.或其他业务接口提供持久能力.Service面向业务,提供访问业务功能的接口,使用领域模型描述业务需求,方便产品人员.需求人员和客户沟通理解业务流程.最后,Control面向业务流程整合,提供基于事务的需求实现.     事务,用需求

使用无限生命期Session的方法

本文不敢说非常好,但是笔者相信大多数PHPer都曾经想过这些问题. 在PHP4.0中加入了对Session的支持,方便了我们很多程序,比如购物车等等! 在很多论坛中,Session也用于处理用户的登陆,记录下用户名和密码,使得用户不必每次都输入自己的用户名和密码!但是一般的Session的生命期有限,如果用户关闭了浏览器,就不能保存Session的变量了!那么怎么样可以实现Session的永久生命期呢? 大家知道,Session储存在服务器端,根据客户端提供的SessionID来得到这个用户的文

Android百日程序: Fragment动态管理和生命期

之前写过Fragment使用的程序,Fragment可以静态,也可以动态载入内存中的,这一章进一步看看如何动态地更换Fragment和看看Fragment生命期都有什么函数. 本章利用响应菜单点击事件,轮流载入不同的Fragment,显示不同的界面,效果如下: 开始的是没有载入Fragmen为空白: 点击菜单的NEXT FRAGMENT VIEW,就进入下一个界面,载入两个: 继续点击显示Fragment 1: 继续点击,显示Fragment2: 然后就是循环了: 如此循环显示不同画面. 一 首

可见性和生命期的区别

变量的可见性(即作用域)和生命期既有区别,又有联系. 一个变量在程序的某处没有可见性,但生命期任然可以存在,比如局部静态变量的可见性在它所在的函数里面,在函数外部不可访问,但是它的生命期确是整个程序的运行期间:而一个变量生命期结束,它也就没有了可见性,就像局部变量,随着栈空间的释放,它也就不存在了,所以外部无法访问. 决定一个变量的可见性是变量被声明的位置(就像局部静态变量,由于在函数里面声明,所以只能在函数里面可见),而决定一个变量的生命期是变量在内存中的位置(就像静态局部变量不是分配在栈上,

Android百日程序: Activity的生命期

到了Android 4 的activity的生命期内的函数有7个,变得十分复杂,完善.故此要掌握好Activity的各个函数,知道应该在那个函数写什么逻辑代码. 本篇博客写个小程序测试一下activity的声明周期如何运作. 首先总结一下: 1 onCreate():activyt第一次创建的时候调用,还可能在改activity在onStop或者onPauce状态下被系统自动释放,那么当用户需要继续回到改activity的时候,也会调用这个onCreate()函数 2 onStart():当改a

使用 JointCode.Shuttle 管理远程服务对象的生命期

JointCode.Shuttle 是一个用于进程内 AppDomain 间通信的服务架构(不支持跨进程). 一般情况下,在进行跨 AppDomain 调用时,大部分人选择使用运行时库默认提供的.基于 MarshalByrefObject 类继承的通信机制.代码也很简单,例如: 1 namespace JoitCode.Shuttle.SimpleSample 2 { 3 public class MyService : MarshalByRefObject 4 { 5 public void

线程安全的对象生命期管理

线程安全的对象生命期管理 解决对象构造的线程安全 当我们需要动态分配对象时( 懒加载 ), 如果在多个线程中运行这一部分代码,可能出现多次初始化的问题. 单例模式 c++ 11 之后static 变量的初始化是线程安全的.可以利用静态变量来实现单例模式,解决重复初始化的问题. std::call_once 个人感觉不够优美,要传递一个flag 来标识初始化情况 线程访问未初始化的资源 比如在Thread类中,std::thread被构造之后立即开始执行线程中函数,然而函数中访问的资源可能还没有被

项目管理系列(2):项目生命期和项目过程

1.填空 1.1 项目阶段与项目生命期 1. 每一个项目阶段都以一个或数个可交付成果的完成为标志. 2. 把项目分为多个节点的目的在于确保达到本阶段的目标并通过评估验收. 3. 项目绩效评审的目标是决定项目是否可以进入下一个阶段. 4. 软件开发生命周期包括调试. 5. 只有对需求进行确认,才能作为后续开发的基础,从而减少风险. 6. 项目的需求会发生变更,允许变更,但要规范变更,尽量减少变更. 7. 在项目策划阶段,变更成本较低. 8. 一般情况下,随着项目的逐渐进展,成本和人员投入呈现出先增

C++构造析构函数生命期及对象生命期