std::unique_ptr
— это реализация интеллектуального указателя на C++, которая обеспечивает монопольное владение объектом, а это означает, что объект, управляемый unique_ptr, может принадлежать только одному экземпляру unique_ptr.
Вот реализация std::unique_ptr
на C++:
template <typename T> struct default_delete { default_delete() = default; default_delete(const default_delete&) = default; template <typename U> default_delete(const default_delete<U>&) {} void operator()(T* ptr) const { delete ptr; } }; template <typename T, typename Deleter = default_delete<T>> class unique_ptr { public: unique_ptr() = default; unique_ptr(T* ptr) : ptr_(ptr) {} unique_ptr(T* ptr, const Deleter& deleter) : ptr_(ptr), deleter_(deleter) {} ~unique_ptr() { deleter_(ptr_); } unique_ptr(const unique_ptr& other) = delete; unique_ptr(unique_ptr&& other) noexcept : ptr_(other.release()), deleter_(other.deleter_) {} // generalized move ctor template <typename U, typename E> unique_ptr(unique_ptr<U, E>&& other) noexcept : ptr_(other.release()), deleter_(std::forward<E>(other.get_deleter())) {} unique_ptr& operator=(const unique_ptr& other) = delete; unique_ptr& operator=(unique_ptr&& other) noexcept { unique_ptr(std::move(other)).swap(*this); return *this; } void reset(T* ptr) noexcept { deleter_(ptr_); ptr_ = ptr; } T* release() noexcept { auto old_ptr = ptr_; ptr_ = nullptr; return old_ptr; } void swap(unique_ptr& other) noexcept { using std::swap; swap(ptr_, other.ptr_); } T& operator*() const noexcept { return *ptr_; } T* operator->() const noexcept { return ptr_; } T* get() const noexcept { return ptr_; } Deleter get_deleter() const noexcept { return deleter_; } explicit operator bool() { return ptr_ != nullptr; } private: T* ptr_ = nullptr; Deleter deleter_ = Deleter(); };
Примечание. Это простая реализация, которая не включает в себя все возможности и функции std::unique_ptr
.
Основные моменты:
- Эта реализация
unique_ptr
позволяет вам передавать пользовательский модуль удаления в качестве второго параметра шаблона. Средство удаления по умолчанию —default_delete
, которое вызываетdelete
для управляемого указателя. Однако вы можете указать пользовательское средство удаления, которое может делать все, что вы хотите, с управляемым указателем, когдаunique_ptr
выходит за пределы области действия. - Метод перемещения и замены используется для реализации оператора перемещения-присваивания. Идея этого метода заключается в определении временного экземпляра
unique_ptr
и обмене его содержимым с целевым экземпляромunique_ptr
. Этот подход гарантирует, что целевой экземплярunique_ptr
останется в допустимом состоянии, даже если присваивание завершится ошибкой из-за возникновения исключения. Временный экземплярunique_ptr
уничтожается в конце области действия, а счетчик ссылок динамически выделяемого объекта корректно управляется. - Эта реализация обобщенного конструктора перемещения позволяет перемещать
unique_ptr
любого типаU
и средство удаленияE
вunique_ptr
типаT
и средство удаленияDeleter
. Управляемый указатель передается из экземпляраother
путем вызоваrelease()
, а пользовательское средство удаления передается путем вызоваget_deleter()
и пересылки результата конструкторуDeleter
. В этой реализации используетсяstd::forward
, чтобы гарантировать сохранение типа пользовательского удаления, даже если это ссылка на rvalue. Обратите внимание, что средство удаления также должно поддерживать обобщенный конструктор копирования/перемещения.
Вот несколько тестов для проверки функциональности unique_ptr
:
struct Foo { Foo() { std::cout << "Foo created\n"; } ~Foo() { std::cout << "Foo destroyed\n"; } }; struct Base { virtual void bar() { std::cout << "Base\n"; } }; struct Derived : public Base { void bar() override { std::cout << "Derived\n"; } }; struct FileDeleter { void operator()(FILE* file) const { std::cout << "Closing file..." << std::endl; fclose(file); } }; int main() { // Test 1: Construct unique_ptr and verify the object is created { unique_ptr<Foo> p1(new Foo); // Foo created } // Foo destroyed // Test 2: Reset unique_ptr and verify the original object is destroyed { unique_ptr<Foo> p1(new Foo); // Foo created p1.reset(nullptr); // Foo destroyed } // Test 3: Move unique_ptr and verify the moved-from instance has a nullptr { unique_ptr<Foo> p1(new Foo); // Foo created unique_ptr<Foo> p2 = std::move(p1); std::cout << "p1 is null: " << (p1 ? "false" : "true") << '\n'; // outputs true } // Foo destroyed // Test 4: Move assignment and verify the moved-from instance has a nullptr { unique_ptr<Foo> p1(new Foo); // Foo created unique_ptr<Foo> p2; p2 = std::move(p1); std::cout << "p1 is null: " << (p1 ? "false" : "true") << '\n'; // outputs true } // Foo destroyed // Test 5: Generalized copy constructor with related types { unique_ptr<Derived> p1(new Derived); unique_ptr<Base> p2(std::move(p1)); p2->bar(); // outputs Derived } // Test 6: Custom deleter { unique_ptr<FILE, FileDeleter> file(tmpfile(), FileDeleter()); if (!file) { std::cerr << "Failed to open temporary file." << std::endl; return 1; } fprintf(file.get(), "Hello, world!"); rewind(file.get()); char buffer[50]; fgets(buffer, sizeof(buffer), file.get()); std::cout << "Content of file: " << buffer << std::endl; // outputs Hello World! } // outputs Closing file... return 0; }
Эти тесты проверяют основные функции, такие как построение unique_ptr
, сброс un
ique_ptr
, перемещение unique_ptr
, использование универсального конструктора перемещения для создания unique_ptr
связанного типа и использование пользовательского удаления.
Связанное чтение: