Nick
Nick

Reputation: 10539

C++11 dependency injection in constructor with reference

Consider following code:

#include <stdio.h>
#include <memory>

class Sub{};

class SubImpl : public Sub {};

class A{
public:
    A(Sub & sub) : sub(sub){}

    void doSomething(){
        // ...
    };

private:
    Sub & sub;
};

std::unique_ptr<A> factory(){
    SubImpl sub;

    return std::unique_ptr<A>(new A(sub));
}

int main(){
    auto a = factory();

    a->doSomething();
}

This code have problem - Sub object lifetime is not same as A object, and A::sub reference is dangled.

In order to fix this, I can do:

Is there any other way I can fix this?

Upvotes: 2

Views: 1293

Answers (2)

Chris Drew
Chris Drew

Reputation: 15334

It seems you have two choices, either the lifetime of SubImpl is controlled by A in which case I see no alternative to using a pointer, preferably a unique_ptr, like bolov has shown. And I don't think there is anything particularly wrong with that.

Or the lifetime of SubImpl is controlled by something outside of A in which case you can use references.

For example, you could change the factory so that it is an object that owns the SubImpl. Here is a simplistic example:

// definitions of Sub, SubImpl and A as before... 

class SubOwner {
    SubImpl sub;
public:
    A createA() const {
        return A(sub);
    }
};

int main(){
    SubOwner so;
    auto a = so.createA();
    a.doSomething();
} 

That's fine if you only want one SubImpl but it is possible that you want to use your factory to create many As and each A should have a reference to a different SubImpl. In which case you could have an object that owns a collection of SubImpl:

class SubOwner {

    // Using deque instead of vector to avoid reference invalidation. 
    // Could use vector if you knew how many SubImpl you need up front. 
    std::deque<SubImpl> subs;
public:
    A createA() const {
        subs.emplace_back();
        return A(subs.back());
    }
};

int main(){
    SubOwner so;
    auto a1 = so.createA();
    auto a2 = so.createA();

    a1.doSomething();
    a2.doSomething();
}

Providing SubOwner and A are themselves owned by the same object or local to the same scope then you can guarantee that the lifetime of SubImpl will be the same as A.

Upvotes: 1

bolov
bolov

Reputation: 75815

Use unique_ptr if the object sub is used solely to create only one A object. Use shared_ptr otherwise.

class Sub{};

class SubImpl : public Sub {};

class A{
public:
    A(std::unique_ptr &&sub) : sub(std::move(sub)){}

    void doSomething(){
        // ...
    };

private:
    std::unique_ptr sub;
};

std::unique_ptr<A> factory(){
    auto sub = std::make_unique<SubImpl>();;

    return std::make_unique<A>(std::move(sub));
}

int main(){
    auto a = factory();

    a->doSomething();
}

Upvotes: 2

Related Questions