1

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.)

1
  • one dirty solution is that you can have a static member of bar type in foo class, and provide it in second constructor :) Commented Feb 7, 2017 at 19:16

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Good catch. I fixed the ordering.
0

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.

2 Comments

This solution feels very error prone. Example, how do you know rather or not to delete the address of bar in the destructor?
@François Andrieux: yes, that's of course an issue, which must (and can) be handled. If, however, the requirement is that every Foo shall have it's own default bar, then one has to deal with replacing / freeing the default bar once not used any more. Right?
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)) {}

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

2 Comments

That works if I only ever want one Bar object. I didn't mention it above; but, that's not the case here.
@Zack, in that case, the answer by François Andrieux should work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.