一、C++标准库的智能指针
- 标准库提供了3种智能指针:
- 各种语法本文就不介绍了,查看上面的链接
二、智能指针如何实现
- 本文主要介绍shared_ptr是如何实现的,我们知道在代码中可以让多份实例指向于同一个shared_ptr对象
- 智能指针主要是靠一个“引用计数”来实现的:
- 每次创建类的新对象时,初始化指针并将引用计数置为1
- 当对象作为另一个对象的副本而创建时,拷贝构造函数拷贝指针并增加与之对应的引用计数
- 当一个对象进行赋值时,赋值操作符减少左操作符所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作符所指对象的引用计数
- 调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象)
- 重载"->"以及"*"操作符,使得智能指针有类似于普通指针的操作
三、手写一个智能指针
// SmartPtr.h
#ifndef __SMARTPTR_H_
#define __SMARTPTR_H_
#include <iostream>
using namespace std;
template<typename T>
class SmartPtr
{
public:
SmartPtr(T *p = 0); // 构造函数
SmartPtr(SmartPtr<T>& other); // 拷贝构造函数
~SmartPtr(); // 析构函数
SmartPtr& operator=(SmartPtr<T>& rhs); // 赋值运算符
T& operator*();
T* operator->();
private:
// 将对象的引用计数减1
void decrRef()
{
// 如果引用计数减1之后变为0, 则释放该智能指针的底层数据, 此智能指针不再继续使用
if(--*_mpRef == 0)
{
delete _mPtr;
delete _mpRef;
}
}
private:
T *_mPtr; // 保存数据对象指针
size_t *_mpRef; // 当前数据对象的引用计数
};
template<typename T>
SmartPtr<T>::SmartPtr(T *p)
{
// 与p指向同一块内存, 并将引用计数初始化为1
_mPtr = p;
_mpRef = new size_t(1);
}
template<typename T>
SmartPtr<T>::SmartPtr(SmartPtr<T>& other)
{
_mPtr = other._mPtr;
other._mpRef++;
_mpRef = other._mpRef;
}
template<typename T>
SmartPtr<T>::~SmartPtr()
{
decrRef();
}
template<typename T>
SmartPtr<T>& SmartPtr<T>::operator=(SmartPtr<T>& rhs)
{
// 将自身的引用计数减1
decrRef();
// 指向于参数所指的对象, 并将参数所指对象的引用计数+1, 并指向其引用计数
_mPtr = rhs._mPtr;
rhs._mpRef++;
_mpRef = rhs._mpRef;
return *this;
}
template<typename T>
T& SmartPtr<T>::operator*()
{
if(_mPtr)
return *_mPtr;
throw std::runtime_error("dereference of NULL pointer");
}
template<typename T>
T* SmartPtr<T>::operator->()
{
if(_mPtr)
return _mPtr;
throw std::runtime_error("access rgeough NULL pointer");
}
#endif // __SMARTPTR_H_
// main.cpp
#include "SmartPtr.h"
class test
{
public:
test(char *_name = ""): name(_name) {}
void showName() { cout << name << endl; }
private:
string name;
};
int main()
{
SmartPtr<test> mp1;
SmartPtr<test> mp2(new test("C++"));
SmartPtr<test> mp3(mp2);
mp2->showName();
mp3->showName();
std::cout << std::endl;
SmartPtr<test> mp4(new test("Linux"));
mp3 = mp4;
mp3->showName();
mp4->showName();
std::cout << std::endl;
*mp3 = "Golang";
mp3->showName();
return 0;
}