Qt源码分析之QPointer
QPointer是一个指针封装类,其作用类似于智能指针,但是它最大的特点应该是在指针的控制上,它希望一个Qt的指针(当然是从QObject派生的)可以同时被多个类拥有,这在
界面编程中当然是很常见的事情了,但是当这个指针被删除时,我们不希望再找到那两个界面类然后通知它们,相反我们希望这两个界面类可以直接判断QPointer中的isNull方法
很自然的知道原始指针已经不存在了
1.试验代码:
#include <QApplication>
#include <QPushButton>
#include <QPointer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton* pButton = new QPushButton("wgs");
QPointer<QPushButton> button = pButton;
delete pButton;
if (!button.isNull())
{
button->setText("www");
}
return app.exec();
}
一段很短的代码,这里需要的注意的是QPointer指针的使用
template <class T>
class QPointer
{
QObject *o;
public:
inline QPointer() : o(0) {}
inline QPointer(T *p) : o(p)
{ QMetaObject::addGuard(&o); }
inline QPointer(const QPointer<T> &p) : o(p.o)
{ QMetaObject::addGuard(&o); }
inline ~QPointer()
{ QMetaObject::removeGuard(&o); }
inline QPointer<T> &operator=(const QPointer<T> &p)
{ if (this != &p) QMetaObject::changeGuard(&o, p.o); return *this; }
inline QPointer<T> &operator=(T* p)
{ if (o != p) QMetaObject::changeGuard(&o, p); return *this; }
inline bool isNull() const
{ return !o; }
inline T* operator->() const
{ return static_cast<T*>(const_cast<QObject*>(o)); }
inline T& operator*() const
{ return *static_cast<T*>(const_cast<QObject*>(o)); }
inline operator T*() const
{ return static_cast<T*>(const_cast<QObject*>(o)); }
};
QPointer只是一个简单的模板,对QMetaObject的相关操作做了简单的封装,这里的基本思想是
在QPointer构造的时候调用QMetaObject::addGuard(&o),把T的指针加入QMetaObject内的一个哈希表中,
在QPointer析构的时候调用QMetaObject::removeGuard(&o),把T的指针从哈希表中删除
这是一个QMetaObject中静态成员,该哈希表的定义如下:
typedef QMultiHash<QObject *, QObject **> GuardHash;
这个哈希表存储的是指针的值和指针的地址,因此加入的代码如下:
void QMetaObject::addGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash *hash = guardHash();
if (!hash) {
*ptr = 0;
return;
}
QWriteLocker locker(guardHashLock());
hash->insert(*ptr, ptr);
}
为什么不是只保存一个指针呢,原来是为了防止误删除,看看删除的代码:
void QMetaObject::removeGuard(QObject **ptr)
{
if (!*ptr)
return;
GuardHash *hash = guardHash();
if (!hash)
return;
QWriteLocker locker(guardHashLock());
GuardHash::iterator it = hash->find(*ptr);
const GuardHash::iterator end = hash->end();
for (; it.key() == *ptr && it != end; ++it) {
if (it.value() == ptr) {
(void) hash->erase(it);
break;
}
}
}
只有在it.value() == ptr的时候才会删除
但是等等,当删除普通指针时又如何更新这个哈希表呢?
delete pButton;如何通知QMetaObject中的哈希表更新?
答案是在QObject中,请注意QObject的析构函数
QObject::~QObject()
{
Q_D(QObject);
if (d->wasDeleted) {
#if defined(QT_DEBUG)
qWarning("Double QObject deletion detected");
#endif
return;
}
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
// set all QPointers for this object to zero
GuardHash *hash = ::guardHash();
if (hash) {
QWriteLocker locker(guardHashLock());
GuardHash::iterator it = hash->find(this);
const GuardHash::iterator end = hash->end();
while (it.key() == this && it != end) {
*it.value() = 0;
it = hash->erase(it);
}
}
emit destroyed(this);
QConnectionList *list = ::connectionList();
if (list) {
QWriteLocker locker(&list->lock);
list->remove(this);
}
if (d->pendTimer) {
// have pending timers
QThread *thr = thread();
if (thr || d->thread == 0) {
// don't unregister timers in the wrong thread
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(thr);
if (eventDispatcher)
eventDispatcher->unregisterTimers(this);
}
}
d->eventFilters.clear();
// delete children objects
if (!d->children.isEmpty()) {
qDeleteAll(d->children);
d->children.clear();
}
{
QWriteLocker locker(QObjectPrivate::readWriteLock());
::qt_removeObject(this);
/*
theoretically, we cannot check d->postedEvents without
holding the postEventList.mutex for the object's thread,
but since we hold the QObjectPrivate::readWriteLock(),
nothing can Go into QCoreApplication::postEvent(), which
effectively means noone can post new events, which is what
we are trying to prevent. this means we can safely check
d->postedEvents, since we are fairly sure it will not
change (it could, but only by decreasing, i.e. removing
posted events from a differebnt thread)
*/
if (d->postedEvents > 0)
QCoreApplication::removePostedEvents(this);
}
if (d->parent) // remove it from parent object
d->setParent_helper(0);
delete d;
d_ptr = 0;
}
这里做了很多很多的事情,其中的代码
// set all QPointers for this object to zero
GuardHash *hash = ::guardHash();
if (hash) {
QWriteLocker locker(guardHashLock());
GuardHash::iterator it = hash->find(this);
const GuardHash::iterator end = hash->end();
while (it.key() == this && it != end) {
*it.value() = 0;
it = hash->erase(it);
}
}
就是更新QMetaObject中哈希表的
这也就是单基类的好处,可以在这里控制很多事情
http://blog.csdn.net/oowgsoo/article/details/1529424
Qt源码分析之QPointer的更多相关文章
- Qt源码分析之QObject
原文:http://blog.csdn.net/oowgsoo/article/details/1529284 我感觉oowgsoo兄弟写的分析相当透彻,赞! 1.试验代码: #include < ...
- QT源码分析(从QApplication开始)
QT源码分析 转载自:http://no001.blog.51cto.com/1142339/282130 今天,在给同学讲东西的时候,谈到了Qt源代码的问题,才发现自己对Qt机制的了解是在太少了,而 ...
- QT源码分析:QTcpServer
最近在看有关IO复用方面的内容,自己也用标准c++库实现了select模型.iocp模型.poll模型.回过头来很想了解QT的socket是基于什么模型来实现的,所以看了QT关于TcpServer实现 ...
- QT源码分析:QObject
QT框架里面最大的特色就是在C++的基础上增加了元对象系统(Meta-Object System),而元对象系统里面最重要的内容就是信号与槽机制,这个机制是在C++语法的基础上实现的,使用了函数.函数 ...
- Qt源码分析之信号和槽机制
Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的说法,简单点说就是如何在一个类的一个 ...
- Qt源码分析之信号和槽机制(QMetaObject是一个内部struct)
Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的说法,简单点说就是如何在一个类的一个 ...
- QT 源码分析--1
Ref: http://blog.sina.com.cn/s/blog_6e80f1390100qoc0.html 安装qt之后(我使用的是online自动安装),安装目录下有\5.10.1\Src\ ...
- Qt之使用setWindowFlags方法遇到的问题(追踪进入QWidget的源码分析原因,最后用WINAPI解决问题)good
一.简述 前段时间在使用setWindowFlags方法时遇到了一个坑,具体情况是想通过窗口界面上一个checkBox来控制窗口当前状态是否置顶,而Qt提供了Qt::WindowStaysOnTopH ...
- Qt update刷新之源码分析(一)
在做GUI开发时,要让控件刷新,会调用update函数:那么在调用了update函数后,Qt究竟基于什么原理.执行了什么代码使得屏幕上有变化?本文就带大家来探究探究其内部源码. Qt手册中关于QWid ...
随机推荐
- 【转】数据库SQL优化大总结之 百万级数据库优化方案
原帖地址:http://www.cnblogs.com/yunfeifei/p/3850440.html#undefined 1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 or ...
- php中实现精确设置session过期时间的方法
http://www.jb51.net/article/52309.htm 大多数据情况下我们对于session过期时间使用的是默认设置的时间,而对于一些有特殊要求的情况下我们可以设置一下sessio ...
- Solr 1.3 安装步骤
可以通过以下三种方式之一设置 Solr 的主位置: 1.设置 java 系统属性 solr.solr.home (没错,就是 solr.solr.home). 2.配置 ...
- spring mvc源码解析
1.从DispatcherServlet开始 与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet 为中心来 ...
- Object-C Dealloc
上一篇为Object-C Init dealloc方法在一个对象从内存中删除时被调用.通常在这个方法里面释放所有对象里的实例变量 -(void)dealloc { [super dealloc]; [ ...
- C#中运用事件实现异步调用
问题引出: winform程序中的耗时操作,一般不能在UI线程中执行,需要另开线程.往往我们需要在耗时操作结束后将结果显示在UI上. 以下是Mainform.cs中调用耗时操作的一段代码: Job j ...
- 解码一个加密的js文件
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- 无责任共享 Coursera、Udacity 等课程视频
本文转载自网络,原作者不详. (本文是用 markdown 写的,访问 https://www.zybuluo.com/illuz/note/71868 获得更佳体验) 程序语言 interactiv ...
- QT 自定义assert
预览 代码 #define assert_(expression,message) if (expression) \ { \ if (QMessageBox::Yes == QMessageBoxE ...
- 每天一条linux命令——crontab
crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查 ...