目录
1.unique_ptr:
1.1.拷贝构造和赋值语句:
在unique_ptr里因为其所有权的唯一性,拷贝构造和赋值语句会被删除。但是可以进行移动构造和移动赋值。
还要注意的一点是在调用函数时,因为传参时调用拷贝构造函数,可能传参出现错误。
1.2.删除一个对象和一组对象:
unique_ptr提供两个模板,一个针对一个对象,一个针对一组对象。
2.shared_ptr:
2.1.构成:
shared_ptr多出一个结构引用计数,用于计算该对象被多少指针对象拥有。可用use_cout计算。
2.2简单实现和内存分布:
template<class _Ty>
class RefCnt
{
private:
_Ty* mptr;
int ref;
public:
RefCnt(_Ty* p = nullptr) :mptr(p), ref(mptr != nullptr)
{
}
~RefCnt() {}
};
template<class _Ty, class _Dx= MyDeletor<_Ty>>//_Dx是删除器
class my_shared_ptr
{
public:
my_shared_ptr(_Ty* p = nullptr) : ptr(nullptr)
{
if (p != nullptr)
{
ptr = new RefCnt(p);
}
}
~my_shared_ptr()
{
if (ptr != nullptr && --ptr->ref == 0)
{
mDeletor(ptr->mptr);
}
ptr = nullptr;
}
private:
_Ty* _Ptr;
_Dx mDeletor;
}
class Object
{
private:
int value;
public:
Object(int x = 0) :value(x) {};
~Object() {};
}
int main()
{
my_shared_ptr<Object>op1(new Object(10));
}
内存分布:
2.3拷贝构造和赋值语句:
拷贝构造和赋值语句都是指向同一块RefCnt,并使其引用计数+1.
这里边最难的应该就是移动赋值。在移动赋值时,先确定取出自赋值。如果this指针的指向和s的指向相同,即两个指向同一个RefCnt,那么移动赋值就只是把该计数-1,并把s置空。如果ptr->ref=1,并且不为空,所以在清空ptr时,会直接调用删除器删除对象。最后就是正常的赋值,和清空s.ptr.
//拷贝构造
my_shared_ptr(const my_shared_ptr& s)
{
if (ptr != nullptr)
{
s.ptr = ptr;
ptr->ref += 1;
}
}
//移动构造
my_shared_ptr(my_shared_ptr&& s)
{
if (ptr != nullptr)
{
ptr = s.ptr;
s.ptr = nullptr;
}
}
//赋值语句
my_shared_ptr& operator=(my_shared_ptr& s)
{
if (this == &s || ptr == s.ptr)return *this;
if (ptr != nullptr && --ptr->ref == 0)
{
mDeletor(ptr);
}
ptr = s.ptr;
if (ptr != nullptr)
{
ptr->ref += 1;
}
return *this;
}
//移动赋值
my_shared_ptr& operator=(my_shared_ptr&& s)
{
if (this == &s)return *this;
if (ptr == s.ptr && ptr != nullptr && s.ptr != nullptr)
{
ptr->ref -= 1;
s.ptr = nullptr;
return *this;
}
if (ptr != nullptr && --ptr->ref == 0)
{
mDeletor(ptr);
}
ptr = s.ptr;
s.ptr = nullptr;
return *this;
}
2.4共享性智能指针的缺点:
(1)共享性智能指针在多线程中不安全,因为它引用计数不是原子操作,可以优化为原子操作
(2)共享性智能指针相互引用会造成无法释放。
#include<iostream>
#include<memory>
using namespace std;
class Child;
class Parent
{
public:
shared_ptr<Child>child;
Parent() { cout << "Create Parent"; }
~Parent() { cout << "Destory Parent"; }
void hi() { cout << "hello"; }
};
class Child
{
public:
shared_ptr<Parent>parent;
Child() { cout << "Create Child"; }
~Child() { cout << "Destory Child"; }
};
int main()
{
shared_ptr<Parent>p = make_shared<Parent>();
shared_ptr<Child>c = make_shared<Child>();
p->child = c;
c->parent = p;
c->parent->hi();
return 0;
}
此时的内存分配:
Parent对象在建立时,ref=1,Child同理,ref=1。当Parent中child指向Child后,ref+1=2。Child同理。在生存期结束时,ref最后等于1,不能析构。
只有建立没有析构,解决方法就是使用weak_ptr。
#include<iostream>
#include<memory>
using namespace std;
class Child;
class Parent
{
public:
weak_ptr<Child>child;
Parent() { cout << "Create Parent"<<endl; }
~Parent() { cout << "Destory Parent" << endl; }
void hi() { cout << "hello"<<endl; }
};
class Child
{
public:
weak_ptr<Parent>parent;
Child() { cout << "Create Child" << endl; }
~Child() { cout << "Destory Child" << endl; }
};
int main()
{
shared_ptr<Parent>p = make_shared<Parent>();
shared_ptr<Child>c = make_shared<Child>();
p->child = c;
c->parent = p;
p->hi();
return 0;
}
后便会详细接收为什么使用weak_ptr可以解决shared_ptr的相互引用问题。