作用域§
释放超出作用域的对象。
C++中,RAII依托栈和析构函数,管理内存资源。
因此,在类的析构函数中释放申请的资源即可。离开作用域时,即使在作用域中出现异常,编译器也会自动调用析构函数。
模板化§
template<typename T>
class smart_ptr {
public:
explicit smart_ptr(T* ptr = nullptr)
: ptr_(ptr) {}
~smart_ptr() {
delete ptr_;
}
T* get() const { return ptr_; }
private:
T* ptr_;
};
运算符§
*运算符,解引用。
->运算符,指向对象成员。
在布尔表达式中使用。
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
拷贝构造和赋值§
禁止拷贝行为。
避免指针传递到其它对象中,将导致重复释放的错误。
作为智能指针,自然要尽可能复用一个指针,并且最终能够安全释放。
smart_ptr(const smart_ptr&) = delete;
smart_ptr& operator=(const smart_ptr&) = delete;
在拷贝时转移指针。
copy-and-swap idiom
...
smart_ptr(const other&) {
ptr_ = other.release();
}
smart_ptr& operator=(const smart_ptr& rhs) {
smart_ptr(rhs).swap(*this);
return *this;
}
...
T* release() {
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(smart_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
}
...
移动语义§
提供移动构造函数后未手动提供拷贝构造函数,后者将自动被删除。
smart_ptr(smart_ptr&& other) {
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr rhs) {
rhs.swap(*this);
return *this;
}
赋值函数为拷贝还是移动,依赖于构造参数采用拷贝还是移动。
引用计数§
以上的实现,基本上满足C++11的unique_ptr。下面将其改进为shared_ptr。
为了使得多个智能指针共享一个对象,添加引用计数功能。
class shared_count {
public:
shared_count() : count_(1) {}
void add_count() { ++count_; }
std::size_t reduce_count() { return --count_; }
std::size_t get_count() const { return count_; }
private:
std::size_t count_;
};
类型转换§
子类指针转向基类指针§
添加构造函数,借助模板完成转换。
template<typename U>
smart_ptr(smart_ptr<U>&& other) {
ptr_ = other.release();
}
指针类型转换§
- static_cast
- dynamic_cast
- reinterpret_cast
- const_cast
以dynamic_cast为例,实现如下
template<typename T, typename U>
smart_ptr<T> dynamic_pointer_cast(const smart_ptr<U>& other) {
T* ptr = dynamic_cast<T*>(other.get());
return smart_ptr<T>(ptr);
}
完整代码§
当引入第二个模板参数类型U时,不同模板类型实例之间并不能互相访问私有成员。需要显式定义为友元。
在现有smart_ptr基础上,使其同时维护一个引用计数对象。代码如下
class shared_count {
public:
shared_count() noexcept :count_(1) {}
void add_count() noexcept {
++count_;
}
::std::size_t reduce_count() noexcept {
return --count_;
}
::std::size_t get_count() noexcept {
return count_;
}
private:
::std::size_t count_;
};
template <typename T>
class smart_ptr {
public:
template <typename U>
friend class smart_ptr;
explicit smart_ptr(T* ptr = nullptr) :ptr_(ptr) {
if(ptr_) {
count_ = new shared_count();
}
}
~smart_ptr() {
if(ptr_&&!count_->reduce_count()) {
delete ptr_;
delete count_;
}
}
smart_ptr(const smart_ptr& other) {
ptr_ = other.ptr_;
if(ptr_) {
other.count_->add_count();
count_ = other.count_;
}
}
smart_ptr& operator=(smart_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
smart_ptr(smart_ptr&& other) {
ptr_ = other.ptr_;
if(ptr_) {
count_ = other.count_;
other.ptr_ = nullptr;
}
}
template <typename U>
smart_ptr(const smart_ptr<U>& other) noexcept {
ptr_ = other.ptr_;
if(ptr_) {
other.count_->add_count();
count_ = other.count_;
}
}
template <typename U>
smart_ptr(smart_ptr<U>&& other) noexcept {
ptr_ = other.ptr_;
if(ptr_) {
count_ = other.count_;
other.ptr_ = nullptr;
}
}
template <typename U>
smart_ptr(const smart_ptr<U> other, T* ptr) noexcept {
ptr_ = ptr;
if(ptr_) {
other.count_->add_count();
count_ = other.count_;
}
}
T* get() const noexcept {
return ptr_;
}
::std::size_t use_count() const noexcept {
if(ptr_) {
return count_->get_count();
}
return 0;
}
void swap(smart_ptr& rhs) noexcept {
using ::std::swap;
swap(ptr_, rhs.ptr_);
swap(count_, rhs.count_);
}
T& operator*() const noexcept {
return *ptr_;
}
T* operator->() const noexcept {
return ptr_;
}
operator bool() const noexcept {
return ptr_;
}
private:
T* ptr_;
shared_count* count_;
};
template <typename T>
void swap(smart_ptr<T>& lhs, smart_ptr<T>& rhs) noexcept {
lhs.swap(rhs);
}
template <typename T, typename U>
smart_ptr<T> static_pointer_cast(
const smart_ptr<U>& other) noexcept {
T* ptr = static_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}
template <typename T, typename U>
smart_ptr<T> dynamic_pointer_cast(
const smart_ptr<U>& other) noexcept {
T* ptr = dynamic_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}
template <typename T, typename U>
smart_ptr<T> reinterpret_pointer_cast(
const smart_ptr<U>& other) noexcept {
T* ptr = reinterpret_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}
template <typename T, typename U>
smart_ptr<T> const_pointer_cast(
const smart_ptr<U>& other) noexcept {
T* ptr = const_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}
测试代码如下
#include <iostream>
using namespace std;
#include "smart_ptr.h"
class shape {
public:
virtual ~shape() { puts("~shape()"); }
};
class circle : public shape {
public:
~circle() { puts("~circle()"); }
};
int main() {
cerr << "construct smart_ptr<circle> ptr1" << endl;
smart_ptr<circle> ptr1(new circle());
cerr << "use count of ptr1: " << ptr1.use_count() << endl;
cerr << "use ptr1 copy construct smart_ptr<shape> ptr2" << endl;
smart_ptr<shape> ptr2;
cerr << "use count of ptr2: " << ptr2.use_count() << endl;
ptr2 = ptr1;
cerr << "use count of ptr2: " << ptr2.use_count() << endl;
cerr << "use count of ptr1: " << ptr1.use_count() << endl;
cerr << "use ptr1 move construct smart_ptr<shape> ptr3" << endl;
smart_ptr<shape> ptr3;
cerr << "use count of ptr3: " << ptr3.use_count() << endl;
ptr3 = move(ptr1);
cerr << "use count of ptr3: " << ptr3.use_count() << endl;
cerr << "use count of ptr2: " << ptr2.use_count() << endl;
cerr << "use count of ptr1: " << ptr1.use_count() << endl;
cerr << "use dynamic_cast convert smart_ptr<shape> ptr2 to smart_ptr<circle> ptr1" << endl;
ptr1 = dynamic_pointer_cast<circle>(ptr2);
cerr << "use count of ptr2: " << ptr2.use_count() << endl;
cerr << "use count of ptr1: " << ptr1.use_count() << endl;
return 0;
}
输出结果如下
construct smart_ptr<circle> ptr1
use count of ptr1: 1
use ptr1 copy construct smart_ptr<shape> ptr2
use count of ptr2: 0
use count of ptr2: 2
use count of ptr1: 2
use ptr1 move construct smart_ptr<shape> ptr3
use count of ptr3: 0
use count of ptr3: 2
use count of ptr2: 2
use count of ptr1: 0
use dynamic_cast convert smart_ptr<shape> ptr2 to smart_ptr<circle> ptr1
use count of ptr2: 3
use count of ptr1: 3
~circle()
~shape()