Qt信息隱藏(Q_D/Q_Q)介紹


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 @$ v

3:常見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 s

public:


    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) @) y

    void addChild();

4 E8 c2 o  x& J9 w, `0 D% |& m1 |! I

    void 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! c

classTreeOpPrivate;


classTreeOp : public QTreeWidget

$ ^# ^  z# ^! ~, c

{

1 T  {& C: r1 C2 y$ f/ K- W

    Q_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/ {% d

    virtual~TreeOp();


    virtualQTreeWidgetItem *newTreeItem();

. A. v7 L! B2 b, w0 h+ r% c9 j3 s

private:


    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 x

    virtual~TreeOp();

( T4 ^0 M+ `5 S4 W* ]2 C3 r

    virtualQTreeWidgetItem *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 A

    TreeOpPrivate*const d_ptr;


private:

+ A3 V" t0 X/ G0 d" Y$ V

   Q_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" \: {' r

            d_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 l

    boolisCopyable();


    voidsetDragable(bool);


    boolisDragable();

! f3 B& \; w1 P4 N7 i

    voidsetDelable(bool);


    boolisDelable();


    voidsetUndoable(bool);


    boolisUndoable();


    boolisModified();

//設置是否可編輯,可拷貝,可拖拉,可編輯,可undo,可刪除等

    voidcopy();

/ E, ~7 Y# U( X

    voidpaste();

# r! Y7 a% d# g7 X# y: t

    voidcut();

( i* @7 F5 T; Q5 J

    voidmoveUp();


    voidmoveDown();

- [3 `) Y; H% H; x2 S1 }% K

    voidmoveLeft();

7 X# w  k' @3 N3 e5 K6 g! B

    voidmoveRight();

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 o

    virtualbool canCut(QTreeWidgetItem *item);


    virtualbool canMoveUp(QTreeWidgetItem *item);


    virtualbool canMoveDown(QTreeWidgetItem *item);

" V. L. m# V( S4 w2 u. q

    virtualbool canMoveLeft(QTreeWidgetItem *item);


    virtualbool canMoveRight(QTreeWidgetItem *item);

2 s: a% m8 K2 m3 R

    virtualbool 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 b

    voidaddPrevSlibing();

//添加節點

    voidrefreshSignals();

//重新刷一邊使能信號

protected:


    voiddragEnterEvent(QDragEnterEvent *event);

: ?$ _5 N$ ]2 H* Q% d7 e

    voiddropEvent(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$ P

    voidsigMoveLeftEnabled(bool);

# `1 z- ~0 p/ N" {* o8 N. _3 R

    voidsigMoveRightEnabled(bool);

+ O$ |, \4 b# g0 L3 c* r( u' t

    voidsigAddChildEnabled(bool);

; v/ u7 M- e# f7 A5 |) g: ]: a& t6 b$ d

    voidsigAddNextSlibingEnabled(bool);

8 {. y. [  y/ V" s; P$ _

    voidsigAddPrevSlibingEnabled(bool);

//操作的使能信號

protected:

# \' C# n, J+ Q

    TreeOpPrivate* const d_ptr;

8 t5 F; Z4 ^* y( G

private:

  u* I+ [5 a& I

   Q_DECLARE_PRIVATE(TreeOp);


};

; G  N& Z2 @) w' {7 Q
1 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這個動作。

5 @' f% s1 W$ p, I8 c( q3 @2 Z


我們有新添加了一個文件叫做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 h

classTreeOpWidgetPrivate;

5 _- `$ S9 w3 P: p

classTreeOp;

# O4 E/ Z3 J' u. r2 Z8 q. B1 g% r' q

classTreeOpWidget : 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+ i

    TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);

( R+ W, r0 p. T( |+ _( v* u

    virtual~TreeOpWidget();

4 {4 T4 H% X- F

    voidshowToolbar(bool);


    TreeOp*treeWidget();


protected:

6 e& L8 i! x' e; i2 t

    TreeOpWidgetPrivate* 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,請參看代碼:

( U6 x/ Y& _; u9 @$ D' C
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- k

classTreeOpWidget : public QWidget

6 ?' L( _; W" {9 A

{


    Q_OBJECT

6 i) U- h. R  `( l  N, g

public:


    TreeOpWidget(TreeOp*tree = NULL,QWidget *parent = NULL);


    virtual~TreeOpWidget();

+ c. Q4 `; D. N4 K+ M! }% L

    voidshowToolbar(bool);


    TreeOp*treeWidget();

, E3 G0 w& b4 t' M$ u

protected:

' L) b4 E3 ?) b

    TreeOpWidgetPrivate* 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! F

   Q_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! Q

classBookmarksTreeItem;


classBookmarkWidgetPrivate;


classTreeOpWidget;


classBookmarkWidget : public TreeOpWidget


{


    Q_OBJECT


public:


    BookmarkWidget(QWidget*parent = NULL);


    virtual~BookmarkWidget();

protected:
 BookmarkWidgetPrivate* const d_ptr;

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM