В последнее время я использовал много разных умных указателей для повышения, а также обычные указатели. Я заметил, что по мере развития вы склонны понимать, что вам приходится переключать типы указателей и механизм управления памятью, потому что вы пропускаете некоторую циклическую зависимость или какую-то небольшую раздражающую вещь. Когда это происходит и вы меняете тип указателя, вам нужно либо пойти и изменить целую кучу сигнатур ваших методов, чтобы взять новый тип указателя, либо на каждом сайте вызова вы должны конвертировать между типами указателей. У вас также есть проблема, если у вас есть та же функция, но вы хотите, чтобы она принимала несколько типов указателей.
Мне интересно, существует ли уже общий способ иметь дело, то есть написать методы, которые не зависят от типа указателя, который вы передаете ему?
Очевидно, что я вижу несколько способов сделать это, один из которых состоит в написании перегруженных методов для каждого типа указателя, но это быстро становится проблемой. Другой - использовать решение в виде шаблона с определенным выводом типа, но это приведет к некоторому значительному увеличению объема скомпилированного кода и, вероятно, приведет к появлению странных неразрешимых ошибок шаблона.
Моя идея - написать новый класс any_ptr<T>
с конструкторами преобразования из всех основных типов указателей, скажем, T*
, shared_ptr<T>
, auto_ptr<T>
, scoped_ptr<T>
, возможно, даже weak_ptr<T>
, и затем иметь его выставить операторы *
и ->
. Таким образом, его можно использовать в любой функции, которая не возвращает указатель вне функции и может вызываться с любой комбинацией общих типов указателей.
Мой вопрос: действительно ли это глупо? Я вижу, что им можно злоупотреблять, но при условии, что он никогда не используется для функций, возвращающих any_ptr, есть ли серьезная проблема, с которой я бы столкнулся? Ваши мысли, пожалуйста.
РЕДАКТИРОВАТЬ 1
Прочитав ваши ответы, я хотел бы сделать несколько заметок, которые были слишком длинными для комментариев.
Во-первых относительно использования необработанных указателей или ссылок (@shoosh). Я согласен, что вы могли бы заставить функции использовать необработанные указатели, но затем предположите случай, когда я использовал shared_ptr, что означает, что мне пришлось бы идти ptr.get () на каждом сайте вызова, теперь предположим, что я понимаю, что сделал циклическую ссылку и Я должен изменить указатель на weak_ptr, затем я должен пойти и изменить все эти сайты вызовов на x.lock (). Get (). Теперь я согласен, что это не катастрофа, но это раздражает, и я чувствую, что есть элегантное решение для этого. То же самое можно сказать и для передачи ссылок T & T и перехода * x, аналогичные изменения сайта вызовов должны быть сделаны.
То, что я пытаюсь сделать здесь, - это сделать код более элегантным для чтения и упростить его рефакторинг даже при больших изменениях в типе указателя.
Во-вторых в отношении семантики smart_ptr: я согласен, что разные интеллектуальные указатели используются по разным причинам и имеют определенные соображения, которые необходимо учитывать при копировании и хранении (вот почему boost::shared_ptr<T>
не конвертируется автоматически в T*
).
Однако я предполагал, что any_ptr (может быть, плохое имя в ретроспективе) будет использоваться только в тех случаях, когда указатель не будет сохранен (в чем-либо, кроме, возможно, временных переменных в стеке). Он должен быть просто неявно конструируемым из различных типов интеллектуальных указателей, перегружать операторы * и -> и быть преобразованным в T * (через пользовательскую функцию преобразования T*()
). Таким образом, семантика any_ptr
точно такая же, как и T*
. И как таковой, он должен использоваться только там, где было бы безопасно использовать необработанный ptr (это то, что @ Alexandre_C говорил в комментарии). Это также означает, что не будет ни одной из «тяжелых машин», о которых говорил @Matthieu_M.
В-третьих в отношении шаблонов. Хотя шаблоны хороши для чего-то, я опасаюсь их по причинам, изложенным выше.
ЗАКЛЮЧИТЕЛЬНО : В общем, я пытаюсь сделать это для функций, в которых в качестве параметров обычно используется raw ptr (T *). Я хотел бы создать систему, в которой эти параметры могут автоматически принимать любыеразличных типов smart_ptr без необходимости выполнять преобразования на сайтах вызовов.Причина, по которой я хочу это сделать, заключается в том, что я думаю, что это сделает код более читабельным, исключив излишнюю конверсию (и, следовательно, также немного короче, хотя и ненамного), и это сделает рефакторинг и попытки использования различных режимов интеллектуальных указателей менее хлопотными.
Возможно, мне следовало бы назвать это unmanaged_ptr
вместо any_ptr
.Это было бы более правильно описать семантику.Я прошу прощения за грязное имя.
РЕДАКТИРОВАТЬ 2
Хорошо, вот класс, который я имел в виду.Я назвал его dumb_ptr.
template<typename T>
class dumb_ptr {
public:
dumb_ptr(const dumb_ptr<T> & dm_ptr) : raw_ptr(dm_ptr.raw_ptr) { }
dumb_ptr(T* raw_ptr) : raw_ptr(raw_ptr) { }
dumb_ptr(const boost::shared_ptr<T> & sh_ptr) : raw_ptr(sh_ptr.get()) { }
dumb_ptr(const boost::weak_ptr<T> & wk_ptr) : raw_ptr(wk_ptr.lock().get()) { }
dumb_ptr(const boost::scoped_ptr<T> & sc_ptr) : raw_ptr(sc_ptr.get()) { }
dumb_ptr(const std::auto_ptr<T> & au_ptr) : raw_ptr(au_ptr.get()) { }
T& operator*() { return *raw_ptr; }
T * operator->() { return raw_ptr; }
operator T*() { return raw_ptr; }
private:
dumb_ptr() { }
dumb_ptr<T> operator=(const dumb_ptr<T> & x) { }
T* raw_ptr;
};
Он может автоматически конвертировать из общих интеллектуальных указателей и может рассматриваться как необработанный указатель T *, в дальнейшем он может быть автоматически преобразован в T *.Конструктор по умолчанию и оператор присваивания (=) были скрыты, чтобы удержать людей от использования его для чего-либо, кроме аргументов функции.При использовании в качестве аргумента функции может быть сделано следующее:
void some_fn(dumb_ptr<A> ptr) {
B = ptr->b;
A a = *ptr;
A* raw = ptr;
ptr==raw;
ptr+1;
}
Это почти все, что вы хотели бы сделать с указателем.Он имеет ту же семантику, что и необработанный указатель T*
.Но теперь его можно использовать с любым интеллектуальным указателем в качестве параметра без необходимости повторять код преобразования (.get, .lock) на каждом сайте вызова.Кроме того, если вы измените свои умные указатели, вам не придется исправлять каждый сайт вызовов.
Теперь я думаю, что это достаточно полезно, и я не вижу проблем с этим?