Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 566
AndyG Avatar asked Jul 14 '14 16:07

AndyG


2 Answers

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

like image 165
Kerrek SB Avatar answered Sep 20 '22 22:09

Kerrek SB


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.

like image 40
Slava Avatar answered Sep 24 '22 22:09

Slava