获取对返回对象的引用

C++ obtaining a reference to a returned object

本文关键字:引用 对象 返回 获取      更新时间:2023-10-16

我需要通过引用抓取一个对象,我过去是这样做的:

MyObject& obj = FactoryThatGivesAnObject();
obj.MethodThatModifieObj();

不,我需要基于一个条件:

MyObject obj;
// Need obj to be a reference to the returned values below
if( foo )
    obj = FactoryThatGivesAnObject();
else
    obj = OtherFactoryThatGivesAnObject();
obj.MethodThatModifiesObj();

我如何在第二个例子中让obj成为引用?

与指针不同,引用只能设置一次。这在很多时候都是一个有用的功能,但这是一个令人沮丧的方面。您只需要设置一次引用,但可能是不同的内容。

你有两个选择。

1)使用三元操作符

这通常是最简单的,如果您只处理两个工厂,并使用一个简单的布尔值来决定使用哪个:

MyObject& obj = ( foo
                  ? FactoryThatGivesAnObject();
                  : OtherFactoryThatGivesAnObject() );

但是,如果foo更复杂,或者如果您有多个出厂选项,则下一个选项可能更简洁。

2)使用自己的工厂方法
MyObject& get_an_object(const int state) // or whatever parameters you need
{
    switch(state)
    {
       case USE_LEGACY_FACTORY:    return FactoryThatGivesAnObject();
       case USE_FOO_FACTORY:       return OtherFactoryThatGivesAnObject();
       case DO_SOMETHING_ELSE:     return YetAnotherObjectFactory();
    }
    throw std::runtime_error("Bad Factory Selector");
}
// usage is simpler now
MyObject& obj = get_an_object(foo);

注意,你可能需要传递几个参数给你的工厂方法:

  • 选择标准。您的示例只是foo—一个简单的布尔值。随着业务的增长,您可能需要额外的标准来帮助确定使用哪个工厂。
  • 工厂对象。你可能有工厂对象而不是工厂方法,在这种情况下,你需要将对那些对象的引用传递到你的方法中。

一种解决方案是使用三元操作符:

obj = foo ? FactoryThatGivesAnObject() : OtherFactoryThatGivesAnObject();

也可以使用指针:

MyObject* pobj;
if( foo )
    pobj = &FactoryThatGivesAnObject();
else
    pobj = &OtherFactoryThatGivesAnObject();

你的第一行是阴暗的:

MyObject& obj = FactoryThatGivesAnObject();

这是怎么回事?工厂方法不能返回对临时对象的引用,因此它能返回的唯一合理的引用是对动态创建的对象的引用——但是现在谁对这个对象负责呢?

(除非您只是返回对现有对象的引用,否则就是。但我假设你的工厂真的在创造新的对象。

这段代码是内存泄漏的车祸;我看不出有什么办法能写出这样合理的东西。一个更好的方法是在一个负责任的容器中返回新创建的对象,例如shared_ptrunique_ptr:

#include <memory>
std::unique_ptr<MyObject> FactoryFunction()
{
  return std::unique_ptr<MyObject>(new MyObject(3,5,7));
}

这样,如果没有人拿起工厂产品,或者发生异常,动态分配的对象将被正确处理。

这也使得根据条件赋值不同的指针变得很简单:

std::unique_ptr<MyObject> x;
if (...)      { x = Factory1(); }
else if (...) { x = Factory2(a,b); }
else          { x = Factory3(argc, argv); }

我如何在第二个例子中让obj成为引用?

你不能。引用是别名;您只能通过将它们指向某个对象来创建它们,并且一旦指向了它们,它们就不能被重新分配。

你可能最好在这里使用std::auto_ptrstd::unique_ptr之类的东西。注意,您的工厂需要返回auto/unique_ptr。如果您的工厂返回一个引用,我怀疑您可能不小心返回了对未命名临时(未定义行为)的引用,但如果没有看到工厂的代码,则很难判断。

这里有一个解决方案,它在技术上不是工厂,但解决了同样的问题——在更改参数时提供新对象:

struct A
{
  int a;
  float x;
  int c;
};
class ObjectCollection
{
public:
    ObjectCollection() { m_a.c = 10; }
    A &get_obj_a(int a, float x)
    {
    m_a.a = a;
    m_a.x = x;
    return m_a;
    }
private:
    A m_a;
};

这个版本的优点是它没有传递对象的所有权,但是你仍然可以用它创建不同类型的对象。虽然两次调用get_obj_a()会导致问题,但只有在需要对象之前立即调用get_obj_a()才有效。现在可以将if语句放入工厂函数中。还有另一种方法:

class DerivedFactory
{
public:
   DerivedFactory(ObjectCollection1 &c, ObjectCollection2 &c2) : c(c),c2(c2) { }
   Base &get_obj_a_or_b(bool b) {
         if (b) return c.get_obj_a(10,11.0);
         else return c2.get_obj_b(20.0,13.0);
   } 
private:
  ObjectCollection1 &c;
  ObjectCollection2 &c2;
};