C++中用于对象构造的**成语

Posted

技术标签:

【中文标题】C++中用于对象构造的**成语【英文标题】:The ** idiom in C++ for object construction 【发布时间】:2010-06-08 22:30:27 【问题描述】:

在许多为您制作东西的 C++ API(基于 COM 的 API)中,指向所构造对象的指针通常需要作为 ** 指针(和该函数将为您构建并初始化它)

您通常会看到如下签名:

HRESULT createAnObject( int howbig, Object **objectYouWantMeToInitialize ) ;

-- 但你很少看到新对象作为返回值被传递。

除了人们希望看到错误代码之外,这是什么原因?对于更简单的操作,使用** 模式而不是返回的指针是否更好,例如:

wchar_t* getUnicode( const char* src ) ;

或者写成这样更好:

void getUnicode( const char* src, wchar_t** dst ) ;

我能想到的最重要的事情是记住释放它,而** 方式,出于某种原因,往往会提醒我我也必须释放它。

【问题讨论】:

在我看来像 C。在 C++ 中,他们只是将其作为引用传递或抛出异常。 为什么这么多答案都集中在 COM..?这只是其使用位置的一个示例——它也用于许多其他 C-API,例如 FMOD。 【参考方案1】:

“除了需要错误代码”?

是什么让你觉得还有一个。错误代码几乎是唯一的原因。该功能需要某种方式来指示失败。 C没有异常,所以它必须通过指针参数或返回值来做到这一点,并且返回值是惯用的,并且在调用函数时更容易检查。

(顺便说一句,** 意味着您必须释放对象并没有通用规则。情况并非总是如此,使用任意的东西来提醒您要清理的对象可能是个坏主意.)

【讨论】:

确实,您也可以用 C 语言进行 COM 编程。 COM 的跨语言设计和时代意味着它缺少很多现代 C++ 的东西,比如 C++ 异常。 哦,对了,我忘了提到那部分。是的,COM 是独立于语言和编译器的,这意味着它不能依赖异常(在某些语言中不存在异常,甚至在确实存在的 C++ 中,它们可能在编译器之间实现不同) 【参考方案2】:

我想到了两个原因。

首先实际上是错误代码。除了 C++,C 没有异常,COM 是一个 C-API。由于各种原因,许多基于 C++ 的项目也不喜欢使用异常。 在某些情况下,返回值不能表示错误,例如如果您的函数返回一个整数,则可能没有整数值,可以表示错误代码。虽然使用指针指示错误很容易(NULL == Error),但一些 API 设计人员更喜欢在所有函数中以一致的方式指示错误。

其次,函数只能有一个返回值,但调用它们可能会创建多个对象。如果使用非 NULL 指针调用这些函数,则某些 Win32 API 函数采用多个指向可以选择填充的指针的指针。您不能返回两个指针,或者如果返回值是包含多个指针的某个结构值,则使用起来会很尴尬。在这里,一致的 API 也是一个明智的目标。

【讨论】:

【参考方案3】:

** 传递的函数参数中的新对象更好。这让我对将来使用更改 voidbool 感到安慰,例如返回函数的成功或其他信息提供函数的工作。

一行回答:这对于生成的错误代码要好得多

【讨论】:

【参考方案4】:

除了人们希望看到错误代码之外,这是什么原因?

这有一些原因。其中之一是编写可在 C 中使用的接口(您可以在 WinAPI 和 Windows COM 中看到这一点)。

向后兼容性是另一个原因(即接口是这样编写的,现在破坏它会破坏现有代码)。

在使用这样的代码时,我会采用 C 兼容性作为设计原则。如果你用 C++ 写,你会写

retval Myfunction(Result *& output);

而不是

retval Myfunction(Result ** output);

或者(甚至更好):

Result *Myfunction();

并让函数在出错时抛出异常。

【讨论】:

【参考方案5】:

我不确定我是否同意这是最好的方法......这可能会更好:

Object * createAnObject(int howbig, HRESULT * optPlaceResultCodeHereIfNotNull = NULL);

这样就不会弄乱双重间接(这对于不习惯它的人来说可能有点棘手),并且不关心结果代码的人不必担心关于第二个参数......他们可以检查返回值是否为NULL。

实际上,由于它是 C++,您还可以使用函数重载让事情变得更简单:

Object * createAnObject(int howbig);
Object * createAnObject(int howbig, HRESULT & returnResultCode);

【讨论】:

顺便说一句,这就是 boost.asio 的编写方式——所有内容都被一个抛出 boost::system::system_error 的版本和一个接受 boost::system::error_code & 的版本重载【参考方案6】:

COM 调用中的任何方法调用必须是 HRESULT。返回代码在整个框架中得到利用,传递双指针是获取创建对象的众所周知的方法。

【讨论】:

【参考方案7】:

没有回答您的问题,而是发表评论,因为您的问题引发了我对使用 C++ 进行 COM/DCOM 编程的一些想法。

所有这些“指针”和“指向指​​针的指针”、内存管理和引用计数都是我回避使用 C++ 进行 COM 编程的原因。即使有 ATL,我也不喜欢它,原因很简单,它看起来不够自然。话虽如此,我确实使用 ATL 做了一些项目。

当时的替代方法是使用 VB。 VB 代码对于 COM 或 DCOM 编程看起来更自然。

今天,我将使用 C#。

【讨论】:

以上是关于C++中用于对象构造的**成语的主要内容,如果未能解决你的问题,请参考以下文章

构造函数用于创建类的实例对象,构造函数名应与类名相同,返回类型为void.

C++ 错误:没有用于初始化的匹配构造函数

C++ 错误:没有用于初始化的匹配构造函数

c++的复制构造函数

C++的默认构造函数与构造函数

C++提高:拷贝构造函数