1. 程式人生 > >C++的流設計很糟糕

C++的流設計很糟糕

最近需要提供一個功能,採用類似C++流輸出的格式輸出一些日誌資訊, 例如Log(FATAL) << "log to" .

我找了兩個類似專案來研究,google的gloglog4cpp, 它們都支援以C++流輸出格式進行輸出.

但是研究到最後,我發現最大的問題是, 如果按照C++的流輸出格式進行輸出, 將無法判定需要輸出的資訊到哪裡是結束.比如log << "hello " << "world",是無法判斷到底在輸出"hello"還是"world"的時候上面的引數輸入已經結束了.上面兩個專案中, 解決這個問題的辦法大致是相同的,以下面可編譯執行程式碼為例說明它們的做法(在linux g++下面編譯通過):
#include <iostream>
#include 
<sstream
>

#ifdef __DEPRECATED
// Make GCC quiet. # undef __DEPRECATED
 # include 
<strstream>
 # define __DEPRECATED
#else
 # include 
<strstream>#endifusingnamespace std;

class LoggerStream : public std::ostrstream {
 
public:
  LoggerStream(
char* buf, int len)
   : ostrstream(buf, len),
    buf_(buf),
    len_(len) {
  }

  
~LoggerStream() {
    
// do the real fucking output    cout << buf_;
  }

 
private:
  
char*buf_;
  
int len_;
};

int main() {
  
char buf[100= {'\0'};

  LoggerStream(buf, 
sizeof(buf)) <<1<<" hello world\n";

  cout 
<<"buf = "<< buf << endl;

  
return0;
}

在上面的程式碼中, 開始進行輸出的時候首先初始化一個LoggerStream物件, 而在輸出引數輸入完畢的時候將呼叫它的解構函式,在這個解構函式中才完成真正的輸出動作.也就是說,由於對輸入引數結束位置判斷手段的缺失,C++中不得不採用這個手段在解構函式中完成最終的輸出工作.
這樣的做法,最大的問題是,頻繁的構造/析構開銷大,而且每個"<<"操作符背後又需要呼叫ostream的operator<<,也就是假如你的輸入引數有三個將呼叫operator <<三次(當然是經過過載的,不一定都是同一個operator<<),因此,假如需要考慮多執行緒的話,那麼一次輸入有多個函式函式中被呼叫,仍然是問題.天,要使用這門語言寫出正確的程式來,需要了解底下多少的細節呢?!

最後,我向專案組反映這個問題,一致同意以C中類似sprintf可變引數的形式實現這個功能.可變引數解決這個問題,就我的感覺而言,就是輸入引數的時候,稍顯複雜,需要使用者指定輸入的格式.然而,其實這個做法也有好處:作為函式的使用者,你必須明確的知道你在做什麼並且反饋給你所使用的函式.明確的,無歧義的使用函式,而不是依靠所謂函式過載猜你的用意,我想也是避免問題的一個手段.gcc中, 提供了對可變引數檢查的機制,見
這裡
.