管理指针成员
在包含指针的类中需要注意复制控制,复制指针时只复制指针中的地址,不会复制指针指向的对象。
大多数c++类采用三种方法管理指针成员:1)指针成员采用常规指针型行为。
2)采用智能指针
3)采取值型行为
常规指针缺陷:可能会出现悬垂指针。当一个指针复制到另一个指针,两个指针指向同一个对象,当一个指针删除对象时,另一个指针不知道,所以出现悬垂指针。即使使用默认合成复制构造函数也会出现,类本身无法避免。
智能指针:加入了引用计数。引用计数跟踪该类有多少对象共享同一指针。当引用计数为0 时,删除对象。创建新类时,初始化指针并将引用计数置为1.进行复制时,增加相应引用计数值。赋值时,减少左操作数所指对象的引用计数的值(减至0,就删除对象),增加右操作数所指对象的引用计数的值。最后,调用析构函数时,减少引用计数的值。如果减至0,就删除对象。
值型类:复制时会new一个新的副本,指针所指向的对象是唯一的,每个类对象独立管理。
为了管理具有指针成员的类,必须定义三个复制控制成员:复制构造函数,赋值操作符和析构函数。这些成员可以定义指针成员的指针型行为或者值型行为。
c++出现内存问题的地方一般:1)缓冲区溢出
2)悬垂指针/野指针
3)重复释放
4)内存泄漏
5)不配对的 new[]/delete
都可以通过智能指针很好的解决这些问题,比如:
1)->用vector/string或者自己写的buffer类管理,自动增加缓冲区大小,用成员函数操作,不直接通过野指针操作
2),3),4)->可以通过智能指针解决,只在对象析构的时候释放一次内存,引用计数为0的时候才删除指针,自动释放
5)->使用vector,自己释放内存
智能指针模版类
template <class T >
class RefCountedPointer
{
public:
typedef RefCountedPointer<T> ThisType;
RefCountedPointer() :counted_(0),refs_(0)
{
}
explicit RefCountedPointer(T* c) :counted_(c),refs_(0) //禁止隐式转化
{
std::auto_ptr<T> exception_guard(counted_); //异常抛出后可以自动释放,异常安全
if (counted_) {
refs_ = new size_t(1);
}
exception_guard.release();
}
RefCountedPointer(const ThisType& other) :counted_(other.counted_),refs_(other.counted_ ? other.refs_ : 0)
{
if (counted_) {
++(*refs_);
}
}
~RefCountedPointer()
{
if (refs_ && (--(*refs_) == 0)) {
delete counted_;
delete refs_;
}
}
ThisType& assign(T* c)
{
ThisType(c).swap(*this);//创建临时对象,退出作用域后自动调用析构函数,异常安全的
return *this;
}
ThisType& assign(const ThisType& other)
{
ThisType(other).swap(*this);
return *this;
}
ThisType& operator =(T* c)
{
return assign(c);
}
ThisType& operator =(const ThisType& rhs)
{
return assign(rhs);
}
T* operator ->() const
{
return counted_;
}
T& operator *() const
{
return *counted_;
}
T* get()
{
return counted_;
}
const T* get() const
{
return counted_;
}
void swap(ThisType& other)//异常安全,不抛出异常
{
std::swap(counted_, other.counted_);
std::swap(refs_, other.refs_);
}
private:
T* counted_;
size_t* refs_;//增强程序的可移植性
};
PS:参考资料《c++ primer》
在写一个操作类的时候,开始定义为了常规指针,后来优化重构的时候,看了一些不错的源代码,采用了智能指针,原来一直只有一个概念,使用后比较熟悉和了解了,这也是一个学习的过程。