Мне нужен умный указатель для моего проекта, который можно отправить в несколько методов в качестве параметра. Я проверил auto_ptr и shared_ptr от буста. Но ИМО, это не соответствует моим требованиям. Ниже приведены мои выводы
auto_ptr: при передаче другому методу право собственности будет передано, а базовый указатель будет удален, когда область действия этого метода закончится. Мы можем обойти это, передав auto_ptr по ссылке, но не существует механизма времени компиляции, гарантирующего, что он всегда передается по ссылке. Если по ошибке пользователь забыл передать ссылку, возникнут проблемы.
boost :: shared_ptr: это выглядит многообещающим и работает правильно для моих нужд. Но я считаю, что это перебор для моего проекта, поскольку он очень маленький.
Поэтому я решил написать тривиальный класс контейнера с шаблонным указателем, который нельзя скопировать по значению, и позаботился об удалении базового указателя. Вот
template <typename T>
class simple_ptr{
public:
simple_ptr(T* t){
pointer = t;
}
~simple_ptr(){
delete pointer;
}
T* operator->(){
return pointer;
}
private:
T* pointer;
simple_ptr(const simple_ptr<T>& t);
};
Это правильная реализация? Я сделал конструктор копирования закрытым, чтобы компилятор предупреждал, когда кто-то пытается передать его по значению.
Если случайно указатель удален, операция удаления деструктора вызовет ошибку утверждения. Как я могу это исправить?
Я новичок в C++ и очень ценю ваше предложение.
Спасибо





Чтобы ответить на ваш первый вопрос, лучшая гарантия того, что это правильно, - это реализовать вокруг него тестовую оснастку, чтобы выполнить некоторую простую задачу, и убедиться, что вы получаете ожидаемое поведение. Для меня это гораздо больше утешения, чем то, что код правильный, чем мнение какого-то случайного человека, читающего его.
Что касается вашего второго вопроса, вы работаете с Удалить, выдающим ошибку утверждения, устанавливая указатель на некоторое значение маркера после его удаления в первый раз. Что-то вроде:
if (pointer) {
delete pointer;
pointer = NULL;
} else {
error("Attempted to free already freed pointer.");
}
Проблема, с которой вы здесь столкнетесь, заключается в том, что ваш перегруженный оператор -> возвращает значение указателя, а это означает, что любой, кому вы его вернете, также может вызвать Удалить, в результате чего проверка, которую я предлагаю выше, не сработает. Например:
simple_ptr<int> myPtr = someIntPointer;
...
delete myPtr.operator->(); /* poof goes your pointered memory region */
Я мог бы порекомендовать вам не полагаться на перегрузку оператор->, а просто потребовать, чтобы те, кто использует этот класс, вызывали метод, который разыменовывает указатель внутри, прежде чем передавать это значение обратно пользователю.
Надеюсь это поможет.
Is this implementation correct? I have made copy constructor as private ...
Вы можете сделать то же самое для оператора присваивания:
simple_ptr& operator=(const simple_ptr<T>& t);
Также может быть полезна константная версия оператора разыменования, и умные указатели обычно определяют и другой тип оператора разыменования:
const T* operator->() const { return pointer; }
const T& operator*() const { return *pointer; }
T& operator*() { return *pointer; }
If by chance the pointer is deleted, delete operation on the destructor will throw assertion error. How can I workaround this?
Вы имеете в виду, если я сделаю это:
//create instance
Car* car = new Car;
//assign to smart ptr
simple_ptr<Car> ptr(car);
//explicit delete
delete car;
//... assertion when ptr is destroyed ...
Способ (я не знаю, является ли это способ хорошо), чтобы предотвратить это, состоит в том, чтобы объявить конструктор и / или деструктор и / или оператор удаления класса T как private и сказать, что simple_ptr является friend из класс T (так что только класс simple_ptr может создавать и / или уничтожать и / или удалять экземпляры T).
Marking the new operator as private in T class seems to be impossible as I have to modify all the classes which will be used with simple_ptr
Да, это правда: чтобы сделать мое предложение непосредственно выше, вам нужно будет изменить определения классов.
Если ваш вопрос: «как я могу сделать невозможным двойное удаление, не изменяя определения классов?» тогда я думаю, что ответы таковы:
smart_ptr опросить ваш менеджер кучи, чтобы узнать, выделено ли это воплощение этого указателя, прежде чем вы его удалите.Спасибо за ответ. Пометить новый оператор как частный в классе T кажется невозможным, поскольку мне нужно изменить все классы, которые будут использоваться с simple_ptr.
Иногда я видел использование простых макросов DISABLE_COPY:
#define DISABLE_COPY(Class) \
Class(const Class &); \
Class &operator=(const Class &);
Таким образом, принято определять конструктор копирования и оператор присваивания как частные для вашей задачи.
Разве вам не нужно включать "private:" перед этими строками? (Хотя это означало бы неожиданное изменение режима доступа для более поздних операторов ... К сожалению, нет способа «протолкнуть» и «вытолкнуть» текущий режим доступа к члену.)
Трудно превзойти boost :: noncopyable. Без private это заменяет сгенерированные компилятором реализации по умолчанию неопределенными общедоступными объявлениями. Это все еще ошибка компоновщика. Добавьте к их аргументам «volatile», и вы поймаете 99% во время компиляции.
Вы сделали boost :: scoped_ptr
Также прочтите комментарий j_random_hacker.
Да, и, пожалуйста, используйте boost :: scoped_ptr вместо вашей собственной реализации просто потому, что сверхразумные мастера C++ потратили много времени на ее реализацию и убедились, что она ведет себя так, как ожидалось.
Пожалуйста, используйте boost :: scoped_ptr <>, как предложил Мартин Йорк, потому что он:
Хотя я не вижу никаких проблем с вашей реализацией (после применения изменений, предложенных ChrisW), у C++ много темных углов, и я не удивлюсь, если есть какой-то неясный угловой случай, который вы, я и другие здесь не смогли место.
boost :: scoped_ptr также имеет то преимущество, что он никогда не пытается удалить указатель на неполный тип класса - он использует boost :: checked_delete вместо простого удаления. Это особенно полезно при использовании частной переменной-члена scoped_ptr для хранения деталей реализации.
Ух ты. Это как раз тот случай, о котором я говорю. Спасибо, Дуг!
У вас есть два варианта:
boost::scoped_ptr уже подробно описан j_random_hacker, потому что он не копируемый (не разделяет права собственности, как shared_ptr) и не перемещается (не передает право собственности, как auto_ptr. Auto_ptr имеет конструктор копирования, но он не копирует. Он перемещает оригинал указатель на * this). boost::scoped_ptr - это именно то, что вам нужно.const auto_ptr не допускает передачу права собственности. И возьмите свой параметр по ссылке на const (auto_ptr<T> const&). Если вместо этого автор функции принимает значение по значению, он все равно не будет работать, если вы попытаетесь передать const auto_ptr, потому что его конструктору копирования требуется неконстантный auto_ptr.До C++ 1x, boost :: scoped_ptr - лучший выбор для ваших нужд или const auto_ptr, если вам нужно использовать официальные стандартные вещи (прочтите это). В C++ 1x вы можете использовать std::unique_ptr как лучшую альтернативу auto_ptr, потому что вам нужно явно указать, когда вы хотите передать право собственности. Попытка скопировать его приведет к ошибке времени компиляции. unique_ptr немного подробно описан в ответе это.
О нет. Когда они начали называть это 1x?
они начали так называть это в прошлом году после какой-то встречи - будем надеяться, что они закончат его в 2010 году :)
Вы должны использовать boost :: scoped_ptr <>, как уже упоминалось.
В общем случае, если вам нужно сделать класс не копируемым, вы должны унаследовать от boost :: noncopyable, т.е.
#include <boost/utility.hpp>
class myclass : boost::noncopyable
{
...
};
Это делает всю работу по предотвращению копирования и хорошо самодокументируется.
Спасибо за ответ. Но установка значения маркера не сработала. Это все еще выдает ошибку.