如何使用std :: make_shared避免大内存分配

时间:2014-07-14 16:17:18

标签: c++ c++11 memory-management

假设我有一些任意的类,A:

class A {
 //... stuff
};

我想调用一个外部API,它接受一个类型的共享指针,就像这样(我不能改变这个接口):

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

但是,在我正在使用的(遗留)代码中,我正在使用的类A实例被分配到堆栈中(我无法解决):

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

除此之外,A类的实例非常大,每个实例大约1 GB。

我知道我可以打电话

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

但是这会为A的副本分配内存,我真的很想避免。

问题

有没有办法将对std::make_shared的一些调用(可能带有move语义)一起破解,这样我就不会被迫为另一个A类实例分配内存?

我尝试过这样的事情:

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

但据我所知,仍然会创建A的新实例。

示例代码

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

输出:

  

默认值









移动
车辆43
42

3 个答案:

答案 0 :(得分:18)

使用shared_ptr构造函数可以实现“带有非空存储指针的空实例”:

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

cppreference documentation上的“重载(8)”。)

答案 1 :(得分:7)

如果你知道你传递给foo()的共享指针不会被存储,复制等,即不会比你的对象寿命更长,你可以使std::shared_ptr指向堆栈上带有空删除器的对象:< / p>

void emptyDeleter( A * ) {}

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

同样,你需要确保共享指针或它的副本不会比对象更长,并且很好地记录了这个黑客。

答案 2 :(得分:5)

假设类A支持移动语义,请执行以下操作:

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

不要再使用_a,仅使用newA。您现在可以将newA传递给该函数。

如果类A不支持移动语义,则没有安全/理智的方法来执行此操作。任何黑客只会发生工作,并可能在未来破裂。如果您控制了足够的类代码,则可以添加对移动语义的支持。

  

但据我所知,仍然会创建一个新的A实例。

为什么要关心?你要避免的是复制所有实例中的数据,这样就可以了。

移动语义的意思是将数据从一个实例移动到另一个实例,而不必进行分配/复制/免费。当然,这会使原始实例“空”,所以不要再使用它了。