目录
智能指针原理
智能指针:一个特殊的指针 ,是对裸指针进行包装的类,指针生命周期结束时主动释放堆空间 。
关键技术:操作符重载,实现类对象可以模拟指针的行为
// 智能指针的初步实现
#include <iostream>
#include <string>
using namespace std;
class Test
{
int i;
public:
Test(int i)
{
cout << "Test(int i)" << endl;
this->i = i;
}
int value()
{
return i;
}
~Test()
{
cout << "~Test()" << endl;
}
};
class Pointer
{
Test* mp; // 裸指针
public:
Pointer(Test* p = NULL)
{
mp = p;
}
Pointer(const Pointer& obj) // 一片堆空间最多只能由一个指针指向
{
mp = obj.mp;
const_cast<Pointer&>(obj).mp = NULL;
}
Pointer& operator = (const Pointer& obj)
{
if( this != &obj )
{
delete mp;
mp = obj.mp;
const_cast<Pointer&>(obj).mp = NULL;
}
return *this;
}
Test* operator -> ()
{
return mp;
}
Test& operator * ()
{
return *mp;
}
bool isNull()
{
return (mp == NULL);
}
~Pointer()
{
delete mp; //关键
}
};
int main()
{
Pointer p1 = new Test(0);
cout << p1->value() << endl;
Pointer p2 = p1;
cout << p1.isNull() << endl;
cout << p2->value() << endl;
return 0;
}
发现当智能指针发生拷贝构造后发生了资源转移,不能多个指针指向同一个堆对象,且智能指针只能指向堆内存,不能指向其他系统资源如文件等,不能完美的释放堆数组,局限性很大
解决方案: 基于引用计数的智能指针:一个堆对象可以被多个智能指针指向,当没有智能指针指向时释放堆空间,模板参数提供删除器选项,可根据需要传入任意自定义删除器
#include <iostream>
using namespace std;
template <typename T>
struct DeleteHeap
{
public:
void operator () (T*& p)
{
delete p;
p = NULL;
}
};
template <typename T>
struct DeleteHeapArray
{
public:
void operator () (T*& p)
{
delete[] p;
p = NULL;
}
};
template <typename T>
struct CloseFile
{
public:
void operator () (FILE*& fp)
{
cout << "fclose(fp)" << endl;
fclose(fp);
fp = NULL;
}
};
template <typename T, class Deleter = DeleteHeap<T>>
class SharedPointer
{
private:
T* m_ptr;
int* m_refCount;
public:
SharedPointer() : m_ptr(nullptr), m_refCount(nullptr)
{
}
explicit SharedPointer(T* ptr) : m_ptr(ptr), m_refCount(nullptr) // explict
{
if(m_ptr != nullptr)
{
m_refCount = new int(1);
}
}
SharedPointer(const SharedPointer<T>& obj)
{
m_ptr = obj.m_ptr;
m_refCount = obj.m_refCount;
if(m_refCount != nullptr) // 引用计数指针不为空,说明拷贝对象不是一个空指针
{
(*m_refCount)++;
}
}
SharedPointer<T>& operator = (const SharedPointer<T>& obj)
{
if(this != &obj)
{
reset();
m_ptr = obj.m_ptr;
m_refCount = obj.m_refCount;
if(m_refCount != nullptr)
{
(*m_refCount)++;
}
}
return *this;
}
// 返回引用计数
int use_count()
{
if(m_refCount)
return *m_refCount;
return 0;
}
// 引用计数>0时,引用计数-1;指针置空,若引用计数为0 释放对象
void reset()
{
T* toDel = m_ptr;
int* ref = m_refCount;
m_ptr = nullptr;
m_refCount = nullptr;
if(ref != nullptr)
{
(*ref)--;
if(*ref == 0)
{
Deleter()(toDel);
delete ref;
}
}
}
bool unique()
{
return (use_count() == 1);
}
T& operator * ()
{
return *m_ptr;
}
T* operator -> ()
{
return m_ptr;
}
T* get() const
{
return m_ptr;
}
// ...
~SharedPointer(){
reset();
}
};
class Test
{
int m_i;
public:
Test(int i = 0){
m_i = i;
}
int getI() {
return m_i;
}
~Test(){
cout << "~Test()" << " " << m_i << endl;
}
};
int main()
{
SharedPointer<Test> sp1(new Test(100)); // 必须直接初始化,是explict,不支持隐士类型转换
SharedPointer<Test> sp2 = sp1;
cout << "sp1: " << sp1.get() << endl; // 0158FDE8
cout << "sp2: " << sp2.get() << endl; // 0158FDE8
cout << "sp2.use_count(): " << sp2.use_count() << endl; // 2
sp1.reset();
cout << "sp2.use_count(): " << sp2.use_count() << endl; // 1
cout << "sp2.unique(): " << sp2.unique() << endl; // 1
sp2.reset();
SharedPointer<Test, DeleteHeapArray<Test>> sp3(new Test[2]);
SharedPointer<FILE, CloseFile<FILE>> sp4(fopen("./test.txt", "w+"));
fwrite("123", 3, 1, sp4.get());
return 0;
}
STL中的智能指针
STL中的智能指针 :auto_ptr(C++98)、unique_ptr(C++11)、shared_ptr(C++11)、weak_ptr(C++11)
auto_ptr
—片堆空间只属于—个智能指针对象 ,多个智能指针对象不能指向同—片堆空间
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Test
{
string m_name;
public:
Test(const char* name)
{
cout << "Hello, " << name << "." << endl;
m_name = name;
}
void print()
{
cout << "I'm " << m_name << "." << endl;
}
~Test()
{
cout << "Goodbye, " << m_name << "." << endl;
}
};
int main()
{
auto_ptr<Test> pt(new Test("A"));
cout << "pt = " << pt.get() << endl;
pt->print();
cout << endl;
auto_ptr<Test> pt1(pt); //所有权转让
cout << "pt = " << pt.get() << endl; // 0
cout << "pt1 = " << pt1.get() << endl;
pt1->print();
return 0;
}
auto_ptr 的缺点
int main()
{
int* p = new int(10);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1; // 拷贝构造、赋值操作时所有权转移
cout << ap1.get() << endl; // 0
cout << (ap2.get() == p) << endl; // 1
// auto_ptr的危害
//cout << *ap1 << endl; // 所有权已经转移,不应该再次操作(太不易察觉了,怎么解决呢?)
// 慎用裸指针
auto_ptr<int> ap3(p); // 使用裸指针初始化两个智能指针对象,p指向的对象多次析构
cout << (ap3.get() == ap2.get()) << endl; // 1
// auto_ptr的析构函数使用的是delete不是delete[],所以不应该用来管理一个数组
// auto_ptr破坏了复制语义,不能放到标准容器中
return 0;
}
shared_ptr、weak_ptr
shared_ptr : 带有引用计数机制,支持多个指针指向同一个对象,最后一个指针被销毁对象才销毁,原理参考前面代码
weak_ptr :配合shared_ptr而引入的—种智能指针,起监控作用,可以解决shared_ptr循环引用问题
#include <iostream>
#include <memory>
using namespace std;
class Test
{
int m_i;
public:
Test(int i = 0){
m_i = i;
}
int getI() {
return m_i;
}
~Test(){
cout << "~Test()" << " " << m_i << endl;
}
};
shared_ptr<int> make(int val)
{
return shared_ptr<int>(new int(val));
}
template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
return shared_ptr<T> p(new T[size], std::default_delete<T[]>());
}
void myDel(Test* p)
{
delete[] p;
}
void test_shared_ptr()
{
shared_ptr<Test> sp1(new Test(1)); // 必须直接初始化,是explict,不支持隐士类型转换
shared_ptr<Test> sp2 = sp1;
cout << "sp1.get(): " << sp1.get() << endl;
cout << "sp2.get(): " << sp2.get() << endl;
cout << "sp2.use_count():" << sp2.use_count() << endl; // 2
sp1 = nullptr;
cout << "sp2.use_count():" << sp2.use_count() << endl; // 1
cout << "sp2.unique():" << sp2.unique() << endl;
sp2.reset();
// make_shard(); // 在堆中分配并初始化一个对象并返回指向该对象的shared_ptr
shared_ptr<Test> sp3 = make_shared<Test>(2);
sp3 = make_shared<Test>(3); // 释放原来指向的堆对象,重新指向新对象
sp3.reset();
shared_ptr<int> sp4 = make_shared<int>(1000);
shared_ptr<int> sp5 = make_shared<int>(); // 初始化为0
cout << endl;
cout << *sp4 << " " << *sp5 << endl;
sp5.swap(sp4); // 交换两个智能指针所指向对象
cout << *sp4 << " " << *sp5 << endl;
cout << endl;
// 指定删除器取代默认删除器(当管理动态数组时需要,手动delete[])
shared_ptr<Test> p1(new Test[2], myDel); // 当引用计数为0时,调用myDel
shared_ptr<int> p2(new int(1), [](int* p) {delete p; }); //删除器可以是lambda表达式
shared_ptr<Test> p3(new Test[2], std::default_delete<Test[]>());
}
void test_weak_ptr()
{
// 强指针:shared_ptr,弱指针:weak_ptr
// weak_ptr指向由shared_ptr管理的对象,不会改变引用计数,起"监视作用"
// lock(); 检查weak_ptr所指向的对象是否存在,存在返回指向该对象的shared_ptr,强引用计数+1,不存在返回空的shared_ptr;
// use_count(); 监控资源的强引用计数
// expired(); use_count == 0,测试所有权是否已过期,即是否已删除引用的对象
// reset() 弱引用计数-1,不影响强引用
shared_ptr<Test> sp1(new Test(100));
weak_ptr<Test> wp1(sp1); // 强引用计数不变,弱引用计数+1, 弱引用计数并不影响堆对象生命周期
cout << "sp1.use_count(): " << sp1.use_count() << endl; // 1
cout << "wp1.use_count(): " << wp1.use_count() << endl; // 1
shared_ptr<Test> sp2 = sp1; // 强引用计数+1
cout << "sp1.use_count(): " << sp1.use_count() << endl; // 2
cout << "wp1.use_count(): " << wp1.use_count() << endl; // 2
weak_ptr<Test> wp2 = wp1; // 弱引用计数并不影响堆对象生命周期
wp2.reset();
wp1.reset();
cout << "sp1.use_count(): " << sp1.use_count() << endl; // 2
cout << "wp2.use_count(): " << wp2.use_count() << endl; // 0
cout << endl;
wp1 = sp1;
if (!wp1.expired())
{
auto sp3 = wp1.lock(); // 强引用计数+1
if (sp3 != nullptr){
cout << "wp1.use_count(): " << wp1.use_count() << endl;
}
}
else
{
cout << "use_count == 0,指针指向的对象已经不存在了" << endl;
}
cout << endl << endl;
weak_ptr<Test> wp4;
{
auto sp4 = make_shared<Test>(200);
wp4 = sp4;
cout << "wp4.use_count()" << wp4.use_count() << endl;
} // 执行到这里,sp4指向的对象销毁,wp4过期
if (!wp4.expired()) {
}
else {
cout << "sp4指向的对象已经销毁"<< endl;
}
}
// 循环引用
class Node
{
int value;
public:
Node(int val = 0) {
value = val;
}
shared_ptr<Node> prev;
shared_ptr<Node> next;
~Node() {
cout << "~Node() : " << value << endl;;
}
};
// 循环引用解决方案
class NodeOK
{
int value;
public:
NodeOK(int val = 0) {
value = val;
}
weak_ptr<NodeOK> prev;
shared_ptr<NodeOK> next;
~NodeOK() {
cout << "~NodeOK() : " << value << endl;;
}
};
void test_circle()
{
{
shared_ptr<Node> sp1(new Node(100));
shared_ptr<Node> sp2(new Node(200));
cout << sp1.use_count() << endl; // Node(100)引用计数为 1
cout << sp2.use_count() << endl; // Node(200)引用计数为 1
sp1->next = sp2; // Node(200)引用计数为 2
sp2->prev = sp1; // Node(100)引用计数为 2
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
// sp2局部变量析构,sp1局部变量析构,Node(200)引和Node(100)引用计数都变为1
// 最终结果:Node(100) 和 Node(200)都没有被析构
}
cout << endl;
{
shared_ptr<NodeOK> sp1(new NodeOK(100));
shared_ptr<NodeOK> sp2(new NodeOK(200));
cout << sp1.use_count() << endl; // NodeOK(100)引用计数为 1
cout << sp2.use_count() << endl; // NodeOK(200)引用计数为 1
sp1->next = sp2; // NodeOK(200)引用计数为 2
sp2->prev = sp1; // NodeOK(100)引用计数为 1(弱引用不会改变引用计数)
cout << sp1.use_count() << endl; // 1
cout << sp2.use_count() << endl; // 2
// sp2局部变量析构,NodeOK(200)引用计数变为1
// sp1局部变量析构,NodeOK(100)引用计数变为0, NodeOK(200)引用计数变为0
}
}
int main()
{
test_shared_ptr();
cout << "---------------------------------------" <<endl;
test_weak_ptr();
cout << "---------------------------------------" << endl;
test_circle();
return 0;
}
unique_ptr
—个指针对象指向—片内存空间,同一时间只能有一个指针可以指向,不能拷贝构造和赋值, 但支持移动语句
unique_ptr作为函数参数或返回值不是拷贝构造而是发生了移动语义
auto_ptr直接调用拷贝构造实现资源转移,不易察觉,易出错。
而unique_ptr禁用了拷贝构造使用std::move()或移动构造实现实现资源转移,直观明了
class unique_ptr {
public:
unique_ptr();
unique_ptr(nullptr_t Nptr);
explicit unique_ptr(pointer Ptr);
unique_ptr(pointer Ptr,
typename conditional<is_reference<Del>::value, Del,
typename add_reference<const Del>::type>::type Deleter);
unique_ptr(pointer Ptr,
typename remove_reference<Del>::type&& Deleter);
unique_ptr(unique_ptr&& Right);
template <class T2, Class Del2>
unique_ptr(unique_ptr<T2, Del2>&& Right);
unique_ptr(const unique_ptr& Right) = delete; // 禁用拷贝构造和复制操作
unique_ptr& operator=(const unique_ptr& Right) = delete;
};
//Specialization for arrays:
template <class T, class D>
class unique_ptr<T[], D> {
public:
typedef pointer;
typedef T element_type;
typedef D deleter_type;
constexpr unique_ptr() noexcept;
template <class U>
explicit unique_ptr(U p) noexcept;
template <class U>
unique_ptr(U p, see below d) noexcept;
template <class U>
unique_ptr(U p, see below d) noexcept;
unique_ptr(unique_ptr&& u) noexcept;
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } template <class U, class E>
unique_ptr(unique_ptr<U, E>&& u) noexcept;
~unique_ptr();
unique_ptr& operator=(unique_ptr&& u) noexcept;
template <class U, class E>
unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
unique_ptr& operator=(nullptr_t) noexcept;
T& operator[](size_t i) const;
pointer get() const noexcept;
deleter_type& get_deleter() noexcept;
const deleter_type& get_deleter() const noexcept;
explicit operator bool() const noexcept;
pointer release() noexcept;
void reset(pointer p = pointer()) noexcept;
void reset(nullptr_t = nullptr) noexcept;
template <class U>
void reset(U p) noexcept = delete;
void swap(unique_ptr& u) noexcept; // disable copy from lvalue unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
};
#include <iostream>
#include <memory>
using namespace std;
class Test
{
int m_i;
public:
Test(int i = 0){
m_i = i;
}
int getI() {
return m_i;
}
~Test() {
cout << "~Test()" << " " << m_i << endl;
}
};
void test_unique_ptr()
{
unique_ptr<int> up1 = make_unique<int>(100);// C++14才有make_unique
unique_ptr<int> up2(new int(200)); // explict
// p1 = p2; // error
unique_ptr<int> up3(up1.release()); // 放弃指针控制权,返回裸指针,同时将该智能指针置空
cout << (up1 == nullptr) << endl; // 1
up3.reset(); // 释放智能指针指向的堆对象,并置空
// 使用移动语义比auto_ptr更能体现资源的移交
unique_ptr<int> up4 = std::move(up2);
cout << up2.get() << " " << up4.get() << endl;
// unique_ptr可以指向堆数组
unique_ptr<Test[]> up5(new Test[2]);
}
int main()
{
test_unique_ptr();
return 0;
}
慎用裸指针
#include <iostream>
#include <memory>
using namespace std;
class Test
{
int m_i;
public:
Test(int i = 0) {
m_i = i;
}
shared_ptr<Test> getSelf()
{
return shared_ptr<Test>(this);
}
~Test() {
cout << "~Test()" << " " << m_i << endl;
}
};
void test_unique_ptr()
{
unique_ptr<int> up1 = make_unique<int>(100);// C++14才有make_unique
unique_ptr<int> up2(new int(200)); // explict
// p1 = p2; // error
unique_ptr<int> up3(up1.release()); // 放弃指针控制权,返回裸指针,同时将该智能指针置空
cout << (up1 == nullptr) << endl; // 1
up3.reset(); // 释放智能指针指向的堆对象,并置空
// 使用移动语义比auto_ptr更能体现资源的移交
unique_ptr<int> up4 = std::move(up2);
cout << up2.get() << " " << up4.get() << endl;
// unique_ptr可以指向堆数组
unique_ptr<Test[]> up5(new Test[2]);
}
void func(shared_ptr<int> ptr) {}
void test1()
{
int* p = new int(10);
//func(shared_ptr<int>(p)); // 函数形参销毁,引用计数由1变为0,对象被销毁
//*p = 1; // 发生错误
// 安全的使用方式
shared_ptr<int> p2(p); // 内存管理交给p2
func(p2);
*p2 = 445;
}
void test2()
{
int* p = new int(10);
// 不要用裸指针初始化多个shared_ptr
shared_ptr<int> p1(p);
//shared_ptr<int> p2(p);// 引用计数都为1,内存会释放两次
shared_ptr<int> p2(p1); // 正确安全的使用方式
}
void test3()
{
shared_ptr<int> p1(new int(10));
{
shared_ptr<int> p2(p1.get());
// 执行完这里,p1.get()被释放
}
*p1 = 1; // error
}
void test4()
{
shared_ptr<Test> sp1(new Test(100));
shared_ptr<Test> sp2 = sp1->getSelf();
// 上面出错代码等价于下面
Test* p = new Test(200);
shared_ptr<Test> p1(p);
shared_ptr<Test> p2(p);
shared_ptr<Test> p3(p2);
// 如何解决?
// 可以这样 class Test : public enable_shared_from_this<Test>
// 然后这样 shared_ptr<Test> getself() { return shared_from_this();/*内部调用了弱指针的lock()*/; }
}
int main()
{
return 0;
}
Qt中的智能指针
QPointer :当其指向的对象被销毁时,它会被自动置空 ,析构时不会自动销毁所指向的对象
QSharedPointer :引用计数型智能指针 ,可以被自由地拷贝和赋值 ,当引用计数为0时才删除指向的对象
#include <QPointer>
#include <QSharedPointer>
#include <QDebug>
class Test : public QObject
{
QString m_name;
public:
Test(const char* name)
{
qDebug() << "Hello, " << name << ".";
m_name = name;
}
void print()
{
qDebug() << "I'm " << m_name << ".";
}
~Test()
{
qDebug() << "Goodbye, " << m_name << ".";
}
};
int main()
{
QPointer<Test> pt(new Test("Nyist"));
QPointer<Test> pt1(pt);
QPointer<Test> pt2(pt);
pt->print();
pt1->print();
pt2->print();
delete pt; //不会自动调用析构函数
qDebug() << "pt = " << pt;
qDebug() << "pt1 = " << pt1;
qDebug() << "pt2 = " << pt2;
qDebug() << endl;
QSharedPointer<Test> spt(new Test("Nysit")); //引用计数+1
QSharedPointer<Test> spt1(spt);
QSharedPointer<Test> spt2(spt); //此时引用计数为3
spt->print();
spt1->print();
spt2->print();
//—1 ,-1,-1
return 0; //销毁后引用计数 = 0
}
Qt中的其它智能指针 :QWeakPointer 、QScopedPointer 、QScopedArrayPointer 、QSharedDataPointer 、QExplicitlySharedDataPointer