1

I want to create a variable of type Foo. The class Foo consists of a variable called Bar bar_ which is filled directly in the constructor initialization. The thing is, class Bar has a reference to the interface IFoo& foo_. Foo2 is an object which derive from IFoo. But I do not want to create an variable of type Foo2, because I do not need it again. Only for the interface. (the class Bar is given code, I can not change). So I come with this solution: Foo(int x, int y) : bar_(Foo2(x, y))

I do not get any error, but I ask myself, where the object Foo2 is now stored? Isn't it directly destroyed after the construction? This is why I added some more code, just creating another random object and checking if the values of foo changes. My supposition seems correct. The values of foo does have changed.

Questions:

  1. Is there any way, this code could work?
  2. Or do I have to add a variable of type Foo2 in the class Foo (or somewhere else)?

Code: https://onlinegdb.com/3sQ4X03ji

#include <stdio.h>

class IFoo
{
public:
    virtual int GetSum() = 0;
};

class Foo2 : public IFoo
{
public:
    Foo2(int x, int y) : x_(x), y_(y)
    {
        printf("Creating Foo2 with: x=%u, y=%u\n", x, y);
    }
    
    int GetSum() override
    {
        return x_ + y_;
    }
    
    int x_;
    int y_;
};

class Bar
{
public:
    Bar(Foo2 foo) : foo_(foo)
    {
    }
    
    IFoo& foo_;
};

class Foo
{
public:
    Foo(int x, int y) : bar_(Foo2(x, y))
    {
    }
    
    Bar bar_;
};

int main()
{
    Foo foo(1, 2);
    Bar bar(Foo2(2, 3));
    printf("The sum of foo is: %u (expected 3)\n", foo.bar_.foo_.GetSum());
    printf("The sum of bar is: %u (expected 5)\n", bar.foo_.GetSum());

    return 0;
}

Output:

Creating Foo2 with: x=1, y=2
Creating Foo2 with: x=2, y=3
The sum of foo is: 275680318 (expected 3)
The sum of bar is: 5 (expected 5)
5
  • The flaw in your reasoning is "But I do not want to create an variable of type Foo2, because I do not need it again." you do need an object if you want to maintain a valid reference to it. Commented Aug 15 at 8:05
  • Your code is flawed to start with. If you have a reference, there will always be an object. From your description, however, it feels rather optional -- so it should be a pointer or maybe an optional. Commented Aug 15 at 8:47
  • Should Bar(Foo2 foo) be Bar(Foo2& foo)? If not then refuse to complete the assignment as it contains undefined behaviour Commented Aug 15 at 9:28
  • If you don't need the object of type Foo2, why are you storing a reference to one (even if via reference to base i.e. Foo & ) in Bar? By doing so, you have introduced a requirement that the Foo2 exist for the lifetime of the containing Bar (otherwise any usage of the reference within Bars member functions will give undefined behaviour). If there is no usage of the reference by Bars member functions, what is the purpose of Bar having the reference member at all? Commented Aug 15 at 10:47
  • Dangling references become undefined behavior if the reference is dereferenced. Here's a fixed version of your code: godbolt.org/z/94x1q117v Commented Aug 15 at 11:46

2 Answers 2

6

The problem is evident in this piece of code already:

class Bar
{
public:
Bar(Foo2 foo) : foo_(foo)
    {
    }

IFoo& foo_;
};

The constructor takes a Foo2 by value. The reference member becomes invalid as soon as the constructor returns, because then there is no foo anymore.

If you want to keep a reference to an object, that object must be kept alive as long as you use the reference. Store the object somewhere.

Your example is rather unspecific and abstract, hence it is difficult to give concrete advice. Storing a std::unique_ptr<IFoo> in Bar could be a possible solution.

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

1 Comment

I have been playing with creating a DI framework for C++ and that is the bane of the implementation right now: Runtime created services basically MUST be heap allocated since I am enforcing a testability contract requiring access via interface only and there is no way to return a derived class from a function that returns a base class (interface). So yeah, uniqe_ptr is ugly, but probably the easiest solution too.
0

This made me think for a while tbh

The fundamental issue is that Bar stores a reference to its parameter, not to the object you passed in. So even storing Foo2 as a member doesnt solve it if Bars constructor signature cant be changed.

Bar(Foo2 foo) : foo_(foo) 

foo is a local copy that gets destroyed when constructor ends, thats why foo_ is dangling reference pointing to destroyed memory.

If you are gonna store Foo2 somewhere (probably in Foo) my best guess is:

class Bar
{
public:
    Bar(Foo2 foo) : foo_(foo)
    {
    }
    ~Bar() { std::cout << "Destroying Bar\n"; }
    IFoo& foo_;
};

class Foo
{
public:
    Foo(int x, int y) : foo2_(x,y) , bar_(foo2_)
    {
    }
    ~Foo() { std::cout << "Destroying Foo\n"; }
    Foo2 foo2_;
    Bar bar_;
};

int main()
{
    Foo foo(1, 2);
    Bar bar(Foo2(2, 3));
    printf("The sum of foo is: %u (expected 3)\n", foo.foo2_.GetSum());
    printf("The sum of bar is: %u (expected 5)\n", bar.foo_.GetSum());

    return 0;
}

This is still dangling reference. Best answer might be from other user to use smart pointers.

If you cannot change bar code then i dont think there is clean solution, if you can then i would do:

class IFoo
{
public:
    virtual int  GetSum() const = 0; // const
};

class Foo2 ...
    int GetSum() const override //const
    {
        return x_ + y_;
    }
....

public:
    Bar( const Foo2& foo) : foo_(foo)  // const
    {
    }
    ~Bar() {
        std::cout << "Destroying Bar\n";
    }
    const Foo2& foo_;  // const
};

Because foo is now reference to original object and foo_ points to existing object that exists outside of constructor

or


public:
    Bar(const IFoo& foo) : foo_(foo)  // pass by reference
    {
    }
    ~Bar() {
        std::cout << "Destroying Bar\n";
    }
    const IFoo& foo_;  // reference to interface
};

This might be better since it follows OOP principles and is more flexible, another option might be move semantics.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.