应用需求:

在网盘开发过程中有这样一个需求。即对文件版本号进行控制,即记录文件版本号的更替信息,这里说的更替信息不过记录不同一时候刻的文件变化,即文件的增、删、改、重命名等操作。在每一个待监控的文件夹下都会保存一份文件。记录文件变化的增量信息。每次低版本号到高版本号升级的时候就能够通过消元合并操作高速地进行。关于文件版本号控制的详细实现方案会在开发完好后列出。这里只指出它的保存方式,即将文件操作的实例对象序列化后保存在文件里。

序列化的实现:

这里我们採用QDataStream来实现序列化,QT针对不同的实例化对象有不同的要求。这里主要分两类。即:QT中原生的数据类型。比如:QString、QMap、QHash等,这对这样的原生数据类型的序列化。我们不须要做其他额外的操作,直接就能够序列化到文件里。另一类特殊的就是我们自定义的数据结构或类,这样的方式利用QDataStream不能直接实现序列化,我们必须重载<<和>>操作符,仅仅有重载完之后才干够按我们的要求实现序列化。以下就举例来说明一下。我们自定义的数据结构或类应该怎样实现序列化:

自己定义的类:LHTFileVersionItem,该类用来记录某一个操作。它的定义为:

#ifndef LHT_FILEVERSIONITEM_H
#define LHT_FILEVERSIONITEM_H #include <QDataStream> struct FileVersionItem
{
QString m_sFileAbsolutePath ; QString m_sFileOrgName ;
QString m_sFileNowName ; int m_sFileType ; QString m_sFileMoveFromAbsolutePath ;
QString m_sFileMoveToAbsolutePath ;
}; class LHTFileVersionItem
{
public:
LHTFileVersionItem();
~LHTFileVersionItem(); void setVersion(int version);
void setOp(int op); int GetVersion();
int GetOp();
FileVersionItem* GetFileVersionPointer(); friend QDataStream &operator<<(QDataStream & , const LHTFileVersionItem &);
friend QDataStream &operator>>(QDataStream & , LHTFileVersionItem &); private:
int m_iVersion; //!-1:delete 1:crate 2:change
//!3:rename 4:move
int m_iOp;
FileVersionItem *m_hFileVersionPointer ;
}; #endif // LHT_FILEVERSIONITEM_H

当中,friend QDataStream &operator << 和 friend QDataStream &operator >>就是声明的对操作符的重载。注意:这里我们使用了friend来声明为友元函数。这里对friendkeyword做些介绍:

何谓友元?为什么要使用友元?我们知道,採用类的机制后实现了数据的隐藏和封装。类的数据成员一般定义为私有成员。成员函数一般定义为公有的。依此提供类与外界间的訪问接口。可是,有时须要定义一些函数,注意:这些函数并非类的一部分(因此在cpp文件里实现该函数时。函数前不须要使用。类名::函数名的方式),但又须要频繁地訪问类的私有数据成员,这是能够将这些函数定义为友元函数。除了友元函数外还有友元类,两者统称为友元。友元的作用是提高了程序的执行效率(即:降低了类型检查和安全性检查等,这些操作都须要时间开销),可是它同一时候也破坏了类的封装性和隐蔽性,使得非成员函数能够訪问类的私有成员。

事实上,这里理解友元关注friend就能够了,friend代指“朋友”、“关系友好”的意思。仅仅有两者(函数与类友好形成友元函数。类与类友好形成友元类)关系友好,我才同意它訪问我的私有成员。

自己定义的类:LHTFileVersionItem的实现:

#include "lht_fileversionitem.h"

