Как клиенту отдавать класс А, содержащий weak_ptr, а в библиотеке использовать класс А с shared_ptr без дублирования кода?
Начну с наивных реализаций, чтобы подвести к сути вопроса. Допустим есть первая реализация двух классов. Думаю, тут задача понятна
class A
{
string name_{};
public:
A(string_view name): name_(name) {};
};
class B
{
vector<A*> v_{}; // ВАРИАНТ 1
public:
B() {};
~B() { for (auto& a : v_) delete a; };
A* createA(string_view name)
{
v_.push_back(new A(name));
return v_.back();
}
};
Но отдавать клиенту сырые указатели плохо, поэтому следующий шаг - используем std::shared_ptr для хранения объектов класса A
class A
{
string name_{};
public:
A(string_view name) : name_(name) {};
};
class B
{
vector<shared_ptr<A>> v_{}; // ВАРИАНТ 2
public:
B() {};
shared_ptr<A> createA(string_view name)
{
v_.push_back(make_shared<A>(name));
return v_.back();
}
};
Следующая наивная итерация - скрываем реализацию используя идиому PImpl
class A
{
struct Impl;
shared_ptr<Impl> pImpl; // СЛЕДУЮЩАЯ ИТЕРАЦИЯ
public:
A(string_view name) /* : pImpl(make_shared<Impl>()) */ { };
/*public API*/
};
class B
{
vector<A> v_{};
public:
B() {};
A createA(string_view name)
{
v_.emplace_back(name);
return v_.back();
}
};
Теперь клиенту отдается объекты класса А, которые содержат shared_ptr на реализацию, которая скрыта от клиента.
Я ищу способ сделать еще одну итерацию данного процесса разработки. Дело в том, что в приложении будет намного больше двух классов, которые будут выстраиваться в древовидные структуры. Т.к. класс shared_ptr позволяет подсчитывать количество ссылок, хотелось бы использовать эту возможность. Но количество объектов, созданных клиентом меня не интересует, хотелось бы вести подсчет ссылок только в библиотеке. Т.е. хотелось бы клиенту отдавать класс А, содержащий weak_ptr, а в библиотеке использовать класс А с shared_ptr... Как я не пыталась реализовать - получается много дублирующего кода для поддержки двух видов класса А. Есть ли решение этой задачи на данном пути? Или же надо использовать какой либо другой паттерн проектирования?
Ответы (1 шт):
Как вариант - возможно будет проще во 2 варианте всегда возвращать weak_ptr для всех - и для библиотеки и для клиента? Доступа к объекту через weak_ptr нет, для доступа необходимо его преобразовывать в shared_ptr.
class B
{
vector<shared_ptr<A>> v_{}; // ВАРИАНТ 2
public:
weak_ptr<A> createA(string_view name)
{
v_.push_back(make_shared<A>(name));
return weak_ptr<A> tmp( v_.back() );
}
};
Или по той же причине в class A хранить weak_ptr. Когда библиотеке понадобиться - она преобразует в shared_ptr и счетчик увеличится. Но на мой взгляд делать обертку над оберткой - просто усложнение кода, т.к. shared_ptr (в комплекте с weak_ptr) - уже обертка над объектом, определяющая его время жизни.
Или сделать 2 фабричные функции - одну для библиотеки (не видна клиенту), вторую - для клиента
class B
{
vector<shared_ptr<A>> v_{}; // ВАРИАНТ 2
shared_ptr<A> createA(string_view name) // фабричная функция для библиотеки, недоступная клиенту
{
v_.push_back(make_shared<A>(name));
return v_.back();
}
public:
weak_ptr<A> GetA(string_view name) // фабричная функция для клиента
{
return weak_ptr<A> tmp( createA(name) );
}
};
И непонятно, как клиент использует данные объекта и вообще использует ли? Если использует, то ему всё равно нужно преобразовывать weak_ptr в shared_ptr - а это увеличит счетчик ссылок.
Но количество объектов, созданных клиентом меня не интересует, хотелось бы вести подсчет ссылок только в библиотеке.
Т.е. клиент может создавать объекты минуя class B::createA()? Это точно? Тогда у библиотеки и клиента получается два разных пула объектов и в каком-то месте нужно отдать клиенту копию объекта из библиотеки (shared_ptr на копию объекта) а не указатель на сам объект. Это точно то поведение, которого вы хотите добиться?