AndyG
AndyG

Reputation: 41090

How to avoid big memory allocation with std::make_shared

Let's say that I have some arbitrary class, A:

class A {
 //... stuff
};

I want to call into an external API that takes in a shared pointer to some type, like so (I cannot change this interface):

//...much later
void foo(std::shared_ptr<A> _a){
    //operate on _a as a shared_ptr
}

However, in the (legacy) code I'm working with, the class A instance I'm working with is allocated on the stack (which I cannot get around):

A a;
//...some stuff on a
//Now time to call foo

On top of this, an instance of class A is quite large, on the order of 1 GB per instance.

I know I could call

foo(std::make_shared<A> a);

but that would allocate memory for a copy of A, which I would really like to avoid.

Question

Is there a way to hack together some call to std::make_shared (possibly with move semantics) so that I am not forced to allocate memory for another instance of class A?

I've tried something like this:

foo(std::make_shared<A>(std::move(a)));

But from what I can tell, a new instance of A is still created.

Example code

#include <iostream>
#include <memory>
using namespace std;


class A{
    public:
    A(int _var=42) : var(_var){cout << "Default" << endl;}
    A(const A& _rhs) : var(_rhs.var){cout << "Copy" << endl;}
    A(A&& _rhs) : var(std::move(_rhs.var)){cout << "Move" << endl;}
    int var;
};

void foo(std::shared_ptr<A> _a){
    _a->var = 43;
    cout << _a->var << endl;
}

int main() {
    A a;
    cout << a.var << endl;
    foo(std::make_shared<A>(std::move(a)));
    cout << a.var << endl;
    a.var = 44;
    foo(std::make_shared<A>(std::move(a)));
    cout << a.var << endl;
    return 0;
}

Output:

Default
42
Move
43
42
Move
43
44

Upvotes: 13

Views: 5873

Answers (3)

David Schwartz
David Schwartz

Reputation: 182763

Assuming class A supports move semantics, do this:

std::shared_ptr<A> newA = make_shared<A> (std::move (_a));

Do not use _a anymore, use only newA. You can now pass newA to the function.

If class A does not support move semantics, there is no safe/sane way to do this. Any hack will only happen to work, and may break in the future. If you control enough of the class code, you may be able to add support for move semantics.

But from what I can tell, a new instance of A is still created.

Why do you care? What you're trying to avoid is copying all the data in the instance, and this does that.

The point of move semantics is to move the data from one instance to another without having to do an allocate/copy/free. Of course, this makes the original instance "empty", so don't use that anymore.

Upvotes: 5

Slava
Slava

Reputation: 44248

If you know that shared pointer you pass to foo() will not get stored, copied etc, ie will not outlive your object you can make std::shared_ptr pointed to object on the stack with empty deleter:

void emptyDeleter( A * ) {}

A a;
foo( std::shared_ptr<A>( &a, emptyDeleter ) );

Again you need to make sure that shared pointer or it's copy will not outlive the object and well document this hack.

Upvotes: 7

Kerrek SB
Kerrek SB

Reputation: 477010

This is possible with the shared_ptr constructor that allows for an "empty instance with non-null stored pointer":

A x;
std::shared_ptr<A> i_dont_own(std::shared_ptr<A>(), &x);

(It's "overload (8)" on the cppreference documentation.)

Upvotes: 19

Related Questions