目录
1. 引言
C++中,由于没有垃圾回收机制,当程序员申请了某些资源(譬如new 某个对象A),如果程序员自己负责释放这些资源,则很可能会产生内存泄露,因此现代C++11引进了智能指针来尽量降低资源泄露的可能。本博客便是我自己在学习more effective C++ 的过程,记录自制简易版智能指针的过程。方便以后复习。
2. 智能指针的定义以及粗略实现
2.1 智能指针的定义
智能指针是看起来、用起来像C++内建指针的对象
2.2 智能指针SmartPtr的粗略实现
智能指针可以像内建指针一样,指向各个类型,因此需要考虑使用C++11 模板实现,其第一版粗略实现如下:
#ifndef CPLUS_SMARTPTR_H
#define CPLUS_SMARTPTR_H
template <typename T>
class SmartPtr {
public:
SmartPtr(const SmartPtr& rhs);
SmartPtr(T* pointer = 0);
~SmartPtr() {
delete _ptr;
};
SmartPtr& operator=(const SmartPtr& rhs);
T* operator->() const;
T& operator*() const;
private:
T* _ptr;
};
template <typename T>
SmartPtr<T>::SmartPtr(T *pointer) : _ptr(pointer) {
}
template <typename T>
SmartPtr<T>::SmartPtr(const SmartPtr &rhs) : _ptr(rhs._ptr) {
}
template <typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr &rhs) {
if (this == &rhs) return *this;
_ptr = rhs._ptr;
return *this;
}
template <typename T>
T * SmartPtr<T>::operator->() const {
return _ptr;
}
template <typename T>
T & SmartPtr<T>::operator*() const {
return *_ptr;
}
#endif //CPLUS_SMARTPTR_H
利用上述SmartPtr,测试程序如下
#include <iostream>
#include "smartptr.h"
class TestClass {
public:
void func() {
std::cout << "TesstClass" << std::endl;
}
};
int main() {
SmartPtr<TestClass> smartPtr(new TestClass);
smartPtr->func();
return EXIT_SUCCESS;
}
3. SmartPtr的优化
3.1 分析SmartPtr的构造函数、析构函数以及赋值
如下述代码,当调用SmartPtr拷贝构造函数, 会导致_ptr被删除两次,也即double free 问题。因此上述拷贝构造函数实现有问题,需要优化。
SmartPtr<TestClass> smartPtr(new TestClass);
auto sp = smartPtr;
sp->func();
有三种优化方案:禁止拷贝 或者 引用计数 或者 转移所有权(本文选择转移所有权)
拷贝构造函数优化后如下:
SmartPtr(SmartPtr& rhs);
template <typename T>
SmartPtr<T>::SmartPtr(SmartPtr &rhs) : _ptr(rhs._ptr) {
rhs._ptr = 0;
}
赋值拷贝函数优化如下:
SmartPtr& operator=(SmartPtr& rhs);
template <typename T>
SmartPtr<T>& SmartPtr<T>::operator=(SmartPtr &rhs) {
if (this == &rhs) return *this;
delete _ptr;
_ptr = rhs._ptr;
rhs._ptr = 0;
return *this;
}
3.2 测试指针是否为空
如下代码,测试smartPtr为空失败,这是因为SmartPtr是一个模板,其smartPtr是一个对象,该对象并没有相应的方法与0比较。
那么如何使SmartPtr可以看起来像内建指针一样判断为空呢?
其解决方案为,提供一个隐式类型转换操作符,增加的隐式转换操作符如下
operator void*() {
return _ptr;
}
3.3 使SmartPtr转换为T*指针
有时候为了兼容旧的接口,需要传递原生指针至兼容接口,那么此时便需要将SmartPtr转换成T*,那么如何获得SmartPtr中的T*呢?
有两种方案:提供隐式类型转换操作符 或者 提供一个get方法获得T*
提供隐式转换操作符的实现如下:
operator T*() {
return _ptr;
}
提供get 方法获得T*实现如下:
T* get() {
return _ptr;
}
4. 实现SmartPtr之间的转换
测试代码如下:
函数func_test(SmartPtr<TestClass>& test) 的形参为 SmartPtr<TestClass> 而当传入smartPtrA 或者 smartPtrB时,编译会报错。
原因是smartPtrA 或者 smartPtrB或者smartPtr是不同的对象,不存在相应的对象转换,因此若要使继承关系的smartPtrA 或者 smartPtrB或者smartPtr能相互转换,则需要自定义转换函数。
#include <iostream>
#include "smartptr.h"
class TestClass {
public:
virtual void func() {
std::cout << typeid(*this).name() << std::endl;
}
virtual ~TestClass() {}
};
class TestA : public TestClass {
public:
virtual void func() {
std::cout << typeid(*this).name() << std::endl;
}
};
class TestB : public TestClass {
virtual void func() {
std::cout << typeid(*this).name() << std::endl;
}
};
void func_test(SmartPtr<TestClass>& test) {
test->func();
}
int main() {
SmartPtr<TestClass> smartPtr(new TestClass);
SmartPtr<TestA> smartPtrA(new TestA);
SmartPtr<TestB> smartPtrB(new TestB);
func_test(smartPtr);
func_test(smartPtrA);
func_test(smartPtrB);
return EXIT_SUCCESS;
}
对于C++11来说,可以定义模板转换操作符,其实现如下:
template <typename newType>
operator SmartPtr<newType>() {
return SmartPtr<newType>(_ptr);
}
5. 优化后智能指针实现
#ifndef CPLUS_SMARTPTR_H
#define CPLUS_SMARTPTR_H
template <typename T>
class SmartPtr {
public:
SmartPtr(const SmartPtr& rhs);
SmartPtr(SmartPtr& rhs);
SmartPtr(T* pointer = 0);
~SmartPtr() {
delete _ptr;
};
SmartPtr& operator=(const SmartPtr& rhs);
SmartPtr&operator=(SmartPtr& rhs);
template <typename newType>
operator SmartPtr<newType>() {
return SmartPtr<newType>(_ptr);
}
operator bool() {
return _ptr != nullptr;
}
/*
operator T*() {
return _ptr;
}*/
T* get() {
return _ptr;
}
T* operator->() const;
T& operator*() const;
private:
T* _ptr;
};
template <typename T>
SmartPtr<T>::SmartPtr(T *pointer) : _ptr(pointer) {
}
template <typename T>
SmartPtr<T> & SmartPtr<T>::operator=(SmartPtr<T> &rhs) {
if (this == &rhs) return *this;
delete _ptr;
_ptr = rhs._ptr;
rhs._ptr = 0;
return *this;
}
template <typename T>
SmartPtr<T>::SmartPtr(SmartPtr& rhs) : _ptr(rhs._ptr) {
rhs._ptr = 0;
}
template <typename T>
SmartPtr<T>::SmartPtr(const SmartPtr &rhs) : _ptr(rhs._ptr) {
}
template <typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr &rhs) {
if (this == &rhs) return *this;
delete _ptr;
_ptr = rhs._ptr;
return *this;
}
template <typename T>
T * SmartPtr<T>::operator->() const {
return _ptr;
}
template <typename T>
T & SmartPtr<T>::operator*() const {
return *_ptr;
}
#endif //CPLUS_SMARTPTR_H
注意:该智能指针,在不同指针类型之间转换会导致double free,正在研究解决方案