Ответ 1
Это возможно с помощью конструктора shared_ptr
, который допускает "пустой экземпляр с ненулевым сохраненным указателем":
A x;
std::shared_ptr<A> i_dont_own(std::shared_ptr<A>(), &x);
( "Перегрузка (8)" в документации cppreference.)
Скажем, что у меня есть некоторый произвольный класс, 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 ГБ на каждый экземпляр.
Я знаю, что могу позвонить
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);
( "Перегрузка (8)" в документации cppreference.)
Если вы знаете, что общий указатель, который вы передаете на foo()
, не будет сохранен, скопирован и т.д., то есть не будет переживать ваш объект, вы можете сделать std::shared_ptr
направленным на объект в стеке с пустым удалением:
void emptyDeleter( A * ) {}
A a;
foo( std::shared_ptr<A>( &a, emptyDeleter ) );
Снова вам нужно убедиться, что общий указатель или его копия не оживут объект и хорошо документируют этот взлом.
Предполагая, что класс A
поддерживает семантику переноса, выполните следующее:
std::shared_ptr<A> newA = make_shared<A> (std::move (_a));
Не используйте _a
больше, используйте только newA
. Теперь вы можете передать newA
в функцию.
Если класс A
не поддерживает семантику перемещения, нет безопасного/разумного способа сделать это. Любой взлом будет работать только, и может разорваться в будущем. Если вы достаточно контролируете код класса, вы можете добавить поддержку семантики перемещения.
Но из того, что я могу сказать, новый экземпляр A все еще создан.
Почему вас это волнует? То, что вы пытаетесь избежать, - это копирование всех данных в экземпляре, и это делает это.
Семантика точки перемещения - перемещать данные из одного экземпляра в другой без необходимости делать allocate/copy/free. Конечно, это делает исходный экземпляр "пустым", поэтому больше не используйте его.