https://blog.csdn.net/yuzeze/article/details/51602278
目录:
1:基本介绍与二进制兼容
2:二进制兼容的设计原则
3:常见c++/qt信息隐藏
4:Q_Q,Q_D介绍
5:定制可编辑treewidget与如何访问基类的Private
6:总结
1:基本介绍与二进制兼容
作者虽然一直在linux做开发工作, 对于window平台下,软件的开发模式与稳定性及质量一直不太满意,但是对于window系统在系统兼容方面还是十分佩服的,最近拿着win95下的一款软件在win7上安装了一下,依然运行的很好,这就涉及到了程序兼容的感念,一般可以分为二进制兼容(Binarycompatible),与源代码兼容(sourcecompatible),window的开发接口winapi/mfc在这两方面做的非常好,而linux平台下的大部分软件在这方面表现很差劲,当然主要原因在于linux下的很多基本库属于不同的组织,个人或者基金会,这方面的问题已经是linux系统在桌面的普及的一大障碍,面对将近几百个发行版,而且每一个发行版一年内至少更新一次,对于普通用户来说实在是个艰难的选择。可喜的是linux的社区已经开始注意这方面的问题。
那么什么是二进制兼容呢,二进制兼容是针对动态链接库而言,如果动态库的更新,可以兼容以来此库以前版本的程序的话,我们就说这个库是二进制兼容的(微软提供的大部分library,都在这个级别上),如果程序必须在动态库上重新编译链接的话,那么就是源代码兼容(linux平台大部分库都需要重新编译),如果源代码重新编译不了的话,...,那...太失败了,glibc/gtk++/qt/python在主要版本发生变化时都是这种情况,这里可能引起歧义,程序设计语言可以分为编译型和脚本型,下文限定到编译型语言,你可以直接理解为c/c++.
: m* Q: X& H1 { @2:二进制兼容的设计原则
此段是从kde社区的一篇文档,针对每一条,笔者并没有去测试(内容主要讲一些基本原则,那些操作可以保持二进制兼容,那些操作是禁止,库设计人员需要遵循的准则等等),如果有兴趣的话,可以从编译链接的角度来解释为什么会这样,欢迎讨论啊...
http://techbase.kde.org/index.php?title=Policies/Binary_Compatibility_Issues_With_C%2B%2B
基本原则:
程序设计中,开发正角度看到的库包括头文件(.h)和lib(window下文.lib,linux下为.so)我们知道编译器链接之后呢,访问类中的元素是通过偏移量来访问,所以抽象出我们的一条原则:
a: 不可以改变类的大小和类成员的顺序
如果类中有虚函数的话,虚函数通过虚拟地址表来访问虚函数,这就抽象出第二条原则:
b:如果类中没有虚函数,那么不能添加虚函数,其实也是影响类的大小的变化。
1 N) S5 ^. B9 @$ v3:常见c++/qt信息隐藏
做为软件开发者来说,二进制兼容会经常碰到,在c/c++中如果头文件的变化,按照make的时间戳机制,凡是原来这个头文件的源码文件都会重新编译,而依赖此库的别的库都需要重新编译。而库的实现方法改进,修正bug等等,当然会引起库的不断更新,那么如何继续保持二进制兼容呢.
我们先来看一个基本的C++类的定义,我们以一个可编辑的treewidget为基础,一步一步介绍基本的设计原则与隐藏信息隐藏方法,最终这个treewidget要支持undo/redo/add/del/copy/paste/drag等等所有想到的功能:
#第一个版本v1如下(下载:v1.tar.gz)假设我们需要一个支持可添加子节点,兄弟节点的treewidget
代码如下:
treeop.h
#ifndef _treeop_h_
6 j% W( c9 u. ?* K: d! g# Z#define _treeop_h_
0 S( Y- B- C( g9 {* R" ~#include <QTreeWidget>
#include <QTreeWidgetItem>
9 u; y( G! ?9 j: k( W* G) t0 j; q* p# `#include <QAction>
2 g2 p. g2 I A$ g#include <QAction>
class TreeOpPrivate;
class TreeOp : publicQTreeWidget
{
Q_OBJECT
' S s9 J" r# L" O8 B6 spublic:
TreeOp(QWidget *parent =NULL);
virtual ~TreeOp();
5 _3 W) s7 _8 L" ~virtual QTreeWidgetItem*newTreeItem();
public slots:
: t# V( K4 e# D* N0 B/ H) @) yvoid addChild();
4 E8 c2 o x& J9 w, `0 D% |& m1 |! Ivoid addNextSlibing();
void addPrevSlibing();
private:
QAction *m_addChild;
6 T2 \) N% {$ l- B% {QAction*m_addPrevSlibing;
QAction*m_addNextSlibing;
};
' A: ^" G r3 ~) R
#endif
% _: y6 c0 Y$ @" W! m
源文件请下载:
+ O1 ]; m. u4 c p/ r+ ~$ A
测试代码为main.cpp,请从附件中下载编译方法qmake&&make,下面的例子不再赘述
1 F6 d/ u; F' I7 R. V: S Y( h
添加子节点被绑定为Alt+a,添加上一个兄弟节点为Alt+p,下一个兄弟节点为Alt+n。工作的非常好,打包发布,喝咖啡...
第二天有了反馈,用户希望再添加一个删除的功能,好,在TreeOp类中添加一个新的接口,简单,包含treeop.h的文件重新编译一次即可...等等,用户还想再添加支持节点可以moveup/movedown/moveright/moveleft的功能,OMG...,等等TreeOp类中QAction的几个成员名字起的不好,改改名字,每次都要重新编译,如果您依赖TreeOp,您也得重新编译...崩溃5分钟...,有没有好的办法呢。我们可以设计一个私有类,来保存所有的变量和操作
第二个版本:(下载v2.tar.gz )
treeop.h代码:
#ifndef_treeop_h_
) H$ N5 Q- n% j#define_treeop_h_
#include<QTreeWidget>
#include<QTreeWidgetItem>
#include<QAction>
0 h0 ?5 J+ W) u: L K- j( A/ `#include<QAction>
" y0 } F! y. Y- m! cclassTreeOpPrivate;
classTreeOp : public QTreeWidget
$ ^# ^ z# ^! ~, c{
1 T {& C: r1 C2 y$ f/ K- WQ_OBJECT
8 ?' Q! O" B @) ~. H) G$ V$ `public:
, o2 m0 N9 U# v: x( c; P: @TreeOp(QWidget*parent = NULL);
& W5 W: u5 \# Q/ W/ {% dvirtual~TreeOp();
virtualQTreeWidgetItem *newTreeItem();
. A. v7 L! B2 b, w0 h+ r% c9 j3 sprivate:
TreeOpPrivate*m_p;
8 r; h/ c& F8 M; |6 V) f; }( e};
0 y: L3 l5 f* j+ Y9 T. f
#endif
6 k P m3 x: N" j
从代码中可以看到,treeop.h文件已经完全简化了,我们新添加了一个类,TreeOpPrivate;
把所有可变化的量全部写道treeop.cpp文件中去,TreeOpPrivate继承了QObject类,所以可以添加slots,因为在cpp中,所以呢在treeop.cpp的文件末尾我们#include“treeop.moc”,来让Qt对treeop.cpp也进行预处理。
' I1 {, M$ K# {# }' n+ i
好了现在不管用户想添加什么功能,我们都可以在private类中实现,无论如何修改,需要编译的文件只有自己,别的即使依赖treeop.h的文件也无须重新编译。世界安静了吗?...等等,再看看程序,在源程序中可以看到很多m_owner->m_p->为什么呢,m_owner->是TreeOpPrivate类访问TreeOp的接口,m_p->是TreeOp类访问TreeOpPrivate的接口,好麻烦,有别的方法吗,让我们看起来美观又直接,好下面我们进化到第三个版本,看看Qt中的Q_D/Q_Q宏
4:Q_Q,Q_D介绍
假 如整个类的继承体系有点深怎么办,想用那一层的东西还要一点点的爬着去找,真麻烦!
没关系,有共享d指针呢,只需要在上一层里加个d指针就可以省去这么多麻烦了。它可以访问任意一层的私有数据(其实这样就不是private啦,叫 protected还差不多,有点不像OO),还可以获取父层的访问器。
共享d指针实现方法如下:
1、在基类中定义一个protected权限的d_ptr指针; 3
_2 k6 W5 v2 L1 }
2、在每个派生类中定义d_func(),获取基类d_ptr,并将其转换为当前私有类指针(派生自基类d_ptr); 6
@: r/ a/ {% q
3、在函数中使用Q_D,这样就可以使用d了; .
G3 h) _! m9 C1 I7 V) e' t' t
4、在私有数据继承体系中,不要忘记将析构函数定义为虚函数,基类析构函数中释放d_ptr,以防内存泄露!!! n
5、类的派生,加上protected 构造函数,调用父类构造函数,将私有数据类传参;
第三个版本:(下载v3.tar.gz)
treeop.h的代码:
#ifndef_treeop_h_
#define_treeop_h_
* {1 b9 M) Y2 R7 E#include<QTreeWidget>
#include<QTreeWidgetItem>
#include<QAction>
7 a8 o# _: z7 s- X. X: a0 j#include<QAction>
classTreeOpPrivate;
classTreeOp : public QTreeWidget
{
Q_OBJECT
! E% N* W" H+ m* `public:
TreeOp(QWidget*parent = NULL);
3 W W% X3 C* o2 q: D& M4 F7 xvirtual~TreeOp();
( T4 ^0 M+ `5 S4 W* ]2 C3 rvirtualQTreeWidgetItem *newTreeItem();
: |) k# q! v5 q' V/ L* \protected:
. Z) _ s! m) h( k7 I$ `TreeOpPrivate*const d_ptr;
private:
Q_DECLARE_PRIVATE(TreeOp);
};
1 K- E5 X! F; e, E" e7 d& M0 n2 p+ E6 e( I' ^" u4 r9 J, v
#endif
/ O% X- {" b/ z4 P6 l3 J4 Y
我们可以发现和第二个版本相比,在类TreeOp中多了这几行
protected:
# u$ G& C, b5 _" ?( m! G5 ATreeOpPrivate*const d_ptr;
private:
+ A3 V" t0 X/ G0 d" Y$ VQ_DECLARE_PRIVATE(TreeOp);
3 j6 i1 ?$ R. \/ r( g
在类TreeOpPrivate中,多了以下几行:
TreeOp* const q_ptr;
Q_DECLARE_PUBLIC(TreeOp);
' E( X3 \/ c4 x1 T4 K9 p, k
想要了解一下,把这个宏拆开好了:
#defineQ_DECLARE_PRIVATE(Class) \
inlineClass##Private* d_func() { return reinterpret_cast<Class##Private*>(d_ptr); } \- s2 F ?/ Y+ M i* M; F' a
inline const Class##Private* d_func() const {return reinterpret_cast<const Class##Private *>(d_ptr); }\
friend class Class##Private;
这其中”##”表示的连接的意思,例如a##b,经过编译器预处理后,就成了ab.
从宏的定义可以看出,声明了两个内联的函数d_func和constd_func(),用于返回对应的d_ptr.并且把Class##Private声明为Class的友元.
* y1 w7 b" G Z5 w# E$ g
我们再来看看Q_D宏的展开:
#defineQ_D(Class) Class##Private * const d = d_func()
假如我们传入TreeOp会翻译成 TreeOpPrivate *const d =d_func(); 从而就可以得到了d_ptr的指针。在来看看TreeOp的初始化:
TreeOp::TreeOp(QWidget*parent) : QTreeWidget(parent),d_ptr(new TreeOpPrivate(this))
{
setHeaderHidden(true);
( H. ]. G7 Z' s# {0 C" _6 H$ |}
我们在构造函数中把d_ptr进行初始化,有的同学可能会问,这里写成这样子如何?
TreeOp::TreeOp(QWidget*parent) : QTreeWidget(parent)
{
* z7 |2 s7 V" \: {' rd_ptr = newTreeOpPrivate(this);
setHeaderHidden(true);
9 q% E. a" c5 P} h O" M' Y4 Z
实际编译一下就知道了,应为我们定义的d_ptr为const的,所以这样是无法编译通过的.
再来看看Q_DECLARE_PUBLIC的展开
#defineQ_DECLARE_PUBLIC(Class) \
inlineClass* q_func() { return static_cast<Class *>(q_ptr); } \
inlineconst Class* q_func() const { return static_cast<const Class*>(q_ptr); } \
friendclass Class;7
m0 Q' Y& n/ I; X8 g
#defineQ_Q(Class) Class * const q = q_func()
可以看出和Q_DECLARE_PRIVATE差不多,可以得到q_ptr的指针。
/ K8
M, M$ ] F/ a
同时在看看Q_Q宏
#defineQ_Q(Class) Class * const q = q_func()
好了差不多,经过这次处理,我们的代码看起来整洁多了,结束了吗?可能你已经想结束了,可是本教程还没呢,如果你还有兴趣,我们还要将到如何定制这个可编辑的treewidget,如何把它用到书签,目录中,如何实现engine/gui的分离,好吗,你们不是来听我罗嗦的,不过确实下面才是重点啊。
9 T2
A a t; E% ]4 {
第四个版本:(下载v4.tar.gz)
+ E*
O/ L3 B$ N! Q4 K
在完成了上面的功能之后,我们来考虑一下,冲上一杯咖啡,点燃一根香烟,哦算了,照顾下女同志
a: 从功能角度讲,距离我们预期的目标还很远,我们还缺少copy/paste/cut,drag/drop,undo
/redo/edit等功能
b: 从用户角度看,基本这个widget什么都没有,实现的功能也都是快捷键,我鼠标点击那里阿,右键菜单呢,怎么保存我修改好的呢,这是谁写的阿,怎么差劲...
c: 从使用这个widget的开发者来说,这是啥玩意阿,我要自定义QTreeWidgetItem,我中间有目录,有文件,我还要添加别的功能,我的这个item不能支持删除,也不能再添加子节点了,omg,这是谁写的垃圾阿...
哥是从来不会写出垃圾的,不过这个这个...越看越像小学生的哦...好,我们继续...
2 I% ?# M2 Z( j- u# Z7 N
我们先从使用这个widget的开发人员来看,我希望有一个完全可编辑的的treewidget,我还要可以自定义,我要可以美化这个widget,我想把它用到浏览器的书签功能中,我要做个书籍的分类,我要安排一个日程表...,我要它来记录一个公司组织架构图...
" u6 q! [% H) O. p8 U1 U
神啊,软件工程,设计模式,用户行为...,下班了,哦,不行,不实现这些功能都不准下班。
好,我们提供一个基本的treeop,这个用来处理所有的逻辑操作,但是不包含任何用户行为
我们在构造一个TreeOpWidget类,让它负责跟用户交互,如果RD不满意这个类,让他们自己写好了。
再来看看新版的treeop.h的代码:(因为里面涉及copy/paste/undo/redo/drag/drop)笔者就不详细介绍了,有不满意的地方想联系作者,看文章开头:
#ifndef_treeop_h_
$ r9 ^9 M; M: K( y1 S4 f* o#define_treeop_h_
$ Y( N% V2 [3 Q#include<QTreeWidget>
8 v m Z4 @1 g* ^#include<QTreeWidgetItem>
#include<QAction>
7 C x( L3 E9 X' l9 O9 H2 S8 e#include<QAction>
classTreeOpPrivate;
classTreeOp : public QTreeWidget
{
Q_OBJECT
public:
TreeOp(QWidget*parent = NULL);
virtual~TreeOp();
voidclearUndoStack();
//清空undo栈
voidundo();
//undo操作
voidredo();
//redo操作
voidsetEditable(bool);
boolisEditable();
voidsetCopyable(bool);
3 ?+ u H' ?4 }, O# a8 lboolisCopyable();
voidsetDragable(bool);
boolisDragable();
! f3 B& \; w1 P4 N7 ivoidsetDelable(bool);
boolisDelable();
voidsetUndoable(bool);
boolisUndoable();
boolisModified();
//设置是否可编辑,可拷贝,可拖拉,可编辑,可undo,可删除等
voidcopy();
/ E, ~7 Y# U( Xvoidpaste();
# r! Y7 a% d# g7 X# y: tvoidcut();
( i* @7 F5 T; Q5 JvoidmoveUp();
voidmoveDown();
- [3 `) Y; H% H; x2 S1 }% KvoidmoveLeft();
7 X# w k' @3 N3 e5 K6 g! BvoidmoveRight();
6 `/ l$ o k1 l0 m5 M0 X3 ]voiddel();
//实际操作的接口
voidsort(bool sub);
//无视
virtualbool canCopy(QTreeWidgetItem *item);
virtualbool canPaste(QTreeWidgetItem *item);
. Q+ l$ |# u: T9 Y& `# w: i8 ovirtualbool canCut(QTreeWidgetItem *item);
virtualbool canMoveUp(QTreeWidgetItem *item);
virtualbool canMoveDown(QTreeWidgetItem *item);
" V. L. m# V( S4 w2 u. qvirtualbool canMoveLeft(QTreeWidgetItem *item);
virtualbool canMoveRight(QTreeWidgetItem *item);
2 s: a% m8 K2 m3 Rvirtualbool canAddChild(QTreeWidgetItem *item);
virtualbool canAddNextSlibing(QTreeWidgetItem *item);
virtualbool canAddPrevSlibing(QTreeWidgetItem *item);
2 i* A+ o8 C! ^virtualbool canDel(QTreeWidgetItem *item);
//询问子QTreeWidgetItem,可进行这些操作吗?
virtualQTreeWidgetItem *newTreeItem();
//创建一个新item
virtualQTreeWidgetItem *treeItemFrom(const QString &str);
virtualQString treeItemTo(QTreeWidgetItem *item);
//用于支持copy/paste操作,子QTreeWidgetItem需要自己实现这些功能,产生一个str,并且从str在new一个item出来
virtualQString mimeType();
//copy/paste的mimeType
virtualvoid updateItem(QTreeWidgetItem *item,int col);
//此Item被更新
voidclearMenu();
voidaddAction(QAction*);
voidaddSeaparator();
//做右键菜单
voidaddChild();
voidaddNextSlibing();
( Y; K4 z7 u8 bvoidaddPrevSlibing();
//添加节点
voidrefreshSignals();
//重新刷一边使能信号
protected:
voiddragEnterEvent(QDragEnterEvent *event);
: ?$ _5 N$ ]2 H* Q% d7 evoiddropEvent(QDropEvent *event);
voidcontextMenuEvent(QContextMenuEvent *event);
signals:
voidsigUndoEnabled(bool);
/ J1 M6 `. F' G/ N" a: X" |voidsigRedoEnabled(bool);
voidsigCopyEnabled(bool);
voidsigPasteEnabled(bool);
voidsigCutEnabled(bool);
voidsigDelEnabled(bool);
voidsigMoveUpEnabled(bool);
voidsigMoveDownEnabled(bool);
5 n R- }0 y9 q& w! J$ PvoidsigMoveLeftEnabled(bool);
# `1 z- ~0 p/ N" {* o8 N. _3 RvoidsigMoveRightEnabled(bool);
+ O$ |, \4 b# g0 L3 c* r( u' tvoidsigAddChildEnabled(bool);
; v/ u7 M- e# f7 A5 |) g: ]: a& t6 b$ dvoidsigAddNextSlibingEnabled(bool);
8 {. y. [ y/ V" s; P$ _voidsigAddPrevSlibingEnabled(bool);
//操作的使能信号
protected:
# \' C# n, J+ QTreeOpPrivate* const d_ptr;
8 t5 F; Z4 ^* y( Gprivate:
u* I+ [5 a& IQ_DECLARE_PRIVATE(TreeOp);
};
; G N& Z2 @) w' {7 Q1 s2 M, v1 [% f5 O
#endif
! r7 H& x3 h/ D7 E% v! ]
代码的旁边有简单的注视,我们可以把接口分为几类,
2 A# s- J% B$ s5 \) F. w
一类是使能信号,通过signal送出,表明这时候可以进行这项操作吗?例如voidsigAddChildEnabled(bool);
表示是否可进行添加子节点的操作。
; ?: W# P4 b& x
一类是虚拟类,如virtual boolcanCopy(QTreeWidgetItem *item);
用来判断这个item是否可进行copy。
# S$ U7 D! g/ E0 ]% o. |3 `
一类是实际操作类,voidcopy(),就是进行copy这个动作。
我们有新添加了一个文件叫做treeopwidget.h从名字可以看出这个文件会输出一个可编辑的widget。来看看treeopwidget.h的代码
#ifndef_treeopwidget_h_
#define_treeopwidget_h_
#include<QWidget>
+ Z2 W4 ]( A# Y+ o4 K#include<QTreeWidget>
1 g, K4 ~# k6 c( ?4 h0 hclassTreeOpWidgetPrivate;
5 _- `$ S9 w3 P: pclassTreeOp;
# O4 E/ Z3 J' u. r2 Z8 q. B1 g% r' qclassTreeOpWidget : public QWidget
{
3 O3 E8 Y9 y$ t+ [5 e7 ?Q_OBJECT
2 R% Z2 g- p6 u3 z+ `0 L( ?public:
# W+ z6 `1 M/ Y6 {+ J+ iTreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);
( R+ W, r0 p. T( |+ _( v* uvirtual~TreeOpWidget();
4 {4 T4 H% X- FvoidshowToolbar(bool);
TreeOp*treeWidget();
protected:
6 e& L8 i! x' e; i2 tTreeOpWidgetPrivate* const d_ptr;
private:
Q_DECLARE_PRIVATE(TreeOpWidget);
};
8 J8 R3 Z& H6 }* T#endif
8 C4 P/ N6 n% y2 K- W; ~% \1 F$ w9 N' X b; ^5 M
# P/ W: }5 j/ D
同样我们定义TreeOpWidgetPrivate,TreeOpWidget。
+ @) r; k3 e6 |) a2 @
看构造函数:
TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);
5 P& P4 {) q, |- B: f
如果用户没有继承TreeOp的话,tree为空,那么cpp中就自己new一个出来。在treeopwidget的实现文件中,我们定义了所有的操作,和action,请参看代码:
2 c) L, L& X/ s3 s2 {2 E8 s4 K4 o
' u7 g& R) {# c
到这里第一步功能总算完成了,一个支持所有操作的TreeOp类,一个可以把这个导出为一个Widget的TreeOpWidget类,而且我们对TreeOpWidget的定制不会影响二进制兼容,yeah。
嗯,总算有点样子了。可是这个widget好丑,那个up/down/left/right的action我不想要怎么办,而且放在上面也难看了,一个RD想做个bookmark管理器,要求好多,不过没办法,谁让我们追求完美呢,我们进化到下一个版本
5:定制可编辑treewidget与如何访问基类的Private (下载v5.tar.gz)
bookmark的开发者说,那个TreeOpWidget的类挺好的,想完全抛弃它,能不能在上面改改来完成我的功能呢,比如,我想添加一个open/save的菜单到上面,用来打开和保存编辑内容,你能帮我想想办法吗,
哥哥总是心太软,心里面想你为啥不自己写一个TreeOpWidget呢,可是嘴里说没问题,我来看看,小妹妹阿...
她的需求一句话,虽然她在不同项目组中,但是她希望可以访问到TreeOpWidgetPrivate类,哦,等等,我的TreeOpWidgetPrivate类是在cpp中定义的阿,这这...,把它暴露出来,影响二进制兼容啊,不暴露,怎么使用呢?怎么办,怎么办...
/ I" a% D3 @2 O" O* K& G2 t' _% y
看看QT的源代码吧,为什么存在怎么多_p.h文件呢?...哦
2 @) ~! G Q- y; h* K y( L
既然bookmark想用到TreeOpWidgetPrivate类,那么我们就把它输出出来,但是不能暴露给别的不使用这个类的模块,好,开始...
我们有一个bookmarks.xml文件,内容如下:
<bookmarksversion="0.1">
0 A/ D% U. G% o) h# {4 E- e<categoryname="linux">
/ Z. G5 z8 T0 o8 m, D$ j8 W6 c<dataname="linxu program"/>
' T& j Z# J; t4 e" R1 M<dataname="linux program"/>
<dataname="linux program"/>
$ S# _: N S/ l</category>
4 k$ _, ~4 E0 t# x8 d- k5 b</bookmarks>
category表示这是一个目录,可编辑,data表示这是一项数据,不可编辑。
我们现在把TreeOpWidgetPrivate的定义放到一个新建文件中,treeopwidget_p.h然后在treeopwidget.cpp中include这个文件,treeopwidget.h定义如下:
#ifndef_treeopwidget_h_
; y3 e: d+ ~$ b" G#define_treeopwidget_h_
#include<QWidget>
" t d" x. V$ e9 J& H, N* Q#include<QTreeWidget>
& J) |! z7 X* }- S8 N* @classTreeOpWidgetPrivate;
classTreeOp;
* S y P( {( _" Z5 J- Q5 D2 V- kclassTreeOpWidget : public QWidget
6 ?' L( _; W" {9 A{
Q_OBJECT
6 i) U- h. R `( l N, gpublic:
TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);
virtual~TreeOpWidget();
+ c. Q4 `; D. N4 K+ M! }% LvoidshowToolbar(bool);
TreeOp*treeWidget();
, E3 G0 w& b4 t' M$ uprotected:
' L) b4 E3 ?) bTreeOpWidgetPrivate* const d_ptr;
TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);
2 L* i, y- p) n, P8 }7 S0 ~private:
1 A4 u# g& W, b3 S8 I! FQ_DECLARE_PRIVATE(TreeOpWidget);
$ y$ [, q& b5 W6 X};
. y# z6 W4 g7 u, U4 Y+ m) }
& ], W8 z6 z/ [3 m4 k8 O
#endif
可以看到我们能新添加了一个接口TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);
一个Protected构造接口,方便来bookmark类构造父类.
9 Y' \$ E9 A8 T% F
看bookmark定义,在book/目录下bmwidget.h
#ifndef_bmwidget_h_
#define_bmwidget_h_
#include<QWidget>
4 K$ M% `6 f( L% D0 ^#include"treeopwidget.h"
, c( }0 n& W! T! J" W7 h! QclassBookmarksTreeItem;
classBookmarkWidgetPrivate;
classTreeOpWidget;
classBookmarkWidget : public TreeOpWidget
{
Q_OBJECT
public:
BookmarkWidget(QWidget*parent = NULL);
virtual~BookmarkWidget();
protected:private:
, Q_DECLARE_PRIVATE(BookmarkWidget);};
* l% S$ D( P$ |7 V: M$ @4 u- i#endif
BookmarkWidget继承了TreeOpWidget,BookmarkWidgetPrivate在bmwidget.cpp中,它继承了TreeopWidgetPrivate类,看看BookmakrWidget的构造:
BookmarkWidget::BookmarkWidget(QWidget*parent) : d_ptr(newBookmarkWidgetPrivate(this)),TreeOpWidget(*d_ptr,parent){
- K3 o( y! |8 P' \2 S. w}
通过TreeOpWidget(TreeOpWidgetPrivate&dd,QWidget *parent = NULL);
来初始化了父类。
运行效果如图:
在最前添加了load,save接口。
6:总结
在此教程中我们从二进制兼容开始,讲到了信息隐藏的技术,实际构建了一个可编辑的treewidget,介绍了Q_D,Q_Q等等,其实在window系统我们经常会用到这种技术,最最典型的的就是processMsg(inttype,long *lParam,long*wParm),通过lParam,wParam这两个指针可以传递任何数据类型,在这个接口面前,本文介绍的都是浮云,不管如何,希望你能有点体会,如果想跟作者交流.
在此教程中我们从二进制兼容开始,讲到了信息隐藏的技术,实际构建了一个可编辑的treewidget,介绍了Q_D,Q_Q等等,其实在window系统我们经常会用到这种技术,最最典型的的就是processMsg(inttype,long *lParam,long*wParm),通过lParam,wParam这两个指针可以传递任何数据类型,在这个接口面前,本文介绍的都是浮云,不管如何,希望你能有点体会,如果想跟作者交流.