C++ primer 5th 课后习题 --实现自己版本的shared_ptr和unique_ptr
行为像指针的类都可以像这样进行类比定义,可以自动管理动态内存,无需自己delete
不用再担心悬挂指针、内存泄漏或对同一内存区域两次delete之类的错误
但是注意:无论是shared_ptr还是我们自己实现的类模板my_shared_ptr都存在循环引用时(比如用于双向链表时),依然导致内存泄漏的问题。解决办法是弱引用指针weak_ptr配合shared_ptr使用。
其他关于智能指针使用的注意事项可见C++11智能指针的常见误区 Top10
//16.28 编写自己版本的shared_ptr和unique_ptr
//======================shared_ptr=============================
template <typename T>
class my_shared_ptr {
using DelFuncPtr = void(*)(T*); //删除器的函数指针
public:
//构造函数
my_shared_ptr(T* _ptr=nullptr,DelFuncPtr _del=nullptr)
:ptr(_ptr),del(_del),count_ptr(new size_t(ptr!=nullptr)){ }//指向了元素则将引用计数置为1 否则置为0
//析构函数
~my_shared_ptr() {
if (!ptr) return;
if (--*count_ptr==0) {
del ? del(ptr) : delete ptr;//是否传入了自定义删除器?是 调用删除器删除ptr : 否 直接删除ptr
delete count_ptr;
cout << "the left hand side has been released!" << endl;
}
ptr = nullptr;
del = nullptr;
count_ptr = nullptr;
}
//拷贝构造函数
my_shared_ptr(const my_shared_ptr& msp) :ptr(msp.ptr),del(msp.del),count_ptr(msp.count_ptr){
++*count_ptr;//拷贝构造后引用计数+1
}
//拷贝赋值运算符
//my_shared_ptr& operator=(const my_shared_ptr& rhs) {
// ++*rhs.count_ptr;//先递增右侧运算对象的引用计数 即可保证自赋值的安全
// if (--*count_ptr==0) {//递减本对象的引用计数 如果为0释放资源
// del ? del(ptr) : delete ptr;
// delete count_ptr;
// }
// //更新指针
// ptr = rhs.ptr;
// count_ptr = rhs.count_ptr;
// del = rhs.del;
// return *this;
//}
//可以编写自定义的swap函数来优化分配了资源的类代码
//注:使用拷贝和交换技术的赋值运算符自动就是异常安全的 且能正确处理自赋值
void swap(my_shared_ptr& rhs) noexcept{
using std::swap;//虽然编译器可以根据参数类型调用标准库的swap 但此声明是个好习惯
swap(ptr,rhs.ptr);
swap(count_ptr,rhs.count_ptr);
swap(del,rhs.del);
}
//对于智能指针来说 拷贝赋值和移动赋值其实是一个概念
my_shared_ptr& operator=(my_shared_ptr rhs) {//不传引用而传值
//因为=拷贝赋值函数是以传值方式传递给=运算符 因此拷贝了右侧的副本 调用了拷贝构造函数 右侧引用计数+1
//交换操作中又是引用传递 因此swap后 rhs的副本和左侧状态进行了交换
//当退出=运算符函数作用域后 rhs临时副本调用析构函数 递减原本左侧的引用计数 如果为0则释放资源
swap(rhs);
return *this;
}
//移动构造函数 直接接管资源
my_shared_ptr(my_shared_ptr&& msp)noexcept :my_shared_ptr() { swap(msp); }//先委托默认构造函数 后交换状态
//其他成员函数
void reset(T* _ptr=nullptr,DelFuncPtr _del=nullptr) {
//使用拷贝和交换技术
my_shared_ptr temp(_ptr,_del);//构造一个临时对象
swap(temp);
}
T* get() const noexcept{
return ptr;//我们必须保证get()到的指针不会被delete! 特别是不能用get()的指针初始化另外一个智能指针或为其赋值
}
T& operator*()const noexcept {
return *ptr;
}
T* operator->()const noexcept {
return ptr;
}
size_t use_count() const noexcept{
return *count_ptr;
}
explicit operator bool() const noexcept {
//不允许发生隐式类型转换 注:if() while()等条件判断自动发生类型转换
return (ptr!=nullptr);
}
private:
T* ptr = nullptr; //指向元素
DelFuncPtr del= nullptr; //指向可调用对象作为删除器
size_t* count_ptr = nullptr; //指向引用计数
};
//======================unique_ptr=============================
//my_unique_ptr默认使用的删除器
class Deleter {
public:
//成员模板:
template <typename T>
void operator()(T* p)const {
delete p;
cout << "deleted p by default Delter" << endl;
}
};
template <typename T,typename D=Deleter>
class my_unique_ptr {
public:
my_unique_ptr(T* _ptr, const D& _del = D()) :ptr(_ptr), del(_del) { cout << "constrctor" << endl; }
~my_unique_ptr() { del(ptr); }
//unique_ptr由于独占对象 因此不允许拷贝 定义其拷贝构造函数和拷贝赋值运算符为删除的
my_unique_ptr(const my_unique_ptr&) = delete;
my_unique_ptr& operator=(const my_unique_ptr&) = delete;
//移动构造函数和移动赋值运算符可用
my_unique_ptr(my_unique_ptr&& moved)noexcept :ptr(moved.ptr),del(std::move(moved.del)){
//将被移动对象置为可析构状态
moved.ptr = nullptr;
}
my_unique_ptr& operator=(my_unique_ptr&& moved) {
if (this!=&moved) {//直接检查自赋值
del(ptr);//释放旧资源
ptr = moved.ptr;
del = std::move(moved.del);
moved.ptr = nullptr;//置为可析构状态
}
return *this;
}
//其他成员函数
void reset(T* p=nullptr) noexcept {
del(ptr);//释放旧资源
ptr = p;
}
//注:正如std::unique_ptr一样 release()返回的指针需要自己管理内存资源 函数内部并没有释放掉原有资源
T* release() {//放弃对指针的控制权返回指针 并将ptr置为空
T* ret = ptr;
ptr = nullptr;
return ret;
}
void swap(my_unique_ptr& rhs) {
using std::swap;
swap(ptr,rhs.ptr);
swap(del,rhs.del);
}
T* get() const noexcept{
return ptr;
}
D& get_deleter() noexcept {
return del;
}
const D& get_deleter() const noexcept {
return del;
}
explicit operator bool() const noexcept {
return (ptr!=nullptr);
}
T& operator*() const noexcept {
return *ptr;
}
T* operator->()const noexcept {
return ptr;
}
T& operator[](size_t n)const noexcept {
return ptr[n];
}
private:
T* ptr=nullptr;//指向元素
D del;//删除器
};
int main() {
//test for shared_ptr
cout << "==================test for shared_ptr====================" << endl;
my_shared_ptr<int> sp1(new int(5));
my_shared_ptr<int> sp2(new int(10));
my_shared_ptr<int> sp3(sp1);
//sp3=nullptr 发生了nullptr->my_shared_ptr的隐式转换
DebugDelete dd;
cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;
cout << "after swap sp1 = sp2 : " << endl;
sp1 = sp2;
cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;
cout << "after reset sp2.reset(new int(x)): " << endl;
sp2.reset(new int(15));
sp2.reset(new int(20));
cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;
cout << "after bound a deleter: " << endl;
sp3.reset(new int(25), [](int* p) { delete p; cout << "release by lambda"<<endl; });
cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;
cout << "==================test for unique_ptr====================" << endl;
DebugDelete d;
my_unique_ptr<int> up1(new int(1));
cout << "sp1-adress: " << up1.get() << " sp1-deref: " << *up1 << endl;
if (up1) {
cout << "explicit bool(): up1 is not empty pointer" << endl;
}
my_unique_ptr<int,DebugDelete> up2(new int(2),d);
cout << "up2-adress: " << up2.get() << " up2-deref: " << *up2 << endl;
cout << endl<<"test for move constructor: " << endl;
my_unique_ptr<int> up3(std::move(up1));
cout << "up3-adress: " << up3.get() << " up3-deref: " << *up3 << endl;
my_unique_ptr<int,DebugDelete> up4(std::move(up2));
cout << "up4-adress: " << up4.get() << " up4-deref: " << *up4 << endl;
cout<< endl << "test for reset: " << endl;
up1.reset(new int(3));
if (up1) {
cout << "explicit bool(): up1 is not empty pointer" << endl;
cout << "sp1-adress: " << up1.get() << " sp1-deref: " << *up1 << endl;
}
up2.reset();
if (!up2) {
cout << "explicit bool(): up2 is empty pointer" << endl;
}
cout << endl << "test for release:" << endl;
auto receive = up1.release();
if (!up1) {
cout << "after up1.release() up1 points to nullptr" << endl;
cout << "and the receive pointer is: " << *receive << endl;
}
cout << endl << "test for swap:" << endl;
up3.swap(up1);
if (!up3) {
cout << "after swap up3 points to nullptr " << endl;
cout << "and the up1 now points to riginal value that up3 pointed to: " << *up1 << endl;
}
cout << endl << "test for get_deleter: " << endl;
up1.get_deleter().operator()(new int(2));
up2.get_deleter().operator()(new int());
cout << endl<< "===============out of area, call the destructor==============" << endl;
return 0;
}