簡體   English   中英

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

[英]How to avoid big memory allocation with std::make_shared

假設我有一些任意的類,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;
}

輸出:

默認
42
移動
43
42
移動
43
44

這可以通過shared_ptr構造函數實現,該構造函數允許“具有非空存儲指針的空實例”:

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

(這是cppreference文檔中的“overload(8)”。)

如果你知道你傳遞給foo()共享指針不會被存儲,復制等,即不會比你的對象更活躍,你可以使std::shared_ptr指向堆棧上帶有空刪除器的對象:

void emptyDeleter( A * ) {}

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

你需要再次確保共享指針或它的副本不會比對象更長,並且很好地記錄了這個hack。

假設類A支持移動語義,請執行以下操作:

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

不要再使用_a了,只使用newA 您現在可以將newA傳遞給該函數。

如果A類不支持移動語義,則沒有安全/理智的方法來執行此操作。 任何黑客只會發生工作,並可能在未來破裂。 如果您控制了足夠的類代碼,則可以添加對移動語義的支持。

但據我所知,仍然創建了一個新的A實例。

你為什么在乎? 您要避免的是復制實例中的所有數據 ,這樣做。

移動語義的關鍵是數據從一個實例移動到另一個實例,而不必進行分配/復制/釋放。 當然,這會使原始實例“空”,所以不要再使用它了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM