C++中使用auto_ptr作为输出变量的引用是惯用的吗?

3

假设我想编写一个工厂方法,该方法应该在堆上分配不同类型的对象,并将它们返回给调用者。我考虑设计API如下:

bool MakeEm(auto_ptr<Foo>& outFoo, auto_ptr<Bar>& outBar) {
  ...
  if (...) {
    return false;
  }
  outFoo.reset(new Foo(...));
  outBar.reset(new Bar(...));
  return true;
}

这使得调用者可以这样做:
auto_ptr<Foo> foo;
auto_ptr<Bar> bar;
MakeEm(foo, bar);

我的问题是:“这种用法是否通顺自然?如果不是,有什么正确的方法?”我能想到的其他方法包括返回一个auto_ptrstruct,或者编写工厂API以接受原始指针引用。它们都需要编写更多的代码,后者在异常安全方面还存在其他问题。

你实际上需要那个 bool 返回值吗? - GManNickG
非常不符合惯用语。 - Mark Ransom
我可以不用它,检查所有的输出变量或返回值... - Dilum Ranatunga
@Mark -- 如果你看到这个,你会将它重构成什么样子? - Dilum Ranatunga
@Dilum,如果我能立刻想到答案,我就不会留下讽刺的评论了。不过我还在思考中。 - Mark Ransom
显示剩余3条评论
5个回答

4
询问某些东西是否习惯用语可能会得到一些非常主观的答案。然而,总的来说,我认为auto_ptr是传达所有权的好方法,所以从类工厂返回 - 这可能是一个好事情。我希望重构这个代码,使其返回一个对象而不是两个。如果您需要两个紧密耦合且不能独立存在的对象,我会说您有一个强有力的is-a或has-a重构案例。这是C ++。真正地问问自己,是否应该返回表示成功的值,强制消费者必须每次都进行检查。抛出异常或从工厂中的类的构造函数传递异常。您是否曾经想过接受false并尝试在未初始化的auto_ptr上操作?

这样的双重工厂经常存在,因为虽然对象非常不同,但创建它们的配置显著相似,如果分离这个方面将会很棘手。另外,我见过两种输出类型是独立接口的情况,但在这个工厂中,一个单一的对象实际上可以满足两种角色。 - edA-qa mort-ora-y

1
一般来说,如果涉及到 auto_ptr,那么它就不是惯用的方式。通常情况下,结构也不太习惯 - 通常,您会为每个功能创建一个函数,通过值返回并在失败时抛出异常,如果需要共享变量,则将其作为对象创建。

是的,如果我能优雅地将方法分成两个部分,我会这样做。毕竟我不 masochistic;-) - Dilum Ranatunga
作为一般规则,如果涉及到auto_ptr,那么它就不是惯用的。使用unique_ptr是auto_ptr的惯用替代方案吗? - Dilum Ranatunga
auto_ptr 是我能想到的最干净、最直接的方式之一,用于指示调用者正在接管所有权。虽然 auto_ptr 有点问题,但在 C++0x 中出现 unique_ptr 之前,它是我们最好的选择。 - edA-qa mort-ora-y
Boost中有一个scoped_ptr,它并不丑陋破碎。 - Puppy

1
假设返回值为false表示“不查看输出参数”。
那么我会摒弃布尔返回值,返回一个包含所需auto_pointers的结构体或pair,并在错误情况下throw

总之,使用结构体;额外的代码是值得的,因为它更符合惯用法? - Dilum Ranatunga
是的,但正如@Josh在他的答案中指出的那样,将其拆分为两个函数会更好。我会说使用out auto_ptr 绝对不是惯用法。 - Mark B
如果你真的需要有两个输出,我认为在当前标准下使用结构体解决方案非常笨拙;我更喜欢使用两个引用作为输出变量。显然,使用两个函数来创建每个对象的解决方案会更加简洁,但在某些情况下这并不可行或不合理。 - edA-qa mort-ora-y

1

通常当您有auto_ptr参数时,它们不是引用。

这是因为当您将某些东西传递给接受auto_ptr的函数时,您希望该函数拥有所有权。如果您通过引用传递,则实际上不会获取对象(可能会获取对象)。

这是一个微妙的问题,但最终您需要查看您的接口试图向用户传达什么信息。

另外,您似乎将其用作输出参数。
就我个人而言,我从未见过这种用法(但我可以看到它),只需记录您要做的事情,更重要的是,为什么


这绝对是一个输出参数。我确保它们被命名为相应的名称。除了注释之外,还有什么可以使使用更清晰? - Dilum Ranatunga
@Dilum Ranatunga:我认为将返回值作为函数结果可能比使用输出参数更好。但是如果你必须使用输出参数,那么智能指针是一个不错的选择。 - Martin York
通过引用传递参数是一种非常标准的C++方式,可以使函数返回多个值。没有人真正喜欢这样做,但目前的替代方案同样混乱。具有多个命名返回值肯定会很好。 - edA-qa mort-ora-y
@edA-qa mort-ora-y:实际上,我不同意你的观点。这在C语言中是常见的情况,但在C++(或者说是好的C++)中并非如此。如果你想要返回多个值,你可以使用boost::tie()和boost::tuple()。但这并不意味着它不会发生。 - Martin York

1

你不必自己创建结构体来返回两个值 - 你可以使用std::pair。在这种情况下,返回这两个值并没有太多的语法开销。这种解决方案的问题是“.first”和“.second”不是非常描述性的名称,但如果涉及的类型和函数的名称足够清晰地表明意图,那么这并不一定是一个问题。

如果你正在使用C++0x,你可以使用unique_ptr代替auto_ptr,调用者可以使用auto而不必输入更长的std::pair<std::unique_ptr<A>, std::unique_ptr<B>>。如果你没有使用C++0x,你可以考虑使用typedef代替。

如果你返回这两个值,那么你就没有空间来存储bool值。你可以使用C++0x元组来返回所有三个值。你也可以通过抛出异常或返回空指针来表示错误。假设错误很少/异常,我更喜欢使用异常。

正如其他答案所指出的那样,通常最好拥有两个分别返回单个对象的函数。如果你不能这样做,因为两个对象的初始化是不可分割的,那么你可以创建一个封装初始化过程的类。你可以将制作这两个对象所需的必要信息传递给构造函数(需要异常来表示错误),然后在该类上拥有两个方法,每个方法生成一个对象。


是的,在两个值的情况下,std::pair 可以使用。但当有更多值时,在我非 C++0x 的环境中,我必须使用 boost tuples 或编写结构体。 - Dilum Ranatunga

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