智能指针模板的前言
之前的文章中我们已经介绍过了智能指针,智能指针在本质上是一个对象
这个对象可以像原生的指针一样来使用,智能对象的类通过重载的技术,将
指针相关的操作符进行了重载,优化后的智能指针类模板可以用来定义任意
类型的指针,性能大大提升了
智能指针的意义
现代C++开发库中最重要的类模板之一
C++中自动内存管理的主要手段
能够在很大程序上避开内存相关的问题
STL中的智能指针auto_ptr
生命周期结束时,销毁指向的内存空间
不能指向堆数组,只能指向堆对象(变量)
一片堆空间只属于一个智能指针对象
多个智能指针对象不能指向同一片堆空间
例子:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Test
{
string m_name;
public:
Test(string name)
{
cout<<"Hello,"<<name<<endl;
m_name = name;
}
void printf()
{
cout<<"I'm "<<m_name<<endl;
}
~Test()
{
cout<<"Goodbye,"<<m_name<<endl;
}
};
int main()
{
auto_ptr<Test>pt(new Test("linux"));
pt->printf();
return 0;
}
结果:
sice@sice:~$ ./a.out
Hello,linux
I'm linux
Goodbye,linux
这就是智能指针的作用,当程序退出时自动调用析构函数释放指针所指向的内存
不用我们手工去释放,
修改代码:
int main()
{
auto_ptr<Test>pt(new Test("linux"));
cout<<"pt ="<<pt.get()<<endl;
pt->printf();
cout<<endl;
auto_ptr<Test>pt1(pt);
cout<<"pt ="<<pt.get()<<endl;
cout<<"pt1 ="<<pt1.get()<<endl;
pt1->printf();
return 0;
}
结果:
Hello,linux
pt =0x8cbd020
I'm linux
pt =0
pt1 =0x8cbd020
I'm linux
Goodbye,linux
可以说明pt1确实指向堆空间里面申请的对象,程序结束的时候
pt,pt1的生命周期也就是结束了,这时候就应该释放堆空间里面
的对象,但是这里的析构函数只调用了一次,这是因为pt指向的
堆空间的所有权交给了pt1,pt1在结束时释放,pt指向0地址,也
验证了多个智能指针对象不能指向同一片堆空间 ,这个特性
用来避免重复释放的问题
QT中的智能指针
-QPointer
当其指向的对象被销毁时,它会被自动置空
析构时不会自动销毁所指向的对象
-QSharedPointer
引用计数型智能指针
可以被自由地拷贝和赋值
当引用计数为0时才删除指向的对象
例子:
#include <QPointer>
#include <QSharedPointer>
#include <QDebug>
class Test : public QObject
{
QString m_name;
public:
Test(const char* name)
{
qDebug() << "Hello, " << name << ".";
m_name = name;
}
void print()
{
qDebug() << "I'm " << m_name << ".";
}
~Test()
{
qDebug() << "Goodbye, " << m_name << ".";
}
};
int main()
{
QPointer<Test> pt(new Test("D.T.Software"));
QPointer<Test> pt1(pt);
QPointer<Test> pt2(pt);
pt->print();
pt1->print();
pt2->print();
delete pt;
qDebug() << "pt = " << pt;
qDebug() << "pt1 = " << pt1;
qDebug() << "pt2 = " << pt2;
qDebug() << endl;
QSharedPointer<Test> spt(new Test("Delphi Tang"));
QSharedPointer<Test> spt1(spt);
QSharedPointer<Test> spt2(spt);
spt->print();
spt1->print();
spt2->print();
return 0;
}
结果:
Hello,linux
I'm linux
I'm linux
I'm linux
Goodbye,"linux"
pt = 0
pt1 = 0
pt2 = 0
Hello,linux
I'm linux
I'm linux
I'm linux
Goodbye,"linux"
对于QPointer的分析:(如果我们不加delete pt这个语句,我们没有看到析构函数里面打印的语句,这意味pt这个指针对象在生命周期结束没有去销毁所指向的堆空间里面的对象,上述例子使用了,会打印出析构函数的内容),现在我们可以看到pt,pt1,pt2都为0,这意味QPionter指向的堆空间被释放了,所有指针这个堆空间的指针都被置空,这个可以避免内存多次释放的问题
对于QSharedPointer的分析: 这是QSharedPointer自身的特点,当它指向一个新的对象的时候,这个对象的引用计数就会加1,当结束时spt会检查指向的对象的引用计数是否为0,引用计数为0时删除指向的对象,就会调用对象的析构函数(可以看出上面只调用一次析构函数),可以避免释放多次指针
创建一个智能类指针模板
pointer.h
#ifndef _POINTER_H_
#define _POINTER_H_
using namespace std;
template <typename T>
class Pointer
{
T *mp;
public:
Pointer(T *obj = NULL)
{
mp = obj;
}
Pointer(const Pointer<T> &obj)
{
mp = obj.mp;
const_cast<Pointer<T>&>(obj).mp = NULL;
}
Pointer<T> &operator = (const Pointer<T> &obj)
{
if(this!=obj)
{
delete mp;
mp = obj.mp;
const_cast<Pointer<T>&>(obj).mp = NULL;
}
}
T* operator ->()
{
return mp;
}
T& operator *()
{
return *mp;
}
bool isNull()
{
return (mp == NULL);
}
T* get()
{
return mp;
}
~Pointer()
{
delete mp;
}
};
#endif
main.cpp
#include <iostream>
#include <string>
#include "pointer.h"
using namespace std;
class Test
{
string m_name;
public:
Test(const char* name)
{
cout << "Hello, " << name << "." << endl;
m_name = name;
}
void print()
{
cout << "I'm " << m_name << "." << endl;
}
~Test()
{
cout << "Goodbye, " << m_name << "." << endl;
}
};
int main()
{
Pointer<Test> pt(new Test("linux"));
cout << "pt = " << pt.get() << endl;
pt->print();
cout << endl;
Pointer<Test> pt1(pt);
cout << "pt = " << pt.get() << endl;
cout << "pt1 = " << pt1.get() << endl;
pt1->print();
return 0;
}
结果:
Hello, linux.
pt = 0x96d5008
I'm linux.
pt = 0
pt1 = 0x96d5008
I'm linux.
Goodbye, linux.
可以看出我们的智能指针模板避免了内存泄漏,也避免了多次释放,好了,很困了,晚安