程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C和C++的面向對象專題(3)——C++中的不優雅特性

C和C++的面向對象專題(3)——C++中的不優雅特性

編輯:關於C++

本專欄文章列表

一、何為面向對象

二、C語言也能實現面向對象

三、C++中的不優雅特性

四、解決封裝,避免接口

五、合理使用模板,避免代碼冗余

六、C++也能反射

七、單例模式解決靜態成員對象和全局對象的構造順序難題

八、更為高級的預處理器PHP

三、C++中的不優雅特性

今天來說一說C++中不優雅的一些問題,C++雖然是面向對象的設計語言,但也有很多缺陷和弊病,我們將會討論如何通過良好的設計解決這些問題。

C++編譯緩慢

C++編譯慢已經成為了業界共識,一個大型C++項目甚至要用專用的服務器編譯好久才能完成,Java和.net作為大型開發平台, 卻也沒發現編譯如此緩慢的問題,那麼究竟是什麼,導致了C++編譯難的問題呢?

模板的糾結

C++中模板有個很神奇的問題,就是實現和聲明都必須被使用者引用,這段模板代碼才有效,也就是說,模板是在編譯時展開的代碼生成機制。

我們不妨做個實驗,這是類的聲明:

template
class CObject
{
public:
    CObject(T k) {obj = k;}
    ~CObject() {}
    T getObj();
private:
    T obj;
};

下面是類的實現:

#include "CObject.h"

template
T CObject::getObj(){
    return this->obj;
}

主函數中調用:

#include 
#include "CObject.h"

using namespace std;

int main(){
    CObject Obj(10);
    int k = Obj.getObj();
    printf("%d\n", k);
    return 0;
}

一切看起來是那麼的順利,但是!我的電腦給我顯示如下錯誤信息:

Scanning dependencies of target template_test
[ 50%] Building CXX object CMakeFiles/template_test.dir/src/CObject.cpp.o
[100%] Building CXX object CMakeFiles/template_test.dir/src/main.cpp.o
Linking CXX executable template_test
CMakeFiles/template_test.dir/src/main.cpp.o:在函數‘main’中:
main.cpp:(.text+0x22):對‘CObject::getObj()’未定義的引用
collect2: error: ld returned 1 exit status
make[2]: *** [template_test] 錯誤 1
make[1]: *** [CMakeFiles/template_test.dir/all] 錯誤 2
make: *** [all] 錯誤 2

鏈接器告訴我,我們找不到一個叫做‘CObject::getObj()’的函數,恩?為何,我們不是將類實現鏈接進來了麼?

如果你這樣想就錯了,上網查找解決方案,得到的回復居然是這樣:
#include "CObject.h" => #include "CObject.cpp"

omg,那我還不如把兩個文件寫成一個hpp來的方便呢,其實C++也是推薦你這樣做的,理由就是——模板是編譯時,在用到的時候進行代碼展開得到的
如果不這樣做,鏈接器是不會找到對應的代碼的。

那麼也找到了很多大型工程如boost庫,為何編譯緩慢的直接原因,大量的模板展開消耗了巨大的資源,而且模板展開是很不利於代碼復用的,同樣的算法,換一種類型,必須全部編譯,生成新的代碼,並且這類模板生成的代碼,不能提前編譯成二進制庫,這樣的結果就是,項目哪裡改動一點,好多文件重復編譯,造成編譯十分緩慢。

封裝的問題

C++的類並沒有很好的將代碼封起來,這和上次講到的GObject對比可以發現,C++的私有變量是一同放置在類的聲明中,而我們知道,一個類的聲明,是會被很多其他類引用的。
那麼,思考我們的C++編譯過程,很多類都引用了一個.h文件,那麼這個.h文件一旦發生更改,那麼所有引用這個文件的cpp文件都將被觸發重復編譯,而我們在實現一個類時,對類的成員函數小修小補是很平常的,但由於封裝的不徹底,那麼我們的項目又將被反復編譯,帶來編譯的速度緩慢。

而且,如果是庫的話,那麼私有成員的更新甚至還會影響用戶使用,非常麻煩。
例如下面這段代碼:

class Test {
public:
    Test();
    ~Test();

    void Show();

private:
    std::string message;
    int pointer;
    void formatMessage(std::string&);
};

很明顯,一般的C++類,私有成員都會比公開成員多,那麼私有成員修改一點,哪怕只是一不小心多了個空格,都會帶來這個文件的更新,觸發makefile的重編譯,帶來了低效率。

缺乏反射機制

最新的C++11,引入了眾多的新特性,包括好用的auto關鍵字以及模板元編程特性等,但這些,還是不能彌補反射機制缺失帶來的影響。反射是對象串行化、GUI界面事件響應和根據數據動態調用代碼等技術的核心,缺乏反射機制,會使得C++很多地方十分的不便。

很多大型軟件,如firefox,在實現中,往往搭建了反射框架,供系統使用。但由於C++本身語法的問題,缺乏反射依舊會使得類書寫變得困難。

跨平台困難

C++的跨平台性真的不好,甚至很多編譯器上都會出現匪夷所思的問題,例如在不同平台上,基本類型的大小會隨CPU字長而變化,如果有跨平台需求的軟件,最好使用跨平台定義的類型。
C++的結構體中數據往往有內存對齊的問題,有些編譯器還能通過編譯器指令對其設置,這些問題最好還是能避開就避開。

跨平台時,還應小心異常處理的代碼,因為有些版本的C++編譯器對拋出的異常規格並不很遵守規范。
另外,不同平台的寬字符集也是大問題,往往並不能輕松統一,另外MinGW裡貌似就沒有寬字符- -

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved