Zack
Zack

Reputation: 6606

Provide default value for constructor initializing reference instance variable

I have a class Foo that has a reference to Bar as an instance variable. Is there a way to have one of the Foo constructors create a Bar instance (so users of the Foo class need not explicitly create a Bar object themselves)?

I'd like to do something like this:

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)) {}
}

(In "real life", Foo and Bar are more complicated. Bar is a friend of Foo that has a private constructor used only by Foo. Each Bar can be shared by several Foos. This isn't a major problem, I'm just curious if it can be done without pointers.)

Upvotes: 1

Views: 167

Answers (3)

François Andrieux
François Andrieux

Reputation: 29072

The simplest solution would be to allocate a Bar if and when you need one. Adding a member std::unique_ptr<Bar> is the easiest way of achieving this. I can't conceive of a system that addresses your problem that will not add some memory overhead to your class. You either have to account for storage for a Bar even when it isn't needed or you have to account for some state to track rather or not your Bar is internally managed. Using a unique_ptr adds the size of a pointer to your class.

#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) {}
};

A more elaborate solution could be to provide a wrapper for Bar that provides a default constructor that takes care of initializing a new instance.

Note that in your example, you do not initialize the member in the same order as they are declared. Members will be initialized in the order they are declared regardless of how you order them in your initialization list.

Upvotes: 3

R Sahu
R Sahu

Reputation: 206737

// 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)) {}

You can use a default Bar object, which can be a static member variable of Foo or any other class that can provide such an object.

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;
}

Upvotes: 0

Stephan Lechner
Stephan Lechner

Reputation: 35164

If every Foo shall have it's own individual Bar-object (not a single shared one), then you could try the following:

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;
    }
};

Hope it is what you are looking for.

Upvotes: 0

Related Questions