LHTFileVersionItem::LHTFileVersionItem()
{
m_hFileVersionPointer = new FileVersionItem();
} LHTFileVersionItem::~LHTFileVersionItem()
{
} void LHTFileVersionItem::setVersion(int version)
{
this->m_iVersion = version ;
} void LHTFileVersionItem::setOp(int op)
{
this->m_iOp = op ;
} int LHTFileVersionItem::GetVersion()
{
return this->m_iVersion ;
} int LHTFileVersionItem::GetOp()
{
return this->m_iOp ;
} FileVersionItem* LHTFileVersionItem::GetFileVersionPointer()
{
return this->m_hFileVersionPointer;
} //! 重载操作符<<的实现
QDataStream &operator<<(QDataStream &output , const LHTFileVersionItem & item)
{
output << item.m_iVersion << item.m_iOp << item.m_hFileVersionPointer->m_sFileAbsolutePath << \
item.m_hFileVersionPointer->m_sFileMoveFromAbsolutePath << item.m_hFileVersionPointer->m_sFileMoveToAbsolutePath << \
item.m_hFileVersionPointer->m_sFileNowName << item.m_hFileVersionPointer->m_sFileOrgName ;
return output ;
} //! 重载操作符>>的实现
QDataStream &operator>>(QDataStream & input, LHTFileVersionItem & item)
{
input >> item.m_iVersion >> item.m_iOp >> item.m_hFileVersionPointer->m_sFileAbsolutePath >> \
item.m_hFileVersionPointer->m_sFileMoveFromAbsolutePath >> item.m_hFileVersionPointer->m_sFileMoveToAbsolutePath >> \
item.m_hFileVersionPointer->m_sFileNowName >> item.m_hFileVersionPointer->m_sFileOrgName ;
return input ;
}

继承自Object后会出现的问题:

假设我们自己定义的类继承自QObject,在使用时可能会出现这种编译错误:error C2248 'QObject::QObject' : cannot access private member declared in class 'QObject'例如以下所看到的:

为什么加了继承自QObject就会出现这样的问题呢,肯定是QObject的问题,查看源代码中在private中有这样一句:

