標籤:c++ 介面 封裝 編譯速度
本專欄文章列表
一、何為物件導向
二、C語言也能實現物件導向
三、C++中的不優雅特性
四、解決封裝,避免介面
五、合理使用模板,避免代碼冗餘
六、C++也能反射
七、單例模式解決靜態成員對象和全域對象的構造順序難題
八、更為進階的前置處理器PHP
四、解決封裝,避免介面
恩,今天我們來討論,如何通過設計,解決C++中的不優雅特性,改進項目的結構,改善編譯速度。
上次我們提到,如果一個類的封裝不好,容易導致種種不便,那麼如何設計能夠避免這種現象呢?
class test {public: void print() { printf("Hello\n"); } void print2() { printf("K : %d\n", k); }private: int k;};
簡要的改進,將函數的實現移動到類cpp實現檔案中
最簡單的想法就是將實現和聲明分開,這也是C++提倡的,這樣雖然檔案會增多,但編譯速度和代碼的清晰度會提升。
/* test.h */class test {public: void print(); void print2();private: int k;};
/* test.cpp */#include "test.h"void test::print() { printf("Hello\n");}void test::print2() { printf("K : %d\n", k);}
很明顯的,這樣我們改動cpp檔案中,.h檔案不會受到影響,但假若我的private方法增加了,那麼還是需要改動.h檔案,進而會影響所有引用我的部分,為了避免這種情況出現,有什麼好設計方法嗎?
使用介面降低代碼耦合
一種標準的設計模式是使用介面,這在很多庫的設計時也被經常採用,核心思想是通過多態調用的方式,避免內部方法的暴露。
介面一般就是C++的多態類:
/* Itest.h */class Itest {public: virtual void print() = 0; virtual void print2() = 0;};extern ITest* createItest(); // 類似工廠的方式為你構建類
/* Itest.cpp */#include "test.h"ITest* createItest() { return new test();}
讓test從這個介面繼承出來:
/* test.h */class test : public Itest {public: virtual void print(); virtual void print2();private: int k;};
這樣的好處當然十分明顯了,將類轉成介面的形式,就能方便的修改下面的實作類別,無論實作類別如何改動,都在模組範圍內,介面不變。
但這樣做的壞處也很明顯,如果C++大量使用這樣的方式實現內部封裝,那麼很多情況下效率比較低,而且代碼複雜度就上來了,需要添加很多的介面類。
輕便的成員類封裝
下面介紹一種簡單的方式來實作類別封裝性的提升,首先還是看這個test類,我們將其提示為test2:
/* test2.h */class test2 {public: void print(); void print2();private: int k;};
這裡的k實際上並不需要寫在這裡,我們需要的是將private的部分整體的封裝成一個類:
/* test2.h */class test2_private;class test2 {public: test2(); test2(int); ~test2(); void print(); void print2();protected: test2_private* that;};
/* test2.cpp */#include "test2.h"class test2_private {public: int k;};test2::test2() { that = new test2_private(); that->k = 0;}test2::test2(int k) { that = new test2_private(); that->k = k;}test2::~test2() { delete that;}
這時我們發現,這種封裝可以很有效解決類的介面不便的問題,而由於只使用了類指標,所以我們並不需要前向聲明這個私人類,於是這個類可以方便的被修改,從而避免了介面和多態調用的問題。
這種設計還有一個用途,假若你有另外的代碼產生器產生的程式碼,需要和已有的類嵌入使用,那麼推薦使用這種方式,Qt中就是這樣做的:
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>namespace Ui {class MainWindow;}class MainWindow : public QMainWindow{ Q_OBJECTpublic: explicit MainWindow(QWidget *parent = 0); ~MainWindow();private: Ui::MainWindow *ui;};#endif // MAINWINDOW_H
#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){ ui->setupUi(this);}MainWindow::~MainWindow(){ delete ui;}
我們發現這裡有一個神奇的代碼
namespace Ui {class MainWindow;}
其實這隻是另外一個類,和本類並不同名,Ui::MainWindow是qt設計器幫忙產生的類,用來標註UI介面產生的一些代碼,為了讓代碼很好的和我們自己的類統一起來,他們用了這種方式。
C和C++的物件導向專題(4)——解決封裝,避免介面