ScopedPointer
QScopedPointer模版类可以指向一个动态分配内存的对象,而且可以通过调用自身的析构函数删除其所指向的对象。用C++的程序员都知道,人为的管理在堆上动态分配内存的对象是比较麻烦而且也很容易引发错误的,常见问题的就是会引发内存泄漏又难以维护。QScopedPointer作为一个小而实用的工具类,它通过基于堆栈的内存实现对于在堆上分配的内存的所有权,即所谓的资源获取即初始化((RAII),从而大量简化我们的工作。QScopedPointer能保证对象在离开当前作用域时被删除,如此便可以保证不会出现内存泄漏的情况。QScopedPointer没有复制构造函数和赋值函数。QScopedPointer所起的作用其实就跟C++ STL中的智能auto_ptr一样的。
看看下面的代码,
void myFunction(bool useSubClass)
{
MyClass *p = useSubClass ? newMyClass() : new MySubClass;
QIODevice *device =handsOverOwnership();
if (m_value > 3) {
delete p;
delete device;
return;
}
try {
process(device);
}
catch (...) {
delete p;
delete device;
throw;
}
delete p;
delete device;
}
它动态分配了一个对象,有几个退出点,为了不出现内存泄漏,我们必须在每个可能的退出点删除对象,delete p;
这样的代码看起来让人有点不舒服,如果我们用QScopedPointer,那就简单得多了
void myFunction(bool useSubClass)
{
// assuming that MyClass has avirtual destructor
QScopedPointer<MyClass>p(useSubClass ? new MyClass() : new MySubClass);
QScopedPointer<QIODevice>device(handsOverOwnership());
if (m_value > 3)
return;
process(device);
}
数组指针和由malloc分配的内存是不能用delete进行释放的,QScopedPointer
的第二个模版参数可以设定已经存在的内存释放类或者自定义的内存释放类。
以下几种是Qt已经写好的几个内存释放类:
(1) QScopedPointerDeleter,这是默认模版类,它通过delete进行释放。
(2) QScopedPointerArrayDeleter,它通过delete[] 释放由new [] 分配的数组。
(3) QScopedPointerPodDeleter,它通过free() 释放由malloc分配的内存。
也可以自定义内存释放的类,但是必须有一个静态公有的方法:void cleanup(T *pointer)
// thisQScopedPointer deletes its data using the delete[] operator:
QScopedPointer<int,QScopedPointerArrayDeleter<int> > arrayPointer(new int[42]);
// this QScopedPointer frees its data usingfree():
QScopedPointer<int,QScopedPointerPodDeleter> podPointer(reinterpret_cast<int*>(malloc(42)));
// this struct calls"myCustomDeallocator" to delete the pointer
struct ScopedPointerCustomDeleter
{
static inline voidcleanup(MyCustomClass *pointer)
{
myCustomDeallocator(pointer);
}
};
// QScopedPointer using a custom deleter:
QScopedPointer <MyCustomClass,ScopedPointerCustomDeleter> customPointer(new MyCustomClass);
应用于普通的C++指针的const 条件也一样可以用与限制QScopedPointer指针。
const QWidget *const p = new QWidget();
// is equivalent to:
const QScopedPointer<constQWidget> p(new QWidget());
QWidget *const p = newQWidget();
// isequivalent to:
constQScopedPointer<QWidget> p(new QWidget());
const QWidget *p = newQWidget();
// isequivalent to:
QScopedPointer <constQWidget> p(new QWidget());
我们知道C++还有一个关键字 volatile ,在这里把const换成volatile一样可以的,怎么样,很好用吧,auto_ptr还不能用这种限定吧。
即便是也不能是像QPointer指针和普通C++指针一样,可以相互赋值。如下的代码肯定是编译不通过的,
int *pInt = new int(5);
QScopedPointer<int> pSInt (new int(6));
pInt = pSInt;
前向声明的类一样可以用于 QScopedPointer,只要在QScopedPointer在清理内存时,前向声明类的析构函数已经定义的就可以,因为在释放内存时默认调用析构函数。这意味着:前向声明的类,不能有内联的构造函数,内联析构函数和赋值函数。
class MyPrivateClass; // forward declareMyPrivateClass
class MyClass
{
private:
QScopedPointer<MyPrivateClass> privatePtr; // QScopedPointer to forward declared class
public:
MyClass(); // OK
inline ~MyClass() {} // VIOLATION - Destructor must not be inline
private:
Q_DISABLE_COPY(MyClass) // OK - copy constructor and assignment operators
// are now disabled, so thecompiler won't implicitely
// generate them.
};
接下来我们看看部分源码:
首先看QScopedPointer定义:
template <typename T, typename Cleanup = QScopedPointerDeleter<T>>
class QScopedPointer
{
inline ~QScopedPointer()
{
public:
T *oldD = this->d;
Cleanup::cleanup(oldD);
this->d = 0;
}
。。。。
}
template <typename T>
struct QScopedPointerDeleter
{
static inline void cleanup(T*pointer)
{
// Enforce a complete type.
// If you get a compileerror here, read the secion on forward declared
// classes in theQScopedPointer documentation.
typedef charIsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete pointer;
}
};
由此可以我们就更清除:
1、 为什么在QScopedPointer在清理内存时,前向声明类的析构函数必须是已经定义的了吧。
2、QScopedPointerDeleter的cleanup为什么必须是静态的。
在此顺便看看QScopedPointerArrayDeleter和QScopedPointerPodDeleter的实现吧
template <typename T>
struct QScopedPointerArrayDeleter
{
static inline void cleanup(T*pointer)
{
// Enforce a complete type.
// If you get a compileerror here, read the secion on forward declared
// classes in theQScopedPointer documentation.
typedef charIsIncompleteType[ sizeof(T) ? 1 : -1 ];
(void) sizeof(IsIncompleteType);
delete [] pointer;
}
};
struct QScopedPointerPodDeleter
{
static inline void cleanup(void*pointer) { if (pointer) qFree(pointer); }
};
需要注意的两个方法:T *data() const 和T * take(),由方法名应该知道,take方法调用之后,指针将变为0
最后声明最重要的一点:QScopedPointer的所以方法都是可重入的。鉴于可重入的内容可能比较多,下篇再慢慢论述吧