private:
Q_DISABLE_COPY(QObject)
Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *)) /*
   Some classes do not permit copies to be made of an object. These
   classes contains a private copy constructor and assignment
   operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class) \
    Class(const Class &); \
    Class &operator=(const Class &);

从上面的凝视和实现能够看出继承自QObject后它不同意对象的赋值操作,即=。我查找我全部调用的函数里面没有看到直接赋值的操作啊,那为什么会出现这种问题呢?原因在于在函数调用时形參的传递也会被觉得是赋值操作。因此,出现这样问题,直接将QObject的继承去掉就能够,我们没有使用到QObject独特的特定。

局部变量使用对性能的影响以及进程的堆和栈:

因为在代码中我使用了QMulitHash<QString , LHFilteVersionItem> tmp;这一局部变量来保存某一文件夹下的文件。因为在写測试代码期间,我利用循环模拟了50万的数据序列化后保存在文件里,在执行期间我发现读取函数耗费非常长的时间。而函数里面最耗时的读取操作也仅仅花费了非常短的时间,可是函数一直无法马上退出,在等待了大约30s后才干退出,相关代码例如以下:

void LHTWORKFLOW::ReadAllDataFromFile(QMultiHash<QString, LHTFILEITEM> &m_hFileItemInfo)
{
if (NULL == m_fFileInfoHandle)
{
OpenFile(m_sFileItemInfoAbsolutePath , 0);
} m_fFileInfoHandle->seek(0); QDataStream input(m_fFileInfoHandle); QMultiHash<QString, LHTFILEITEM> final;
while (!input.atEnd())
{
QMultiHash<QString, LHTFILEITEM> tmp ;
input >> tmp ;
final += tmp ;
} m_hFileItemInfo = final ;
CloseFile(m_fFileInfoHandle);
}

经过细致分析和思考,发现问题就出在局部变量final上,因为是局部变量,因此当函数运行完成后局部变量就要销毁,因为是QMultiHash类型的变量,我们知道Hash相比数组来说它的一大优点是数据地址不连续。元素在内存控件中占用的内存地址是不连续的,而数据量又大。因此在销毁的过程中应该是逐步遍历去释放内存指针去了。假设是数组这样的连续的数据结构的话。释放会非常快,仅仅须要把该块内存的标志设为无用它就又能够被系统回收利用了。这样的情况还是我第一次碰到。感到非常有意思。对我以后的代码编写也有一定的知道意义。于是我就查阅了进行相关堆和栈的相关内容。

下面内容的參考链接:http://blog.csdn.net/hairetz/article/details/4141043 。为了加深理解我这里再列出一点吧。

1> 预备知识—程序的内存分配

一个由C++编译的程序占用的内存分为一下几个部分:

1) 栈区(stack)— 由编译器自己主动分配释放。存放函数的參数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2) 堆区(heap)— 一般由程序猿分配释放,若程序猿不释放,程序结束时可能又操作系统回收。

3) 全局区(静态区)(static) — 全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域。未初始化的全局变量和未初始化的静态变量在相邻的还有一块区域。程序结束和由系统释放。

4) 文字常量区— 常量字符串就是放在这里的。程序结束后由系统释放。

5) 程序代码区— 存放函数体的二进制代码。

2> 堆和栈的对照

1) 申请方式

栈(stack)由系统自己主动分配。比如。声明在函数中的一个局部变量 int b ; 系统自己主动在栈中为b开辟空间。

堆(heap)须要程序猿自己申请。并指明大小,在c中malloc函数如:p1 = (char *)malloc(10) ; 在C++中用new运算符如:p2 = new char[10];

但注意:p1、p2本身是在栈中的,仅仅是通过malloc和new分配的空间是在堆中的。

3> 申请后的系统响应

栈(stack):仅仅要栈的剩余空间大于所申请的空间,系统将为程序提供内存。否则将报异常提示栈溢出。

堆(heap):首先应该知道操作系统有一个记录空暇内存地址的链表。当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空暇节点链表中删除,并将该节点的空间分配给程序,另外。对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小。这样代码中的delete语句才干正确的释放本内存空间。

另外,因为找到堆节点的大小不一定正好等于申请的大小,系统会自己主动的将多余的那部分又一次放入空暇链表中。

4> 申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶和栈的最大容量是系统预先规定好的。在windows下。栈的大小是2M,假设申请的空间超过栈的剩余空间时,将提示溢出。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是因为系统使用链表连存储的空暇内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比較灵活,也比較大。

5> 申请效率的比較

栈:由系统自己主动分配。速度较快。但程序猿是无法控制的。

堆:是由new分配的内存,最好的方式是用VirtualAlloc分配虚拟内存,它既不是在堆也不是在栈,而是直接在进程的地址空间中保留一块内存。尽管用起来最不方便。可是速度快也最灵活。

6> 堆和栈中的存储内容

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个參数。在大多数的C编译器中,參数是由右向左入栈的,然后是函数中的局部变量。注意:静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是參数,最后栈顶指针指向最開始存的地址。也就是主函数中的下一条指定。程序由该点继续执行。

堆:通常是在堆的头部用一个字节存放堆的大小。

堆中的详细内容由程序猿安排。

总结:

通过这块内容的分析,对影响程序运行性能的几个方面有了更清除的了解,对进程的堆栈也有了更深入的了解。感觉自己開始慢慢关注那些实质性的东西。这点我感觉是非常好的,加油,对每一个不懂的问题都要认真总结 。

【大话QT之七】QT序列化操作的更多相关文章

  1. Qt中QUdpSocket序列化问题

    写了一个小的Qt网络程序,很简单,发送的网络消息除了字符串还有一个结构体.很简单的想到用memcpy()函数来将数据序列化为BYTE数组从而实现网络传输. 序列化是Java中一个概念,C中并没有,C+ ...

  2. 第32课 Qt中的文件操作

    1. Qt的中IO操作 (1)Qt中IO操作的处理方式 ①Qt通过统一的接口简化了文件和外部设备的操作方式 ②Qt中的文件被看作一种特殊的外部设备 ③Qt中的文件操作与外部设备的操作相同 (2)IO操 ...

  3. 【QT】文件读写操作

    读取输出: QFile file("D:/Englishpath/QTprojects/1.dat"); if(!file.open(QIODevice::ReadOnly)) { ...

  4. Qt数据库 QSqlTableModel实例操作(转)

    本文介绍的是Qt数据库 QSqlTableModel实例操作,详细操作请先来看内容.与上篇内容衔接着,不顾本文也有关于上篇内容的链接. Qt数据库 QSqlTableModel实例操作是本文所介绍的内 ...

  5. C/C++ Qt TreeWidget 嵌套节点操作技巧

    在上一篇博文<C/C++ Qt TreeWidget 单层树形组件应用>中给大家演示了如何使用TreeWidget组件创建单层树形结构,并给这个树形组件增加了右键菜单功能,接下来将继续延申 ...

  6. 3.Qt GUI中一些操作记录

    一.如何在Widget中利用代码添加背景图片 this->setAutoFillBackground(true); // QPalette palette = this->palette( ...

  7. Qt Table Widget常用操作

    一.鼠标悬浮在item上 显示提示信息 1.在构造函数开启table Widget控件的鼠标捕获功能 // 开启鼠标捕获功能(实现table widget的悬浮功能) ui.tableWidget-& ...

  8. 【Qt】Qt之自定义界面(QMessageBox)【转】

    简述 通过前几节的自定义窗体的学习,我们可以很容易的写出一套属于自己风格的界面框架,通用于各种窗体,比如:QWidget.QDialog.QMainWindow. 大多数窗体的实现都是采用控件堆积来完 ...

  9. 【Qt】Qt Assistant介绍【转】

    简介 Qt Assistant也就是我们常说的Qt助手,是一款用于呈现在线文档的工具. 简介 一分钟学会使用 Qt参考文档 Qt Assistant详解 命令行选项 工具窗口 文档窗口 工具栏 菜单 ...

  10. 【Qt】Qt Creator介绍【转】

    简介 Qt Creator是使用Qt开发的IDE.Qt支持Windows.Linux/Unix.Mac OS X.Android.BlackBerry.QNX等多种平台,Qt Creator为不同平台 ...

随机推荐

  1. 演练2-2:Guestbook示例应用程序

    为使Guestbook应用程序有用,我们需要为用户提供一些提交数据的方式,将这些数据存储起来,以便稍后进行查看.为了达到这一目标,我们打算对应用程序添加一个数据库,将其作为该留言簿的后台存储库. 1. ...

  2. 使用Spring标签库

    A.Spring标签库 Web项目若使用Spring Web MVC并使用JSP作为表现的话.从Spring2.0版本开始提供一套标签库可供使用. 使用标签库无非是易于开发,维护之类云云.这里就不阐述 ...

  3. 浅谈PPM (Project Portfolio Management) - 1

    前言: 本文以纯理论性的PPM解说为主,不会涉及到具体怎样实施,我会在以后介绍具体的PPM实施方案介绍. PPM,可能非常多人并不清楚甚至可能没听说过,这是一个近些年才流行起来的概念,是Project ...

  4. 如何在VC中显示透明背景位图

    简单的调用系统API. Windows NT/2000/XP: Included in Windows 2000 and later.Windows 95/98/Me: Included in Win ...

  5. C++ 中使用boost::property_tree读取解析ini文件

    boost 官网 http://www.boost.org/ 下载页面 http://sourceforge.net/projects/boost/files/boost/1.53.0/ 我下载的是  ...

  6. [放松一下] 经典高清电影合集 170G BT种子下载

    经典高清电影合集 170G BT种子下载 点击文件名下载 经典高清电影合集170G BT种子.torrent 下载方法 经典高清电影合集详情见目录: 1. 杀手47 2. 这个杀手不太冷 3. 放牛班 ...

  7. 在Centos 5.4上安装Mysql5.5.10 (整理以前的工作文档)

    1.     安装环境 1.1.  目的 安装Mysql5.5.10服务,提供公司XXXX测试环境.正式环境也采用该版本的mysql 1.2. 硬件环境 PC机:IntelE5300 内存4G 硬盘5 ...

  8. uvc摄像头代码解析6

    10.扫描视频设备链和注册视频设备 10.1 uvc视频链 struct uvc_video_chain { //uvc视频链 struct uvc_device *dev; //uvc设备 stru ...

  9. 【VBA研究】VBA通过HTTP协议实现邮件轨迹跟踪查询

    作者:iamlasong 1.接口说明 通过互联网訪问,运单跟踪信息查询接口基于HTTP协议开发,接口为RESTFul风格的Web Service,信息交互过程为用户按我方提供的web service ...

  10. lucene做简单的文件索引

    package com.mylucene; import java.io.File; import java.io.FileReader; import java.io.IOException; im ...