Получить общий указатель из разыменованного значения общего указателя
Представьте себе, что у вас есть вектор общих указателей
typedef vector< shared_ptr< classA > > PointerVector;
И класс B, который имеет в качестве члена вектор общих указателей, а также метод, который выталкивает обратно в этот вектор уже разыменованный общий указатель.
ClassB
{
public:
void add(classA &ref);
private:
PointerVector vector;
}
Предположим, что основной фид функции add с разыменованным экземпляром shared_ptr
в основном:
shared_ptr< classA > sharedptr ( new classA );
ClassA &ref = *sharedptr;
ClassB B;
B.add(ref);
Есть ли способ реализовать функцию добавления, чтобы принять разыменованный общий указатель и преобразовать его в исходный общий указатель? а потом толкнуть обратно в матрицу?
Вот что я пытаюсь сделать:
void ClassB::add(classA &ref) {
shared_ptr< classA > ptr = &ref;
vector.push_back( ptr );
}
ПРИМЕЧАНИЕ 1. Когда класс A добавляется к вектору, я хочу, чтобы количество общих указателей увеличилось на единицу. Мне не нужно создавать еще один общий указатель, я хочу "найти" общий указатель, который ранее имел объект.
ПРИМЕЧАНИЕ 2: я не могу использовать make_shared, потому что я в настоящее время нахожусь в tr1
2 ответа
Как правило, вы не можете просто "найти" указатели на объект, так как ничто не отслеживает, на какой конкретный объект указывает или нет. Вы должны либо разработать такой механизм самостоятельно, либо использовать тот, который предоставляется стандартной библиотекой: enable_shared_from_this
,
Шаг 1: выведите свой класс из std::enable_shared_from_this
и предоставить функцию-член для получения указателя:
#include <memory>
struct ClassA: std::enable_shared_from_this<ClassA>
{
std::shared_ptr<ClassA> get_pointer()
{
return shared_from_this();
}
//...
};
Шаг 2: Убедитесь, что все объекты, на которые вы хотите получить указатели, управляются shared_ptr
:
std::shared_ptr<ClassA> original_ptr(new ClassA);
Шаг 3: вызывать функцию, определенную на шаге 1, когда вы хотите получить общий указатель на объект:
void ClassB::add(classA& ref) {
shared_ptr<classA> ptr = ref.get_pointer();
vector.push_back( ptr );
}
Есть способ сделать это, но необходимость в этом может означать, что есть недостаток дизайна.
Почему это не работает
shared_ptr
нужен счетчик ссылок для работы. Этот счетчик хранится в блоке управления, который создается при первом shared_ptr
создано. Когда вы копируете shared_ptr
копия не просто копирует указатель на управляемый объект, но также копирует указатель на блок управления. Это позволяет ему получить доступ к счетчику ссылок и уничтожить блок, когда он уничтожает объект.
Функция, которая принимает ClassA &
не имеет доступа к блоку управления. Попытка создать новый shared_ptr
создаст новый блок управления со счетчиком ссылок 1. Когда любой счетчик ссылок достигнет 0, объект будет уничтожен. Следовательно, он будет уничтожен дважды, что является неопределенным поведением.
Как это может работать
Как уже упоминалось в комментариях, std::enable_shared_from_this
решает вашу проблему. Вы должны сделать так, чтобы ваш класс проистекал из этого.
ClassA : std::enable_shared_from_this<ClassA> {...}
Тогда вы можете позвонить ClassA.shared_from_this()
чтобы получить shared_ptr
это копия существующего shared_ptr
с того же объекта.
Почему я не рекомендую использовать его
Вы должны быть очень осторожны с enable_shared_from_this
, cppreference.com заявляет
Разрешается вызывать shared_from_this только для ранее общего объекта, то есть для объекта, управляемого std::shared_ptr. В противном случае поведение не определено (до C++17), генерируется std::bad_weak_ptr (с помощью конструктора shared_ptr, созданного по умолчанию weak_this) (начиная с C++17).
Поэтому вы должны убедиться, что объект уже открыт, прежде чем пытаться создать новый shared_ptr
от этого с shared_from_this
метод.
Кроме того, ваша функция подпись принимает ClassA&
, Он может быть вызван с аргументами в стеке; создавая shared_ptr
Из стека объектов это опасно. Если ваша функция должна делиться собственностью, она должна только принять shared_ptr<ClassA>
чтобы сообщить, каково его намерение. Это делает его самодокументированным и труднее обращаться с ним, не жертвуя ничем.