前言
C++的智能指针类是为了解决动态创建的对象自动释放的问题。通常与运算符重载和引用计数配合使用。
0x00 CAString类
首先我们自己实现了一个简单的字符串类CAString 类:
class CAString
{
public:
CAString()
{
m_pBuff = new char[1];
m_pBuff[0] = '\0';
m_nBuffSize = 1;
m_nLength = 0;
}
CAString(const char* str)
{
m_nLength = strlen(str);
m_nBuffSize = m_nLength + 1;
m_pBuff = new char[m_nBuffSize];
strcpy_s(m_pBuff, m_nBuffSize, str);
}
CAString(const CAString& obj)
{
m_nLength = obj.m_nLength;
m_nBuffSize = obj.m_nBuffSize;
m_pBuff = new char[m_nBuffSize];
strcpy_s(m_pBuff, m_nBuffSize, obj.m_pBuff);
}
CAString(CAString&& obj)
{
swap(m_pBuff, obj.m_pBuff);
swap(m_nLength, obj.m_nLength);
swap(m_nBuffSize, obj.m_nBuffSize);
}
~CAString()
{
if (m_pBuff != nullptr)
{
delete m_pBuff;
m_pBuff = nullptr;
}
}
const char* GetStringBuffer()
{
return m_pBuff;
}
void SetAt(int nIndex, char ch)
{
m_pBuff[nIndex] = ch;
}
public:
friend ostream& operator<<(ostream& os, const CAString& obj)
{
os << obj.m_pBuff;
return os;
}
private:
uint32_t m_nBuffSize;
uint32_t m_nLength;
char* m_pBuff;
};
提供了构造、析构函数,重载了输出流运算符,对外部提供了获取字符串地址的接口。
0x01 智能指针1(解决动态创建的对象资源释放的问题)
利用局部对象在出作用域时自动调用析构函数的特点。将对象的指针保存,封装成另一个智能指针类。智能指针类对象在构造函数中保存对象的指针,在出作用域时析构中显式delete保存的对象的指针。
为此我们写一个简单的C++智能指针类:
class CSmartPtr
{
public:
CSmartPtr(CAString* pObj = nullptr)
{
m_pObject = pObj;
}
~CSmartPtr()
{
if (m_pObject != nullptr)
{
delete m_pObject;
m_pObject = nullptr;
}
}
private:
CAString* m_pObject;
};
现在我们使用这个智能指针类:
int main()
{
CSmartPtr pObj1(new CAString("string1"));
CSmartPtr pObj2(new CAString("string2"));
return 0;
}
这里我们使用智能指针时非常不方便,没法直接对保存的对象指针进行操作。因为我们将 CAString 类对象的指针保存为 private 权限,所以 pObj1 和 pObj2 在外部不能通过访问到保存的 CAString 类对象的指针进而访问 CAString 类对象的成员函数。
下面讨论的是如何通过 CSmartPtr 类对象直接访问到 CAString 类对象的成员函数。
最容易考虑到的方法就是将 CAString 类对象的指针设为 public 权限。但这样通过 CSmartPtr 类对象直接可以修改保存的 CAString 类对象的指针,不建议使用。
还有一种方法,就是在 CSmartPtr 智能指针类中重写所有的 CAString 类的方法,这些方法作为 CSmartPtr 的成员函数,在成员函数中,可以直接对 private 权限的 CAString 类指针进行操作。 这种方法不建议使用,首先因为工作量太大,重写所有的 CAString 类的成员函数将使 CSmartPtr 类变得臃肿;另一点就是即使这样写出的 CSmartPtr 类只能管理 CAString 类,不能泛化管理所有的类。
对另一种方法就是通过运算符重载。我们保存的是 CAString 类对象的指针,我们重载指针一些常用的运算符。
class CSmartPtr
{
public:
CSmartPtr(CAString* pObj = nullptr)
{
m_pObject = pObj;
}
~CSmartPtr()
{
if (m_pObject != nullptr)
{
delete m_pObject;
m_pObject = nullptr;
}
}
CAString* operator->()
{
return m_pObject;
}
CAString** operator&()
{
return &m_pObject;
}
CAString& operator*()
{
return *m_pObject;
}
CAString* operator+(int n)
{
return m_pObject + n;
}
private:
CAString* m_pObject;
};
重载了运算符以后,通过 CSmartPtr 类对象直接访问到 CAString 类对象的成员函数,这样使用 CSmartPtr 类对象和使用 CAString 类对象一样。
现在我们再使用这个智能指针类:
int main()
{
CSmartPtr pObj1(new CAString("string1"));
CSmartPtr pObj2(new CAString("string2"));
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
cout << &pObj1 << endl;
*pObj1;
pObj1 + 1;
return 0;
}
智能指针 + 运算符重载 使我们使用智能指针和直接使用类对象的效果一样。而且智能指针解决了类对象指针释放的问题。
0x02 智能指针1完整代码
class CSmartPtr
{
public:
CSmartPtr(CAString* pObj = nullptr)
{
m_pObject = pObj;
}
~CSmartPtr()
{
if (m_pObject != nullptr)
{
delete m_pObject;
m_pObject = nullptr;
}
}
CAString* operator->()
{
return m_pObject;
}
CAString** operator&()
{
return &m_pObject;
}
CAString& operator*()
{
return *m_pObject;
}
CAString* operator+(int n)
{
return m_pObject + n;
}
private:
CAString* m_pObject;
};
int main()
{
CSmartPtr pObj1(new CAString("string1"));
CSmartPtr pObj2(new CAString("string2"));
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
cout << &pObj1 << endl;
*pObj1;
pObj1 + 1;
return 0;
}
0x03 智能指针1存在的问题(等号赋值)
因为我们的 CSmartPtr 类没有重载赋值运算符,也就是没有实现下面这个函数:
CSmartPtr& operator= (const CSmartPtr& obj)
{
return *this;
}
这将导致下面代码中 pObj1 = pObj2; 会发生资源泄露和重释放的问题。
int main()
{
CSmartPtr pObj1(new CAString("string1"));
CSmartPtr pObj2(new CAString("string2"));
pObj1 = pObj2;
return 0;
}
没有重载等号赋值运算符,等号赋值默认就是执行 memcpy 函数。此时 pObj1 和 pObj2 中保存的对象的指针将指向相同的对象。因此在出作用析构是,pObj2 中保存的对象的指针被 delete 了两次,而 pObj1 中保存的对象的指针丢失了,又发生了资源泄露。
0x04 智能指针2(带引用计数的智能指针)
为了解决资源重复释放的问题,我们很自然的想到了使用引用计数(参考:C++引用计数)。
新增一个引用计数指针作为 CSmartPtr 类的成员变量:
uint32_t* m_pRefCount;
提供对引用计数递增和递减的方法:
void AddRefCount()
{
++(*m_pRefCount);
}
void SubRefCount()
{
if (m_pObject != nullptr && m_pRefCount != nullptr)
{
--(*m_pRefCount);
if (*m_pRefCount == 0)
{
delete m_pRefCount;
m_pRefCount = nullptr;
delete m_pObject;
m_pObject = nullptr;
}
}
}
在构造函数中,初始化引用计数指针,析构函数中递减引用计数。在递减引用计数的函数中,判断如果引用计数为0才会释放保存的对象和引用计数占用的控件。也就是说引用计数和具体的类对象绑定。
构造和析构函数代码:
CSmartPtr()
{
m_pRefCount = nullptr;
m_pObject = nullptr;
}
CSmartPtr(CAString* pObj)
{
m_pRefCount = new uint32_t(0);
m_pObject = pObj;
AddRefCount();
}
CSmartPtr& operator=(const CSmartPtr& obj)
{
SubRefCount();
m_pObject = obj.m_pObject;
m_pRefCount = obj.m_pRefCount;
AddRefCount();
return *this;
}
~CSmartPtr()
{
SubRefCount();
}
0x05 智能指针2完整代码
class CSmartPtr
{
public:
CSmartPtr()
{
m_pRefCount = nullptr;
m_pObject = nullptr;
}
CSmartPtr(CAString* pObj)
{
m_pRefCount = new uint32_t(0);
m_pObject = pObj;
AddRefCount();
}
CSmartPtr& operator=(const CSmartPtr& obj)
{
SubRefCount();
m_pObject = obj.m_pObject;
m_pRefCount = obj.m_pRefCount;
AddRefCount();
return *this;
}
~CSmartPtr()
{
SubRefCount();
}
private:
void AddRefCount()
{
++(*m_pRefCount);
}
void SubRefCount()
{
if (m_pObject != nullptr && m_pRefCount != nullptr)
{
--(*m_pRefCount);
if (*m_pRefCount == 0)
{
delete m_pRefCount;
m_pRefCount = nullptr;
delete m_pObject;
m_pObject = nullptr;
}
}
}
public:
CAString* operator->()
{
return m_pObject;
}
CAString** operator&()
{
return &m_pObject;
}
CAString& operator*()
{
return *m_pObject;
}
CAString* operator+(int n)
{
return m_pObject + n;
}
private:
CAString* m_pObject;
uint32_t* m_pRefCount;
};
int main()
{
CSmartPtr pObj1(new CAString("string1"));
CSmartPtr pObj2(new CAString("string2"));
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
pObj1 = pObj2;
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
return 0;
}
0x06 模拟auto_ptr
将智能指针1封装成模板就模拟实现了 auto_ptr
template <class TYPE>
class CSmartPtr
{
public:
CSmartPtr(TYPE* pObj = nullptr)
{
m_pObject = pObj;
}
~CSmartPtr()
{
if (m_pObject != nullptr)
{
delete m_pObject;
m_pObject = nullptr;
}
}
TYPE* operator->()
{
return m_pObject;
}
TYPE** operator&()
{
return &m_pObject;
}
TYPE& operator*()
{
return *m_pObject;
}
TYPE* operator+(int n)
{
return m_pObject + n;
}
private:
TYPE* m_pObject;
};
使用:
int main()
{
CSmartPtr<CAString> pObj1(new CAString("string1"));
CSmartPtr<CAString> pObj2(new CAString("string2"));
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
return 0;
}
0x07 模拟share_ptr
将智能指针2封装成模板就模拟实现了 share_ptr
template <class TYPE>
class CSmartPtr
{
public:
CSmartPtr()
{
m_pRefCount = nullptr;
m_pObject = nullptr;
}
CSmartPtr(TYPE* pObj)
{
m_pRefCount = new uint32_t(0);
m_pObject = pObj;
AddRefCount();
}
CSmartPtr& operator=(const CSmartPtr& obj)
{
SubRefCount();
m_pObject = obj.m_pObject;
m_pRefCount = obj.m_pRefCount;
AddRefCount();
return *this;
}
~CSmartPtr()
{
SubRefCount();
}
private:
void AddRefCount()
{
++(*m_pRefCount);
}
void SubRefCount()
{
if (m_pObject != nullptr && m_pRefCount != nullptr)
{
--(*m_pRefCount);
if (*m_pRefCount == 0)
{
delete m_pRefCount;
m_pRefCount = nullptr;
delete m_pObject;
m_pObject = nullptr;
}
}
}
public:
TYPE* operator->()
{
return m_pObject;
}
TYPE** operator&()
{
return &m_pObject;
}
TYPE& operator*()
{
return *m_pObject;
}
TYPE* operator+(int n)
{
return m_pObject + n;
}
private:
TYPE* m_pObject;
uint32_t* m_pRefCount;
};
使用:
int main()
{
CSmartPtr<CAString> pObj1(new CAString("string1"));
CSmartPtr<CAString> pObj2(new CAString("string2"));
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
pObj1 = pObj2;
cout << pObj1->GetStringBuffer() << endl;
cout << *pObj2 << endl;
return 0;
}
|