C ++ получение ссылки на возвращаемый объект - PullRequest
1 голос
/ 09 августа 2011

Мне нужно захватить объект по ссылке, и я делал это так:

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 во втором примере?

Ответы [ 5 ]

5 голосов
/ 10 августа 2011

Ссылки, в отличие от указателей, могут быть установлены только один раз.Это полезная функция много раз, но это один из разочаровывающих аспектов.Вы хотите установить ссылку только один раз, но, возможно, на разные вещи.

У вас есть два варианта.

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 - простой логический.По мере роста ситуации вам могут понадобиться дополнительные критерии, чтобы определить, какую фабрику использовать.
  • фабричные объекты.У вас могут быть фабричные объекты вместо фабричных, и в этом случае вам нужно передавать ссылки на эти объекты в ваш метод.
3 голосов
/ 09 августа 2011

Одним из решений может быть использование троичного оператора:

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

Вы также можете использовать указатель:

MyObject* pobj;
if( foo )
    pobj = &FactoryThatGivesAnObject();
else
    pobj = &OtherFactoryThatGivesAnObject();
2 голосов
/ 10 августа 2011

Ваша самая первая строка нечеткая:

MyObject& obj = FactoryThatGivesAnObject();

Как это должно работать?Фабричный метод не может вернуть ссылку на временный объект, поэтому единственная разумная ссылка, которую он может вернуть, - это динамически созданный объект - но теперь, кто отвечает за этот объект?

(Если вы просто не возвращаете ссылку насуществующий объект, то есть. Но я предполагаю, что ваша фабрика действительно создает новые объекты.)

Этот код является автомобильной аварией с утечкой памяти;Я не вижу способа написать что-нибудь толковое.Гораздо лучший способ - вернуть вновь созданный объект в ответственный контейнер, например, shared_ptr или unique_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); }
1 голос
/ 09 августа 2011

Как я могу использовать obj в качестве ссылки во втором примере?

Вы не можете.Ссылки являются псевдонимами;вы можете создать их, только указав на что-то, и после того, как вы их указали, они не могут быть переназначены.

Возможно, вам было бы лучше использовать что-то вроде std::auto_ptr или std::unique_ptr здесьОбратите внимание, что ваша фабрика должна будет вернуть auto / unique_ptr.Если ваша фабрика возвращает ссылку, я подозреваю, что вы могли случайно возвращать ссылки на неназванные временные данные (неопределенное поведение), но, не увидев код фабрики, трудно сказать.

0 голосов
/ 10 августа 2011

Вот одно решение, которое технически не является заводским, но решает ту же проблему - предоставление новых объектов при изменении параметров:

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;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...