2017-02-07 18 views
1

У меня есть класс Foo, который имеет ссылку на Bar в качестве переменной экземпляра. Есть ли способ, чтобы один из конструкторов Foo создал экземпляр Bar (поэтому пользователям класса Foo не нужно явно создавать объект Bar)?Укажите значение по умолчанию для инициализации инициализации ссылочной переменной экземпляра

Я хотел бы сделать что-то вроде этого:

class Foo { 
    private: 
    int size; 
    Bar& bar; // shared by other objects 
    //several other instance variables 

    public: 
    Foo(int s, Bar& b) : size(s), bar(b) {} 

    // This, of course, doesn't work. I can't think of an 
    // alternative that doesn't require users of Foo to 
    // create Bar 
    Foo(int s) : size(s), bar(Bar(s)) {} 
} 

(В «реальной жизни», Foo и Bar являются более сложными Bar является другим Foo, который имеет приватный конструктор используется только Foo. . Каждый Bar может совместно использоваться несколькими Foo с. Это не является серьезной проблемой, я просто интересно, если это может быть сделано без указателей.)

+0

Одно грязное решение состоит в том, что вы можете иметь статический член типа bar в классе foo и предоставить его во втором конструкторе :) – zapredelom

ответ

3

Простейшим решением было бы присвоить Bar, если и когда вам это нужно. Добавление участника std::unique_ptr<Bar> - это самый простой способ достичь этого. Я не могу представить себе систему, которая решает вашу проблему, которая не добавит некоторые издержки памяти для вашего класса. Вы либо должны учитывать хранилище для Bar, даже если это не нужно, или вам нужно учитывать какое-либо состояние для отслеживания, а не ваше Bar внутренне управляется. Использование unique_ptr добавляет размер указателя на ваш класс.

#include <memory> 

class Foo { 
private: 
    std::unique_ptr<Bar> storage; 
    Bar& bar; 
    int size; 

public: 
    Foo(int s, Bar& b) : bar(b), size(s) {} 
    Foo(int s) : storage(std::make_unique<Bar>(s)), bar(*storage), size(s) {} 
}; 

Более сложное решение могло бы обеспечить оболочку для Bar, который предоставляет конструктор по умолчанию, который заботится о инициализации нового экземпляра.

Обратите внимание, что в вашем примере вы не инициализируете элемент в том же порядке, в котором они объявлены. Члены будут инициализированы в том порядке, в котором они объявлены, независимо от того, как вы их заказываете в своем списке инициализации.

+0

Хороший улов. Я исправил порядок. – Zack

0
// This, of course, doesn't work. I can't think of an 
// alternative that doesn't require users of Foo to 
// create Bar 
Foo(int s) : size(s), bar(Bar(14)) {} 

Вы можете использовать объект Bar по умолчанию, который может быть переменной-членом static от Foo или любым другим классом, который может предоставить такой объект.

E.g.

class Foo { 
    private: 
     Bar& bar; // shared by other objects 
     int size; 
     //several other instance variables 

     static Bar& getDefaultBar(); 

    public: 
     Foo(int s, Bar& b) : size(s), bar(b) {} 

     Foo(int s) : size(s), bar(getDefaultBar()) {} 

} 

Bar& Foo::getDefaultBar() 
{ 
    static Bar b(24); 
    return b; 
} 
+0

Это работает, если мне нужен только один объект Bar. Я не упоминал об этом выше; но, это не так. – Zack

+0

@ Зак, в этом случае, [ответ Франсуа Андри] (http://stackoverflow.com/a/42098394/434551) должен работать. –

0

Если каждый Foo должен иметь свой собственный индивидуальные Bar -объект (не один общие один), то вы можете попробовать следующее:

class Foo { 
private: 
    Bar& bar; // shared by other objects 
    int size; 
    Bar* defaultBar; 
    //several other instance variables 

public: 
    Foo(int s, Bar& b) : size(s), bar(b), defaultBar(nullptr) {} 
    Foo(int s) : Foo(s, *(new Bar(14))) { defaultBar=&bar; }; 
    virtual ~Foo() { 
     if (defaultBar) 
      delete defaultBar; 
    } 
    void setBar(Bar &b) { 
     if (defaultBar) 
      delete defaultBar; 
     defaultBar = nullptr; 
     bar=b; 
    } 
}; 

Надеется, что это то, что вы ищете.

+1

Это решение очень подвержено ошибкам. Например, как вы знаете или нет, чтобы удалить адрес «bar» в деструкторе? –

+0

@ François Andrieux: Да, это, конечно, проблема, с которой (и может) быть обработано.Если, однако, требование состоит в том, что каждый Foo должен иметь свой собственный бар по умолчанию, тогда приходится иметь дело с заменой/освобождением бара по умолчанию, который больше не используется. Правильно? –