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++.
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這兩個指針可以傳遞任何數據類型,在這個接口面前,本文介紹的都是浮雲,不管如何,希望你能有點體會,如果想跟作者交流.