这个构造函数的用法术语是什么?

5

我的一个同事写了下面的代码,我相信这是错误的。

我想向他解释问题,但不知道正确的术语,因此无法找到支持我的立场的参考资料:

他的代码:

BSTR someString = _bstr_t(L"Hello World");

为什么我认为这是错误的:
我认为_bstr_t(L"Hello World");调用了_bstr_t的构造函数,并创建了一个短暂的临时变量。该临时变量将在此代码行之后(分号序列点之后)立即被自动删除,其字符串空间被释放。这将导致someString指向已被释放的内存。

问题:
那个构造函数的正确术语是什么?

你能指出一些详细描述使用的参考资料/术语/页面吗?

是否有一个临时的_bstr_t对象术语?
我想我会称其为“匿名临时变量”,但我不知道这是否在技术上准确。

(或者也许我的分析完全错误....如果是这样,我很想知道)


澄清:

_bstr_t是C++类,通常由Microsoft用于包装他们的BSTR类型,因此它具有构造函数/析构函数/运算符等。

BSTR是一个WCHAR*的typedef,因此它没有任何逻辑。它只是一个愚笨的指针。

2个回答

5

你是正确的。

BSTRwchar_t * 的 typedef,CComBSTR/_bstr_t 有一个非常量转换运算符到 wchar_t *

因此,会分配一个临时的 _bstr_t,通过转换运算符将其开头的指针分配给 someString,并在对象超出作用域时释放它。然后你就得到了一个悬空指针。

你可以使用

_bstr_t someString ("Hello World");

或者甚至使用_bstr_t someString = "Hello, World";代替。


很高兴你同意。我的问题是关于“作用域”的。我通常认为,一个对象的作用域是括号对之间的范围,从左大括号{一直到右大括号}。但是这里没有括号……那么到底是什么定义了临时_bstr_t的“作用域”呢? - abelenky
2
@abelenky:我不是语言律师——也许你可以在SO上直接问这个问题,人们会很乐意回答。我确定的是,在赋值之后析构函数被调用。与const _bstr_t& someString = "Hello, World"相反,它调用了复制构造函数,并且其范围由括号限定。 - Alexandre C.
1
@abelenky:相关内容:https://dev59.com/v07Sa4cB1Zd3GeqP3W_m - Alexandre C.

3

所涉及的代码

 BSTR someString = _bstr_t(L"Hello World");

正在进行一个通过传入的wchar_t[]创建转换构造函数调用,从而创建了一个bstr_t实例。这本身是正常的。例如,如果您想调用一个接受BSTR并将字符串字面量传递给它的函数,您可以轻松地执行此操作:

someFunction( _bstr_t(L"Hello World") ); // OKAY

这是可以的,因为临时变量会存活到整个语句结束的位置,而语句以分号结束(这就是C++中临时变量的工作方式)。

然而,问题中的代码不正确,因为_bstr_t实例被用于实例化一个BSTR实例(使用类bstr_t中的转换运算符),该实例的生命周期比临时变量更长(临时变量在分号处被销毁,而BSTR指针someString则存在得更久)。因此,您会得到一个悬空的BSTR指针someString,使用它会导致未定义的行为。如果OLE堆选择将用于字符串的内存映射到进程地址空间中,则甚至可能看起来工作正常。

_bstr_t class的实现已经提供了(在Windows SDK中的comutil.h文件中),所以您可以使用调试器进入代码并查看在创建临时变量时有一个SysAllocString()调用,然后在销毁临时变量时有一个SysFreeString()调用,并且后者发生在代码继续执行下一行之前。因此,在代码继续执行下一行之前,OLE堆中的字符串对象已被释放,someString指针在问题代码后立即悬空。我想这足以说服即使是最怀疑的人。

因此,是的,您是正确的,该代码是错误的。正确的代码应该是:

 _bstr_t someString(L"Hello World");

谢谢,"转换构造函数" 这个术语正是我在寻找的。 - abelenky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接