• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Creative Commons License
            本Blog采用 知識共享署名-非商業(yè)性使用-禁止演繹 3.0 Unported許可協(xié)議 進行許可。 —— Fox <游戲人生>

            游戲人生

            游戲人生 != ( 人生 == 游戲 )
            站點遷移至:http://www.yulefox.com。請訂閱本博的朋友將RSS修改為http://feeds.feedburner.com/yulefox
            posts - 62, comments - 508, trackbacks - 0, articles - 7

            原文地址:

            • 注釋

            注釋雖然寫起來很痛苦,但對保證代碼可讀性至為重要,下面的規(guī)則描述了應該注釋什么、注釋在哪兒。當然也要記住,注釋的確很重要,但最好的代碼本身就是文檔(self-documenting),類型和變量命名意義明確要比通過注釋解釋模糊的命名好得多。

            注釋是為別人(下一個需要理解你的代碼的人)而寫的,認真點吧,那下一個人可能就是你!

            1. 注釋風格(Comment Style)

            使用///* */,統(tǒng)一就好。

            ///* */都可以,//只是用的更加廣泛,在如何注釋和注釋風格上確保統(tǒng)一。

            2. 文件注釋(File Comments)

            在每一個文件開頭加入版權公告,然后是文件內(nèi)容描述。

            法律公告和作者信息

            每一文件包含以下項,依次是:

            1) 版權(copyright statement):如Copyright 2008 Google Inc.

            2) 許可版本(license boilerplate):為項目選擇合適的許可證版本,如Apache 2.0BSDLGPLGPL

            3) 作者(author line):標識文件的原始作者。

            如果你對其他人創(chuàng)建的文件做了重大修改,將你的信息添加到作者信息里,這樣當其他人對該文件有疑問時可以知道該聯(lián)系誰。

            文件內(nèi)容

            每一個文件版權許可及作者信息后,都要對文件內(nèi)容進行注釋說明。

            通常,.h文件要對所聲明的類的功能和用法作簡單說明,.cc文件包含了更多的實現(xiàn)細節(jié)或算法討論,如果你感覺這些實現(xiàn)細節(jié)或算法討論對于閱讀有幫助,可以把.cc中的注釋放到.h中,并在.cc中指出文檔在.h中。

            不要單純在.h.cc間復制注釋,復制的注釋偏離了實際意義。

            3. 類注釋(Class Comments)

            每個類的定義要附著描述類的功能和用法的注釋。

            // Iterates over the contents of a GargantuanTable.  Sample usage:
            //    GargantuanTable_Iterator* iter = table->NewIterator();
            //    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
            //      process(iter->key(), iter->value());
            //    }
            //    delete iter;
            class GargantuanTable_Iterator {
              ...
            };
            
            

            如果你覺得已經(jīng)在文件頂部詳細描述了該類,想直接簡單的來上一句“完整描述見文件頂部”的話,還是多少在類中加點注釋吧。

            如果類有任何同步前提(synchronization assumptions),文檔說明之。如果該類的實例可被多線程訪問,使用時務必注意文檔說明。

            4. 函數(shù)注釋(Function Comments)

            函數(shù)聲明處注釋描述函數(shù)功能,定義處描述函數(shù)實現(xiàn)。

            函數(shù)聲明

            注釋于聲明之前,描述函數(shù)功能及用法,注釋使用描述式("Opens the file")而非指令式("Open the file");注釋只是為了描述函數(shù)而不是告訴函數(shù)做什么。通常,注釋不會描述函數(shù)如何實現(xiàn),那是定義部分的事情。

            函數(shù)聲明處注釋的內(nèi)容:

            1) inputs(輸入)outputs(輸出)

            2) 對類成員函數(shù)而言:函數(shù)調(diào)用期間對象是否需要保持引用參數(shù),是否會釋放這些參數(shù);

            3) 如果函數(shù)分配了空間,需要由調(diào)用者釋放;

            4) 參數(shù)是否可以為NULL

            5) 是否存在函數(shù)使用的性能隱憂(performance implications)

            6) 如果函數(shù)是可重入的(re-entrant),其同步前提(synchronization assumptions)是什么?

            舉例如下:

            // Returns an iterator for this table.  It is the client's
            // responsibility to delete the iterator when it is done with it,
            // and it must not use the iterator once the GargantuanTable object
            // on which the iterator was created has been deleted.
            //
            // The iterator is initially positioned at the beginning of the table.
            //
            // This method is equivalent to:
            //    Iterator* iter = table->NewIterator();
            //    iter->Seek("");
            //    return iter;
            // If you are going to immediately seek to another place in the
            // returned iterator, it will be faster to use NewIterator()
            // and avoid the extra seek.
            Iterator* GetIterator() const;
            
            

            但不要有無謂冗余或顯而易見的注釋,下面的注釋就沒有必要加上“returns false otherwise”,因為已經(jīng)暗含其中了:

            // Returns true if the table cannot hold any more entries.
            bool IsTableFull();
            
            

            注釋構(gòu)造/析構(gòu)函數(shù)時,記住,讀代碼的人知道構(gòu)造/析構(gòu)函數(shù)是什么,所以“destroys this object”這樣的注釋是沒有意義的。說明構(gòu)造函數(shù)對參數(shù)做了什么(例如,是否是指針的所有者)以及析構(gòu)函數(shù)清理了什么,如果都是無關緊要的內(nèi)容,直接省掉注釋,析構(gòu)函數(shù)前沒有注釋是很正常的。

            函數(shù)定義

            每個函數(shù)定義時要以注釋說明函數(shù)功能和實現(xiàn)要點,如使用的漂亮代碼、實現(xiàn)的簡要步驟、如此實現(xiàn)的理由、為什么前半部分要加鎖而后半部分不需要。

            不要從.h文件或其他地方的函數(shù)聲明處直接復制注釋,簡要說明函數(shù)功能是可以的,但重點要放在如何實現(xiàn)上。

            5. 變量注釋(Variable Comments)

            通常變量名本身足以很好說明變量用途,特定情況下,需要額外注釋說明。

            類數(shù)據(jù)成員

            每個類數(shù)據(jù)成員(也叫實例變量或成員變量)應注釋說明用途,如果變量可以接受NULL或-1等警戒值(sentinel values),須說明之,如:

            private:
             // Keeps track of the total number of entries in the table.
             // Used to ensure we do not go over the limit. -1 means
             // that we don't yet know how many entries the table has.
             int num_total_entries_;
            
            

            全局變量(常量)

            和數(shù)據(jù)成員相似,所有全局變量(常量)也應注釋說明含義及用途,如:

            // The total number of tests cases that we run through in this regression test.
            const int kNumTestCases = 6;
            
            

            6. 實現(xiàn)注釋(Implementation Comments)

            對于實現(xiàn)代碼中巧妙的、晦澀的、有趣的、重要的地方加以注釋。

            代碼前注釋

            出彩的或復雜的代碼塊前要加注釋,如:

            // Divide result by two, taking into account that x
            // contains the carry from the add.
            for (int i = 0; i < result->size(); i++) {
              x = (x << 8) + (*result)[i];
              (*result)[i] = x >> 1;
              x &= 1;
            }
            
            

            行注釋

            比較隱晦的地方要在行尾加入注釋,可以在代碼之后空兩格加行尾注釋,如:

            // If we have enough memory, mmap the data portion too.
            mmap_budget = max<int64>(0, mmap_budget - index_->length());
            if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
              return;  // Error already logged.
            
            

            注意,有兩塊注釋描述這段代碼,當函數(shù)返回時注釋提及錯誤已經(jīng)被記入日志。

            前后相鄰幾行都有注釋,可以適當調(diào)整使之可讀性更好:

            ...
            DoSomething();                  // Comment here so the comments line up.
            DoSomethingElseThatIsLonger();  // Comment here so there are two spaces between
                                            // the code and the comment.
            ...
            
            

            NULL、true/false、1、2、3……

            向函數(shù)傳入、布爾值或整數(shù)時,要注釋說明含義,或使用常量讓代碼望文知意,比較一下:

            bool success = CalculateSomething(interesting_value,
                                              10,
                                              false,
                                              NULL);  // What are these arguments??
            
            

            和:

            bool success = CalculateSomething(interesting_value,
                                              10,     // Default base value.
                                              false,  // Not the first time we're calling this.
                                              NULL);  // No callback.
            
            

            使用常量或描述性變量:

            const int kDefaultBaseValue = 10;
            const bool kFirstTimeCalling = false;
            Callback *null_callback = NULL;
            bool success = CalculateSomething(interesting_value,
                                              kDefaultBaseValue,
                                              kFirstTimeCalling,
                                              null_callback);
            
            

            不要

            注意永遠不要用自然語言翻譯代碼作為注釋,要假設讀你代碼的人C++比你強:D:

            // Now go through the b array and make sure that if i occurs,
            // the next element is i+1.
            ...        // Geez.  What a useless comment.
            
            

            7. 標點、拼寫和語法(Punctuation, Spelling and Grammar)

            留意標點、拼寫和語法,寫的好的注釋比差的要易讀的多。

            注釋一般是包含適當大寫和句點(.)的完整的句子,短一點的注釋(如代碼行尾的注釋)可以隨意點,依然要注意風格的一致性。完整的句子可讀性更好,也可以說明該注釋是完整的而不是一點不成熟的想法。

            雖然被別人指出該用分號(semicolon)的時候用了逗號(comma)有點尷尬。清晰易讀的代碼還是很重要的,適當?shù)臉它c、拼寫和語法對此會有所幫助。

            8. TODO注釋(TODO Comments)

            對那些臨時的、短期的解決方案,或已經(jīng)夠好但并不完美的代碼使用TODO注釋。

            這樣的注釋要使用全大寫的字符串TODO,后面括號(parentheses)里加上你的大名、郵件地址等,還可以加上冒號(colon):目的是可以根據(jù)統(tǒng)一的TODO格式進行查找:

            // TODO(kl@gmail.com): Use a "*" here for concatenation operator.
            // TODO(Zeke) change this to use relations.
            
            

            如果加上是為了在“將來某一天做某事”,可以加上一個特定的時間("Fix by November 2005")或事件("Remove this code when all clients can handle XML responses.")。

            ______________________________________

            譯者:注釋也是比較人性化的約定了:

            1. 關于注釋風格,很多C++的coders更喜歡行注釋,C coders或許對塊注釋依然情有獨鐘,或者在文件頭大段大段的注釋時使用塊注釋;

            2. 文件注釋可以炫耀你的成就,也是為了捅了簍子別人可以找你;

            3. 注釋要言簡意賅,不要拖沓冗余,復雜的東西簡單化和簡單的東西復雜化都是要被鄙視的;

            4. 對于Chinese coders來說,用英文注釋還是用中文注釋,it is a problem,但不管怎樣,注釋是為了讓別人看懂,難道是為了炫耀編程語言之外的你的母語或外語水平嗎;

            5. 注釋不要太亂,適當?shù)目s進才會讓人樂意看,但也沒有必要規(guī)定注釋從第幾列開始(我自己寫代碼的時候總喜歡這樣),UNIX/LINUX下還可以約定是使用tab還是space,個人傾向于space;

            6. TODO很不錯,有時候,注釋確實是為了標記一些未完成的或完成的不盡如人意的地方,這樣一搜索,就知道還有哪些活要干,日志都省了。

            posted @ 2008-07-22 17:02 Fox 閱讀(3695) | 評論 (4)編輯 收藏

            原文地址:

            • 命名約定

            最重要的一致性規(guī)則是命名管理,命名風格直接可以直接確定命名實體是:類型、變量、函數(shù)、常量、宏等等,無需查找實體聲明,我們大腦中的模式匹配引擎依賴于這些命名規(guī)則。

            命名規(guī)則具有一定隨意性,但相比按個人喜好命名,一致性更重要,所以不管你怎么想,規(guī)則總歸是規(guī)則。

            1. 通用命名規(guī)則(General Naming Rules)

            函數(shù)命名、變量命名、文件命名應具有描述性,不要過度縮寫,類型和變量應該是名詞,函數(shù)名可以用“命令性”動詞。

            如何命名

            盡可能給出描述性名稱,不要節(jié)約空間,讓別人很快理解你的代碼更重要,好的命名選擇:

            int num_errors;                  // Good.
            int num_completed_connections;   // Good.

            丑陋的命名使用模糊的縮寫或隨意的字符:

            int n;                           // Bad - meaningless.
            int nerr;                        // Bad - ambiguous abbreviation.
            int n_comp_conns;                // Bad - ambiguous abbreviation.

            類型和變量名一般為名詞:如FileOpenernum_errors

            函數(shù)名通常是指令性的,如OpenFile()set_num_errors(),訪問函數(shù)需要描述的更細致,要與其訪問的變量相吻合。

            縮寫

            除非放到項目外也非常明了,否則不要使用縮寫,例如:

            // Good
            // These show proper names with no abbreviations.
            int num_dns_connections;  // Most people know what "DNS" stands for.
            int price_count_reader;   // OK, price count. Makes sense.
             
            // Bad!
            // Abbreviations can be confusing or ambiguous outside a small group.
            int wgc_connections;  // Only your group knows what this stands for.
            int pc_reader;        // Lots of things can be abbreviated "pc".

            不要用省略字母的縮寫:

            int error_count;  // Good.
            int error_cnt;    // Bad.

            2. 文件命名(File Names)

            文件名要全部小寫,可以包含下劃線(_)或短線(-),按項目約定來。

            可接受的文件命名:

            my_useful_class.cc
            my-useful-class.cc
            myusefulclass.cc

            C++文件以.cc結(jié)尾,頭文件以.h結(jié)尾。

            不要使用已經(jīng)存在于/usr/include下的文件名(譯者注,對UNIX、Linux等系統(tǒng)而言),如db.h

            通常,盡量讓文件名更加明確,http_server_logs.h就比logs.h要好,定義類時文件名一般成對出現(xiàn),如foo_bar.hfoo_bar.cc,對應類FooBar

            內(nèi)聯(lián)函數(shù)必須放在.h文件中,如果內(nèi)聯(lián)函數(shù)比較短,就直接放在.h中。如果代碼比較長,可以放到以-inl.h結(jié)尾的文件中。對于包含大量內(nèi)聯(lián)代碼的類,可以有三個文件:

            url_table.h      // The class declaration.
            url_table.cc     // The class definition.
            url_table-inl.h  // Inline functions that include lots of code.

            參考第一篇-inl.h文件一節(jié)。

            3. 類型命名(Type Names)

            類型命名每個單詞以大寫字母開頭,不包含下劃線:MyExcitingClassMyExcitingEnum

            所有類型命名——類、結(jié)構(gòu)體、類型定義(typedef)、枚舉——使用相同約定,例如:

            // classes and structs
            class UrlTable { ... 
            class UrlTableTester { ... 
            struct UrlTableProperties { ...
            
            // typedefs
            typedef hash_map<UrlTableProperties *, string> PropertiesMap;
            
            // enums
            enum UrlTableErrors { ...

            4. 變量命名(Variable Names)

            變量名一律小寫,單詞間以下劃線相連,類的成員變量以下劃線結(jié)尾,如my_exciting_local_variablemy_exciting_member_variable_

            普通變量命名

            舉例:

            string table_name;  // OK - uses underscore.
            string tablename;   // OK - all lowercase.
            string tableName;   // Bad - mixed case.

            類數(shù)據(jù)成員

            結(jié)構(gòu)體的數(shù)據(jù)成員可以和普通變量一樣,不用像類那樣接下劃線:

            struct UrlTableProperties {
              string name;
              int num_entries;
            }

            結(jié)構(gòu)體與類的討論參考第三篇結(jié)構(gòu)體vs.類一節(jié)。

            全局變量

            對全局變量沒有特別要求,少用就好,可以以g_或其他易與局部變量區(qū)分的標志為前綴。

            5. 常量命名(Constant Names)

            在名稱前加kkDaysInAWeek

            所有編譯時常量(無論是局部的、全局的還是類中的)和其他變量保持些許區(qū)別,k后接大寫字母開頭的單詞:

            const int kDaysInAWeek = 7;

            6. 函數(shù)命名(Function Names)

            普通函數(shù)(regular functions,譯者注,這里與訪問函數(shù)等特殊函數(shù)相對)大小寫混合,存取函數(shù)(accessors and mutators)則要求與變量名匹配:MyExcitingFunction()MyExcitingMethod()my_exciting_member_variable()set_my_exciting_member_variable()

            普通函數(shù)

            函數(shù)名以大寫字母開頭,每個單詞首字母大寫,沒有下劃線:

            AddTableEntry()
            DeleteUrl()

            存取函數(shù)

            存取函數(shù)要與存取的變量名匹配,這兒摘錄一個擁有實例變量num_entries_的類:

            class MyClass {
             public:
              ...
              int num_entries() const { return num_entries_; }
              void set_num_entries(int num_entries) { num_entries_ = num_entries; }
            
             private:
              int num_entries_;
            };

            其他短小的內(nèi)聯(lián)函數(shù)名也可以使用小寫字母,例如,在循環(huán)中調(diào)用這樣的函數(shù)甚至都不需要緩存其值,小寫命名就可以接受。

            譯者注:從這一點上可以看出,小寫的函數(shù)名意味著可以直接內(nèi)聯(lián)使用。

            7. 命名空間(Namespace Names)

            命名空間的名稱是全小寫的,其命名基于項目名稱和目錄結(jié)構(gòu):google_awesome_project

            關于命名空間的討論和如何命名,參考第二篇命名空間

            8. 枚舉命名(Enumerator Names)

            枚舉值應全部大寫,單詞間以下劃線相連:MY_EXCITING_ENUM_VALUE

            枚舉名稱屬于類型,因此大小寫混合:UrlTableErrors

            enum UrlTableErrors {
              OK = 0,
              ERROR_OUT_OF_MEMORY,
              ERROR_MALFORMED_INPUT,
            };

            9. 宏命名(Macro Names)

            你并不打算使用宏,對吧?如果使用,像這樣:MY_MACRO_THAT_SCARES_SMALL_CHILDREN

            參考第四篇預處理宏,通常是不使用宏的,如果絕對要用,其命名像枚舉命名一樣全部大寫、使用下劃線:

            #define ROUND(x) ...
            #define PI_ROUNDED 3.0
            MY_EXCITING_ENUM_VALUE

            10. 命名規(guī)則例外(Exceptions to Naming Rules)

            當命名與現(xiàn)有C/C++實體相似的對象時,可參考現(xiàn)有命名約定:

            bigopen()
            函數(shù)名,參考open()
            uint
            typedef類型定義
            bigpos
            structclass,參考pos
            sparse_hash_map
            STL相似實體;參考STL命名約定
            LONGLONG_MAX
            常量,類似INT_MAX

            ______________________________________

            譯者:命名約定就相對輕松許多,在遵從代碼一致性、可讀性的前提下,略顯隨意:

            1. 總體規(guī)則:不要隨意縮寫,如果說ChangeLocalValue寫作ChgLocVal還有情可原的話,把ModifyPlayerName寫作MdfPlyNm就太過分了,除函數(shù)名可適當為動詞外,其他命名盡量使用清晰易懂的名詞;

            2. 宏、枚舉等使用全部大寫+下劃線;

            3. 變量(含類、結(jié)構(gòu)體成員變量)、文件、命名空間、存取函數(shù)等使用全部小寫+下劃線,類成員變量以下劃線結(jié)尾,全局變量以g_開頭;

            4. 普通函數(shù)、類型(含類與結(jié)構(gòu)體、枚舉類型)、常量等使用大小寫混合,不含下劃線;

            5. 參考現(xiàn)有或相近命名約定。

            posted @ 2008-07-22 11:59 Fox 閱讀(4195) | 評論 (3)編輯 收藏

            原文地址:

            • Google特有的風情

            Google有很多自己實現(xiàn)的使C++代碼更加健壯的技巧、功能,以及有異于別處的C++的使用方式。

            1. 智能指針(Smart Pointers)

            如果確實需要使用智能指針的話,scoped_ptr完全可以勝任。在非常特殊的情況下,例如對STL容器中對象,你應該只使用std::tr1::shared_ptr,任何情況下都不要使用auto_ptr。

            “智能”指針看上去是指針,其實是附加了語義的對象。以scoped_ptr為例,scoped_ptr被銷毀時,刪除了它所指向的對象。shared_ptr也是如此,而且,shared_ptr實現(xiàn)了引用計數(shù)(reference-counting),從而只有當它所指向的最后一個對象被銷毀時,指針才會被刪除。

            一般來說,我們傾向于設計對象隸屬明確的代碼,最明確的對象隸屬是根本不使用指針,直接將對象作為一個域(field)或局部變量使用。另一種極端是引用計數(shù)指針不屬于任何對象,這樣設計的問題是容易導致循環(huán)引用或其他導致對象無法刪除的詭異條件,而且在每一次拷貝或賦值時連原子操作都會很慢。

            雖然不推薦這么做,但有些時候,引用計數(shù)指針是最簡單有效的解決方案。

            譯者注:看來,Google所謂的不同之處,在于盡量避免使用智能指針:D,使用時也盡量局部化,并且,安全第一。

            • 其他C++特性

            1. 引用參數(shù)(Reference Arguments)

            所以按引用傳遞的參數(shù)必須加上const。

            定義:在C語言中,如果函數(shù)需要修改變量的值,形參(parameter)必須為指針,如int foo(int *pval)。在C++中,函數(shù)還可以聲明引用形參:int foo(int &val)。

            優(yōu)點:定義形參為引用避免了像(*pval)++這樣丑陋的代碼,像拷貝構(gòu)造函數(shù)這樣的應用也是必需的,而且不像指針那樣不接受空指針NULL。

            缺點:容易引起誤解,因為引用在語法上是值卻擁有指針的語義。

            結(jié)論:

            函數(shù)形參表中,所有引用必須是const:

            void Foo(const string &in, string *out);

            事實上這是一個硬性約定:輸入?yún)?shù)為值或常數(shù)引用,輸出參數(shù)為指針;輸入?yún)?shù)可以是常數(shù)指針,但不能使用非常數(shù)引用形參。

            在強調(diào)參數(shù)不是拷貝而來,在對象生命期內(nèi)必須一直存在時可以使用常數(shù)指針,最好將這些在注釋中詳細說明。bind2nd和mem_fun等STL適配器不接受引用形參,這種情況下也必須以指針形參聲明函數(shù)。

            2. 函數(shù)重載(Function Overloading)

            僅在輸入?yún)?shù)類型不同、功能相同時使用重載函數(shù)(含構(gòu)造函數(shù)),不要使用函數(shù)重載模仿缺省函數(shù)參數(shù)。

            定義:可以定義一個函數(shù)參數(shù)類型為const string&,并定義其重載函數(shù)類型為const char*。

            class MyClass {
            public:
              void Analyze(const string &text);
              void Analyze(const char *text, size_t textlen);
            };

            優(yōu)點:通過重載不同參數(shù)的同名函數(shù),令代碼更加直觀,模板化代碼需要重載,同時為訪問者帶來便利。

            缺點:限制使用重載的一個原因是在特定調(diào)用處很難確定到底調(diào)用的是哪個函數(shù),另一個原因是當派生類只重載函數(shù)的部分變量會令很多人對繼承語義產(chǎn)生困惑。此外在閱讀庫的客戶端代碼時,因缺省函數(shù)參數(shù)造成不必要的費解。

            結(jié)論:如果你想重載一個函數(shù),考慮讓函數(shù)名包含參數(shù)信息,例如,使用AppendString()、AppendInt()而不是Append()。

            3. 缺省參數(shù)(Default Arguments)

            禁止使用缺省函數(shù)參數(shù)。

            優(yōu)點:經(jīng)常用到一個函數(shù)帶有大量缺省值,偶爾會重寫一下這些值,缺省參數(shù)為很少涉及的例外情況提供了少定義一些函數(shù)的方便。

            缺點:大家經(jīng)常會通過查看現(xiàn)有代碼確定如何使用API,缺省參數(shù)使得復制粘貼以前的代碼難以呈現(xiàn)所有參數(shù),當缺省參數(shù)不適用于新代碼時可能導致重大問題。

            結(jié)論:所有參數(shù)必須明確指定,強制程序員考慮API和傳入的各參數(shù)值,避免使用可能不為程序員所知的缺省參數(shù)。

            4. 變長數(shù)組和alloca(Variable-Length Arrays and alloca())

            禁止使用變長數(shù)組和alloca()。

            優(yōu)點:變長數(shù)組具有渾然天成的語法,變長數(shù)組和alloca()也都很高效。

            缺點:變長數(shù)組和alloca()不是標準C++的組成部分,更重要的是,它們在堆棧(stack)上根據(jù)數(shù)據(jù)分配大小可能導致難以發(fā)現(xiàn)的內(nèi)存泄漏:“在我的機器上運行的好好的,到了產(chǎn)品中卻莫名其妙的掛掉了”。

            結(jié)論:

            使用安全的分配器(allocator),如scoped_ptr/scoped_array。

            5. 友元(Friends)

            允許合理使用友元類及友元函數(shù)。

            通常將友元定義在同一文件下,避免讀者跑到其他文件中查找其對某個類私有成員的使用。經(jīng)常用到友元的一個地方是將FooBuilder聲明為Foo的友元,F(xiàn)ooBuilder以便可以正確構(gòu)造Foo的內(nèi)部狀態(tài),而無需將該狀態(tài)暴露出來。某些情況下,將一個單元測試用類聲明為待測類的友元會很方便。

            友元延伸了(但沒有打破)類的封裝界線,當你希望只允許另一個類訪問某個成員時,使用友元通常比將其聲明為public要好得多。當然,大多數(shù)類應該只提供公共成員與其交互。

            6. 異常(Exceptions

            不要使用C++異常。

            優(yōu)點:

            1) 異常允許上層應用決定如何處理在底層嵌套函數(shù)中發(fā)生的“不可能發(fā)生”的失敗,不像出錯代碼的記錄那么模糊費解;

            2) 應用于其他很多現(xiàn)代語言中,引入異常使得C++與Python、Java及其他與C++相近的語言更加兼容;

            3) 許多C++第三方庫使用異常,關閉異常將導致難以與之結(jié)合;

            4) 異常是解決構(gòu)造函數(shù)失敗的唯一方案,雖然可以通過工廠函數(shù)(factory function)或Init()方法模擬異常,但他們分別需要堆分配或新的“非法”狀態(tài);

            5) 在測試框架(testing framework)中,異常確實很好用。

            缺點:

            1) 在現(xiàn)有函數(shù)中添加throw語句時,必須檢查所有調(diào)用處,即使它們至少具有基本的異常安全保護,或者程序正常結(jié)束,永遠不可能捕獲該異常。例如:if f() calls g() calls h()h拋出被f捕獲的異常,g就要當心了,避免沒有完全清理;

            2) 通俗一點說,異常會導致程序控制流(control flow)通過查看代碼無法確定:函數(shù)有可能在不確定的地方返回,從而導致代碼管理和調(diào)試困難,當然,你可以通過規(guī)定何時何地如何使用異常來最小化的降低開銷,卻給開發(fā)人員帶來掌握這些規(guī)定的負擔;

            3) 異常安全需要RAII和不同編碼實踐。輕松、正確編寫異常安全代碼需要大量支撐。允許使用異常;

            4) 加入異常使二進制執(zhí)行代碼體積變大,增加了編譯時長(或許影響不大),還可能增加地址空間壓力;

            5) 異常的實用性可能會刺激開發(fā)人員在不恰當?shù)臅r候拋出異常,或者在不安全的地方從異常中恢復,例如,非法用戶輸入可能導致拋出異常。如果允許使用異常會使得這樣一篇編程風格指南長出很多(譯者注,這個理由有點牽強:-()!

            結(jié)論:

            從表面上看,使用異常利大于弊,尤其是在新項目中,然而,對于現(xiàn)有代碼,引入異常會牽連到所有依賴代碼。如果允許異常在新項目中使用,在跟以前沒有使用異常的代碼整合時也是一個麻煩。因為Google現(xiàn)有的大多數(shù)C++代碼都沒有異常處理,引入帶有異常處理的新代碼相當困難。

            鑒于Google現(xiàn)有代碼不接受異常,在現(xiàn)有代碼中使用異常比在新項目中使用的代價多少要大一點,遷移過程會比較慢,也容易出錯。我們也不相信異常的有效替代方案,如錯誤代碼、斷言等,都是嚴重負擔。

            我們并不是基于哲學或道德層面反對使用異常,而是在實踐的基礎上。因為我們希望使用Google上的開源項目,但項目中使用異常會為此帶來不便,因為我們也建議不要在Google上的開源項目中使用異常,如果我們需要把這些項目推倒重來顯然不太現(xiàn)實。

            對于Windows代碼來說,這一點有個例外(等到最后一篇吧:D)。

            譯者注:對于異常處理,顯然不是短短幾句話能夠說清楚的,以構(gòu)造函數(shù)為例,很多C++書籍上都提到當構(gòu)造失敗時只有異常可以處理,Google禁止使用異常這一點,僅僅是為了自身的方便,說大了,無非是基于軟件管理成本上,實際使用中還是自己決定。

            7. 運行時類型識別(Run-Time Type Information, RTTI

            我們禁止使用RTTI。

            定義:RTTI允許程序員在運行時識別C++類對象的類型。

            優(yōu)點:

            RTTI在某些單元測試中非常有用,如在進行工廠類測試時用于檢驗一個新建對象是否為期望的動態(tài)類型。

            除測試外,極少用到。

            缺點:運行時識別類型意味著設計本身有問題,如果你需要在運行期間確定一個對象的類型,這通常說明你需要重新考慮你的類的設計。

            結(jié)論:

            除單元測試外,不要使用RTTI,如果你發(fā)現(xiàn)需要所寫代碼因?qū)ο箢愋筒煌鴦幼鞲鳟惖脑挘紤]換一種方式識別對象類型。

            虛函數(shù)可以實現(xiàn)隨子類類型不同而執(zhí)行不同代碼,工作都是交給對象本身去完成。

            如果工作在對象之外的代碼中完成,考慮雙重分發(fā)方案,如Visitor模式,可以方便的在對象本身之外確定類的類型。

            如果你認為上面的方法你掌握不了,可以使用RTTI,但務必請三思,不要去手工實現(xiàn)一個貌似RTTI的方案(RTTI-like workaround),我們反對使用RTTI,同樣反對貼上類型標簽的貌似類繼承的替代方案(譯者注,使用就使用吧,不使用也不要造輪子:D)。

            8. 類型轉(zhuǎn)換(Casting

            使用static_cast<>()等C++的類型轉(zhuǎn)換,不要使用int y = (int)xint y = int(x);

            定義:C++引入了有別于C的不同類型的類型轉(zhuǎn)換操作。

            優(yōu)點:C語言的類型轉(zhuǎn)換問題在于操作比較含糊:有時是在做強制轉(zhuǎn)換(如(int)3.5),有時是在做類型轉(zhuǎn)換(如(int)"hello")。另外,C++的類型轉(zhuǎn)換查找更容易、更醒目。

            缺點:語法比較惡心(nasty)

            結(jié)論:使用C++風格而不要使用C風格類型轉(zhuǎn)換。

            1) static_cast:和C風格轉(zhuǎn)換相似可做值的強制轉(zhuǎn)換,或指針的父類到子類的明確的向上轉(zhuǎn)換;

            2) const_cast:移除const屬性;

            3) reinterpret_cast:指針類型和整型或其他指針間不安全的相互轉(zhuǎn)換,僅在你對所做一切了然于心時使用;

            4) dynamic_cast:除測試外不要使用,除單元測試外,如果你需要在運行時確定類型信息,說明設計有缺陷(參考RTTI)。

            9. 流(Streams

            只在記錄日志時使用流。

            定義:流是printf()scanf()的替代。

            優(yōu)點:有了流,在輸出時不需要關心對象的類型,不用擔心格式化字符串與參數(shù)列表不匹配(雖然在gcc中使用printf也不存在這個問題),打開、關閉對應文件時,流可以自動構(gòu)造、析構(gòu)。

            缺點:流使得pread()等功能函數(shù)很難執(zhí)行,如果不使用printf之類的函數(shù)而是使用流很難對格式進行操作(尤其是常用的格式字符串%.*s),流不支持字符串操作符重新定序(%1s),而這一點對國際化很有用。

            結(jié)論:

            不要使用流,除非是日志接口需要,使用printf之類的代替。

            使用流還有很多利弊,代碼一致性勝過一切,不要在代碼中使用流。

            拓展討論:

            對這一條規(guī)則存在一些爭論,這兒給出深層次原因。回憶唯一性原則(Only One Way):我們希望在任何時候都只使用一種確定的I/O類型,使代碼在所有I/O處保持一致。因此,我們不希望用戶來決定是使用流還是printf + read/write,我們應該決定到底用哪一種方式。把日志作為例外是因為流非常適合這么做,也有一定的歷史原因。

            流的支持者們主張流是不二之選,但觀點并不是那么清晰有力,他們所指出流的所有優(yōu)勢也正是其劣勢所在。流最大的優(yōu)勢是在輸出時不需要關心輸出對象的類型,這是一個亮點,也是一個不足:很容易用錯類型,而編譯器不會報警。使用流時容易造成的一類錯誤是:

            cout << this;  // Prints the address
            cout << *this;  // Prints the contents

            編譯器不會報錯,因為<<被重載,就因為這一點我們反對使用操作符重載。

            有人說printf的格式化丑陋不堪、易讀性差,但流也好不到哪兒去。看看下面兩段代碼吧,哪個更加易讀?

            cerr << "Error connecting to '" << foo->bar()->hostname.first
                 << ":" << foo->bar()->hostname.second << ": " << strerror(errno);
            
            fprintf(stderr, "Error connecting to '%s:%u: %s",
                    foo->bar()->hostname.first, foo->bar()->hostname.second,
                    strerror(errno));

            你可能會說,“把流封裝一下就會比較好了”,這兒可以,其他地方呢?而且不要忘了,我們的目標是使語言盡可能小,而不是添加一些別人需要學習的新的內(nèi)容。

            每一種方式都是各有利弊,“沒有最好,只有更好”,簡單化的教條告誡我們必須從中選擇其一,最后的多數(shù)決定是printf + read/write

            10. 前置自增和自減(Preincrement and Predecrement

            對于迭代器和其他模板對象使用前綴形式(++i)的自增、自減運算符。

            定義:對于變量在自增(++ii++)或自減(--ii--)后表達式的值又沒有沒用到的情況下,需要確定到底是使用前置還是后置的自增自減。

            優(yōu)點:不考慮返回值的話,前置自增(++i)通常要比后置自增(i++)效率更高,因為后置的自增自減需要對表達式的值i進行一次拷貝,如果i是迭代器或其他非數(shù)值類型,拷貝的代價是比較大的。既然兩種自增方式動作一樣(譯者注,不考慮表達式的值,相信你知道我在說什么),為什么不直接使用前置自增呢?

            缺點:C語言中,當表達式的值沒有使用時,傳統(tǒng)的做法是使用后置自增,特別是在for循環(huán)中,有些人覺得后置自增更加易懂,因為這很像自然語言,主語(i)在謂語動詞(++)前。

            結(jié)論:對簡單數(shù)值(非對象)來說,兩種都無所謂,對迭代器和模板類型來說,要使用前置自增(自減)。

            11. const的使用(Use of const

            我們強烈建議你在任何可以使用的情況下都要使用const

            定義:在聲明的變量或參數(shù)前加上關鍵字const用于指明變量值不可修改(如const int foo),為類中的函數(shù)加上const限定表明該函數(shù)不會修改類成員變量的狀態(tài)(如class Foo { int Bar(char c) const; };)。

            優(yōu)點:人們更容易理解變量是如何使用的,編輯器可以更好地進行類型檢測、更好地生成代碼。人們對編寫正確的代碼更加自信,因為他們知道所調(diào)用的函數(shù)被限定了能或不能修改變量值。即使是在無鎖的多線程編程中,人們也知道什么樣的函數(shù)是安全的。

            缺點:如果你向一個函數(shù)傳入const變量,函數(shù)原型中也必須是const的(否則變量需要const_cast類型轉(zhuǎn)換),在調(diào)用庫函數(shù)時這尤其是個麻煩。

            結(jié)論const變量、數(shù)據(jù)成員、函數(shù)和參數(shù)為編譯時類型檢測增加了一層保障,更好的盡早發(fā)現(xiàn)錯誤。因此,我們強烈建議在任何可以使用的情況下使用const

            1) 如果函數(shù)不會修改傳入的引用或指針類型的參數(shù),這樣的參數(shù)應該為const

            2) 盡可能將函數(shù)聲明為const,訪問函數(shù)應該總是const,其他函數(shù)如果不會修改任何數(shù)據(jù)成員也應該是const,不要調(diào)用非const函數(shù),不要返回對數(shù)據(jù)成員的非const指針或引用;

            3) 如果數(shù)據(jù)成員在對象構(gòu)造之后不再改變,可將其定義為const

            然而,也不要對const過度使用,像const int * const * const x;就有些過了,即便這樣寫精確描述了x,其實寫成const int** x就可以了。

            關鍵字mutable可以使用,但是在多線程中是不安全的,使用時首先要考慮線程安全。

            const位置

            有人喜歡int const *foo形式不喜歡const int* foo,他們認為前者更加一致因此可讀性更好:遵循了const總位于其描述的對象(int)之后的原則。但是,一致性原則不適用于此,“不要過度使用”的權威抵消了一致性使用。將const放在前面才更易讀,因為在自然語言中形容詞(const)是在名詞(int)之前的。

            這是說,我們提倡const在前,并不是要求,但要兼顧代碼的一致性!

            12. 整型(Integer Types

            C++內(nèi)建整型中,唯一用到的是int,如果程序中需要不同大小的變量,可以使用<stdint.h>中的精確寬度(precise-width)的整型,如int16_t

            定義:C++沒有指定整型的大小,通常人們認為short是16位,int是32位,long是32位,long long是64位。

            優(yōu)點:保持聲明統(tǒng)一。

            缺點:C++中整型大小因編譯器和體系結(jié)構(gòu)的不同而不同。

            結(jié)論

            <stdint.h>定義了int16_tuint32_tint64_t等整型,在需要確定大小的整型時可以使用它們代替shortunsigned long long等,在C整型中,只使用int。適當情況下,推薦使用標準類型如size_tptrdiff_t

            最常使用的是,對整數(shù)來說,通常不會用到太大,如循環(huán)計數(shù)等,可以使用普通的int。你可以認為int至少為32位,但不要認為它會多于32位,需要64位整型的話,可以使用int64_tuint64_t

            對于大整數(shù),使用int64_t

            不要使用uint32_t等無符號整型,除非你是在表示一個位組(bit pattern)而不是一個數(shù)值。即使數(shù)值不會為負值也不要使用無符號類型,使用斷言(assertion,譯者注,這一點很有道理,計算機只會根據(jù)變量、返回值等有無符號確定數(shù)值正負,仍然無法確定對錯)來保護數(shù)據(jù)。

            無符號整型

            有些人,包括一些教科書作者,推薦使用無符號類型表示非負數(shù),類型表明了數(shù)值取值形式。但是,在C語言中,這一優(yōu)點被由其導致的bugs所淹沒。看看:

            for (unsigned int i = foo.Length()-1; i >= 0; --i) ...

            上述代碼永遠不會終止!有時gcc會發(fā)現(xiàn)該bug并報警,但通常不會。類似的bug還會出現(xiàn)在比較有符合變量和無符號變量時,主要是C的類型提升機制(type-promotion scheme,C語言中各種內(nèi)建類型之間的提升轉(zhuǎn)換關系)會致使無符號類型的行為出乎你的意料。

            因此,使用斷言聲明變量為非負數(shù),不要使用無符號型。

            13. 64位下的可移植性(64-bit Portability

            代碼在64位和32位的系統(tǒng)中,原則上應該都比較友好,尤其對于輸出、比較、結(jié)構(gòu)對齊(structure alignment)來說:

            1) printf()指定的一些類型在32位和64位系統(tǒng)上可移植性不是很好,C99標準定義了一些可移植的格式。不幸的是,MSVC 7.1并非全部支持,而且標準中也有所遺漏。所以有時我們就不得不自己定義丑陋的版本(使用標準風格要包含文件inttypes.h):

            // printf macros for size_t, in the style of inttypes.h
            #ifdef _LP64
            #define __PRIS_PREFIX "z"
            #else
            #define __PRIS_PREFIX
            #endif
            
            // Use these macros after a % in a printf format string
            // to get correct 32/64 bit behavior, like this:
            // size_t size = records.size();
            // printf("%"PRIuS"\n", size);
            
            #define PRIdS __PRIS_PREFIX "d"
            #define PRIxS __PRIS_PREFIX "x"
            #define PRIuS __PRIS_PREFIX "u"
            #define PRIXS __PRIS_PREFIX "X"
            #define PRIoS __PRIS_PREFIX "o" 

            類型 不要使用 使用 備注
            void *(或其他指針類型) %lx %p  
            int64_t %qd, %lld %"PRId64"  
            uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"  
            size_t %u %"PRIuS", %"PRIxS" C99指定%zu
            ptrdiff_t %d %"PRIdS" C99指定%zd


            注意宏PRI*會被編譯器擴展為獨立字符串,因此如果使用非常量的格式化字符串,需要將宏的值而不是宏名插入格式中,在使用宏PRI*時同樣可以在%后指定長度等信息。例如,printf("x = %30"PRIuS"\n", x)在32位Linux上將被擴展為printf("x = %30" "u" "\n", x),編譯器會處理為printf("x = %30u\n", x)

            2) 記住sizeof(void *) != sizeof(int),如果需要一個指針大小的整數(shù)要使用intptr_t

            3) 需要對結(jié)構(gòu)對齊加以留心,尤其是對于存儲在磁盤上的結(jié)構(gòu)體。在64位系統(tǒng)中,任何擁有int64_t/uint64_t成員的類/結(jié)構(gòu)體將默認被處理為8字節(jié)對齊。如果32位和64位代碼共用磁盤上的結(jié)構(gòu)體,需要確保兩種體系結(jié)構(gòu)下的結(jié)構(gòu)體的對齊一致。大多數(shù)編譯器提供了調(diào)整結(jié)構(gòu)體對齊的方案。gcc中可使用__attribute__((packed)),MSVC提供了#pragma pack()__declspec(align())(譯者注,解決方案的項目屬性里也可以直接設置)

            4) 創(chuàng)建64位常量時使用LLULL作為后綴,如:

            int64_t my_value = 0x123456789LL;
            uint64_t my_mask = 3ULL << 48;

            5) 如果你確實需要32位和64位系統(tǒng)具有不同代碼,可以在代碼變量前使用。(盡量不要這么做,使用時盡量使修改局部化)。

            14. 預處理宏(Preprocessor Macros

            使用宏時要謹慎,盡量以內(nèi)聯(lián)函數(shù)、枚舉和常量代替之。

            宏意味著你和編譯器看到的代碼是不同的,因此可能導致異常行為,尤其是當宏存在于全局作用域中。

            值得慶幸的是,C++中,宏不像C中那么必要。宏內(nèi)聯(lián)效率關鍵代碼(performance-critical code)可以內(nèi)聯(lián)函數(shù)替代;宏存儲常量可以const變量替代;宏“縮寫”長變量名可以引用替代;使用宏進行條件編譯,這個……,最好不要這么做,會令測試更加痛苦(#define防止頭文件重包含當然是個例外)。

            宏可以做一些其他技術無法實現(xiàn)的事情,在一些代碼庫(尤其是底層庫中)可以看到宏的某些特性(如字符串化(stringifying,譯者注,使用#連接(concatenation,譯者注,使用##等等)。但在使用前,仔細考慮一下能不能不使用宏實現(xiàn)同樣效果。

            譯者注:關于宏的高級應用,可以參考C語言宏的高級應用

            下面給出的用法模式可以避免一些使用宏的問題,供使用宏時參考:

            1) 不要在.h文件中定義宏;

            2) 使用前正確#define,使用后正確#undef

            3) 不要只是對已經(jīng)存在的宏使用#undef,選擇一個不會沖突的名稱;

            4) 不使用會導致不穩(wěn)定的C++構(gòu)造(unbalanced C++ constructs,譯者注)的宏,至少文檔說明其行為。

            15. 0和NULL(0 and NULL

            整數(shù)用0,實數(shù)用0.0,指針用NULL,字符(串)用'\0'

            整數(shù)用0,實數(shù)用0.0,這一點是毫無爭議的。

            對于指針(地址值),到底是用0還是NULL,Bjarne Stroustrup建議使用最原始的0,我們建議使用看上去像是指針的NULL,事實上一些C++編譯器(如gcc 4.1.0)專門提供了NULL的定義,可以給出有用的警告,尤其是sizeof(NULL)和sizeof(0)不相等的情況。

            字符(串)用'\0',不僅類型正確而且可讀性好。

            16. sizeof(sizeof

            盡可能用sizeof(varname)代替sizeof(type)

            使用sizeof(varname)是因為當變量類型改變時代碼自動同步,有些情況下sizeof(type)或許有意義,還是要盡量避免,如果變量類型改變的話不能同步。

            Struct data;
            memset(&data, 0, sizeof(data));
            memset(&data, 0, sizeof(Struct));

            17. Boost庫(Boost

            只使用Boost中被認可的庫。

            定義:Boost庫集是一個非常受歡迎的、同級評議的(peer-reviewed)、免費的、開源的C++庫。

            優(yōu)點:Boost代碼質(zhì)量普遍較高、可移植性好,填補了C++標準庫很多空白,如型別特性(type traits)、更完善的綁定(binders)、更好的智能指針,同時還提供了TR1(標準庫的擴展)的實現(xiàn)。

            缺點:某些Boost庫提倡的編程實踐可讀性差,像元程序(metaprogramming)和其他高級模板技術,以及過度“函數(shù)化”("functional")的編程風格。

            結(jié)論:為了向閱讀和維護代碼的人員提供更好的可讀性,我們只允許使用Boost特性的一個成熟子集,當前,這些庫包括:

            1) Compressed Pairboost/compressed_pair.hpp

            2) Pointer Containerboost/ptr_container不包括ptr_array.hpp和序列化(serialization)。

            我們會積極考慮添加可以的Boost特性,所以不必拘泥于該規(guī)則。

            ______________________________________

            譯者:關于C++特性的注意事項,總結(jié)一下:

            1. 對于智能指針,安全第一、方便第二,盡可能局部化(scoped_ptr)

            2. 引用形參加上const,否則使用指針形參;

            3. 函數(shù)重載的使用要清晰、易讀;

            4. 鑒于容易誤用,禁止使用缺省函數(shù)參數(shù)(值得商榷);

            5. 禁止使用變長數(shù)組;

            6. 合理使用友元;

            7. 為了方便代碼管理,禁止使用異常(值得商榷);

            8. 禁止使用RTTI,否則重新設計代碼吧;

            9. 使用C++風格的類型轉(zhuǎn)換,除單元測試外不要使用dynamic_cast;

            10. 使用流還printf + read/write,it is a problem;

            11. 能用前置自增/減不用后置自增/減;

            12. const能用則用,提倡const在前;

            13. 使用確定大小的整型,除位組外不要使用無符號型;

            14. 格式化輸出及結(jié)構(gòu)對齊時,注意32位和64位的系統(tǒng)差異;

            15. 除字符串化、連接外盡量避免使用宏;

            16. 整數(shù)用0,實數(shù)用0.0,指針用NULL,字符(串)用'\0';

            17. 用sizeof(varname)代替sizeof(type);

            18. 只使用Boost中被認可的庫。

            posted @ 2008-07-21 14:55 Fox 閱讀(6005) | 評論 (5)編輯 收藏

            一個好的日志系統(tǒng),除了可以記錄盡可能多的必要信息,方便trace bugs、提供data analysis source這些基本功能之外,其他的貌似不必太在意。但真正當bugs冒出來的時候,要命的是既沒有dump,也沒有有價值的日志,更要命的是日志居然已經(jīng)記錄了那么多,居然讓你查了半天,居然都是沒有價值的!

            悲劇啊!

            日志需要記錄的信息大概分為兩類:

            1) 系統(tǒng)運行情況:啟動、加載、讀寫、關閉、異常

            2) 用戶使用情況:進入、操作、離開、異常

            我可以想到的關于日志系統(tǒng)的要求大致以下幾點:

            1) 日志系統(tǒng)使用目錄樹結(jié)構(gòu):系統(tǒng)日志和用戶日志分別記錄,正常日志和異常日志分別記錄,不置于同一文件夾下,日志文件命名做到令觀者一目了然;

            2) 記錄詳盡但不冗余:正確記錄日志時間、位置、事件、因果,有可能的話,記錄上下文(這要求有點高了);

            3) 格式統(tǒng)一但嚴禁千篇一律:格式統(tǒng)一是指記錄內(nèi)容遵循一定格式,方便查看,嚴禁千篇一律是指記錄要有層次、輕重,不同事件導致的“同一”異常日志不應不加區(qū)別,同樣是為了方便查看;

            4) 與異常處理相輔相成:有dump時,以日志輔助快速定位,沒有dump時,日志應盡可能提供有效信息,離系統(tǒng)崩潰的地方越近越好(這一點似乎也有難度)。

            ________________________________________________

            突然想到的,也還沒有動手去做,先記下了,歡迎補充。

            _____Added on Jul.25th, 2008_______________________

            還看到一位兄弟在為我說話,謝謝!

            今天在考慮實現(xiàn)時,想到一個很現(xiàn)實的問題,日志幾乎是無處不在的,隨時隨地會有日志記錄。不知道有誰對I/O(當然主要是Output)消耗和對系統(tǒng)的影響做過專門測試,猜測就算了:-),我很想知道有沒有必要放到專門的線程中,如果放到獨立線程中的話,問題就出來了,多長時間寫一次?畢竟,記錄日志的主要目的就是為了全面記錄系統(tǒng)運行和用戶使用情況,如果在服務器crash的時候,還有日志(尤其是crash上下文日志)沒有被順利寫入,日志的意義也就大打折扣。

            誰給點建議?

            posted @ 2008-07-18 10:03 Fox 閱讀(2063) | 評論 (8)編輯 收藏

            這一篇主要提到的是類,Lippman在《Inside The C++ Object Model》第二章中對構(gòu)造函數(shù)作了詳盡說明,本文中提到的幾個單詞基本仿該書中譯本侯捷先生的翻譯:

            explicit:明確的

            implicit:隱含的

            trivial:沒有意義的

            non-trivial:有意義的

             

            原文地址:

            類是C++中基本的代碼單元,自然被廣泛使用。本節(jié)列舉了在寫一個類時要做什么、不要做什么。

            1. 構(gòu)造函數(shù)(Constructor)的職責

            構(gòu)造函數(shù)中只進行那些沒有實際意義的(trivial,譯者注:簡單初始化對于程序執(zhí)行沒有實際的邏輯意義,因為成員變量的“有意義”的值大多不在構(gòu)造函數(shù)中確定)初始化,可能的話,使用Init()方法集中初始化為有意義的(non-trivial)數(shù)據(jù)。

            定義:在構(gòu)造函數(shù)中執(zhí)行初始化操作。

            優(yōu)點:排版方便,無需擔心類是否初始化。

            缺點:在構(gòu)造函數(shù)中執(zhí)行操作引起的問題有:

            1) 構(gòu)造函數(shù)中不易報告錯誤,不能使用異常。

            2) 操作失敗會造成對象初始化失敗,引起不確定狀態(tài)。

            3) 構(gòu)造函數(shù)內(nèi)調(diào)用虛函數(shù),調(diào)用不會派發(fā)到子類實現(xiàn)中,即使當前沒有子類化實現(xiàn),將來仍是隱患。

            4) 如果有人創(chuàng)建該類型的全局變量(雖然違背了上節(jié)提到的規(guī)則),構(gòu)造函數(shù)將在main()之前被調(diào)用,有可能破壞構(gòu)造函數(shù)中暗含的假設條件。例如,gflags尚未初始化。

            結(jié)論:如果對象需要有意義的(non-trivial)初始化,考慮使用另外的Init()方法并(或)增加一個成員標記用于指示對象是否已經(jīng)初始化成功。

            2. 默認構(gòu)造函數(shù)(Default Constructors)

            如果一個類定義了若干成員變量又沒有其他構(gòu)造函數(shù),需要定義一個默認構(gòu)造函數(shù),否則編譯器將自動生產(chǎn)默認構(gòu)造函數(shù)。

            定義:新建一個沒有參數(shù)的對象時,默認構(gòu)造函數(shù)被調(diào)用,當調(diào)用new[](為數(shù)組)時,默認構(gòu)造函數(shù)總是被調(diào)用。

            優(yōu)點:默認將結(jié)構(gòu)體初始化為“不可能的”值,使調(diào)試更加容易。

            缺點:對代碼編寫者來說,這是多余的工作。

            結(jié)論:

            如果類中定義了成員變量,沒有提供其他構(gòu)造函數(shù),你需要定義一個默認構(gòu)造函數(shù)(沒有參數(shù))。默認構(gòu)造函數(shù)更適合于初始化對象,使對象內(nèi)部狀態(tài)(internal state)一致、有效。

            提供默認構(gòu)造函數(shù)的原因是:如果你沒有提供其他構(gòu)造函數(shù),又沒有定義默認構(gòu)造函數(shù),編譯器將為你自動生成一個,編譯器生成的構(gòu)造函數(shù)并不會對對象進行初始化。

            如果你定義的類繼承現(xiàn)有類,而你又沒有增加新的成員變量,則不需要為新類定義默認構(gòu)造函數(shù)。

            3. 明確的構(gòu)造函數(shù)(Explicit Constructors)

            對單參數(shù)構(gòu)造函數(shù)使用C++關鍵字explicit。

            定義:通常,只有一個參數(shù)的構(gòu)造函數(shù)可被用于轉(zhuǎn)換(conversion,譯者注:主要指隱式轉(zhuǎn)換,下文可見),例如,定義了Foo::Foo(string name),當向需要傳入一個Foo對象的函數(shù)傳入一個字符串時,構(gòu)造函數(shù)Foo::Foo(string name)被調(diào)用并將該字符串轉(zhuǎn)換為一個Foo臨時對象傳給調(diào)用函數(shù)。看上去很方便,但如果你并不希望如此通過轉(zhuǎn)換生成一個新對象的話,麻煩也隨之而來。為避免構(gòu)造函數(shù)被調(diào)用造成隱式轉(zhuǎn)換,可以將其聲明為explicit。

            優(yōu)點:避免不合時宜的變換。

            缺點:無。

            結(jié)論:

            所有單參數(shù)構(gòu)造函數(shù)必須是明確的。在類定義中,將關鍵字explicit加到單參數(shù)構(gòu)造函數(shù)前:explicit Foo(string name);

            例外:在少數(shù)情況下,拷貝構(gòu)造函數(shù)可以不聲明為explicit;特意作為其他類的透明包裝器的類。類似例外情況應在注釋中明確說明。

            4. 拷貝構(gòu)造函數(shù)(Copy Constructors)

            僅在代碼中需要拷貝一個類對象的時候使用拷貝構(gòu)造函數(shù);不需要拷貝時應使用DISALLOW_COPY_AND_ASSIGN

            定義:通過拷貝新建對象時可使用拷貝構(gòu)造函數(shù)(特別是對象的傳值時)。

            優(yōu)點:拷貝構(gòu)造函數(shù)使得拷貝對象更加容易,STL容器要求所有內(nèi)容可拷貝、可賦值。

            缺點:C++中對象的隱式拷貝是導致很多性能問題和bugs的根源。拷貝構(gòu)造函數(shù)降低了代碼可讀性,相比按引用傳遞,跟蹤按值傳遞的對象更加困難,對象修改的地方變得難以捉摸。

            結(jié)論:

            大量的類并不需要可拷貝,也不需要一個拷貝構(gòu)造函數(shù)或賦值操作(assignment operator)。不幸的是,如果你不主動聲明它們,編譯器會為你自動生成,而且是public的。

            可以考慮在類的private中添加空的(dummy)拷貝構(gòu)造函數(shù)和賦值操作,只有聲明,沒有定義。由于這些空程序聲明為private,當其他代碼試圖使用它們的時候,編譯器將報錯。為了方便,可以使用宏DISALLOW_COPY_AND_ASSIGN:

            // 禁止使用拷貝構(gòu)造函數(shù)和賦值操作的宏
            // 應在類的private:中使用
            #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
              TypeName(const TypeName&);               \
              void operator=(const TypeName&)

            class Foo {
            public:
              Foo(int f);
              ~Foo();

            private:
              DISALLOW_COPY_AND_ASSIGN(Foo);
            };

            如上所述,絕大多數(shù)情況下都應使用DISALLOW_COPY_AND_ASSIGN,如果類確實需要可拷貝,應在該類的頭文件中說明原由,并適當定義拷貝構(gòu)造函數(shù)和賦值操作,注意在operator=中檢測自賦值(self-assignment)情況。

            在將類作為STL容器值得時候,你可能有使類可拷貝的沖動。類似情況下,真正該做的是使用指針指向STL容器中的對象,可以考慮使用std::tr1::shared_ptr。

            5. 結(jié)構(gòu)體和類(Structs vs. Classes)

            僅當只有數(shù)據(jù)時使用struct,其它一概使用class。

            在C++中,關鍵字struct和class幾乎含義等同,我們?yōu)槠淙藶樘砑诱Z義,以便為定義的數(shù)據(jù)類型合理選擇使用哪個關鍵字。

            struct被用在僅包含數(shù)據(jù)的消極對象(passive objects)上,可能包括有關聯(lián)的常量,但沒有存取數(shù)據(jù)成員之外的函數(shù)功能,而存取功能通過直接訪問實現(xiàn)而無需方法調(diào)用,這兒提到的方法是指只用于處理數(shù)據(jù)成員的,如構(gòu)造函數(shù)、析構(gòu)函數(shù)、Initialize()、Reset()、Validate()。

            如果需要更多的函數(shù)功能,class更適合,如果不確定的話,直接使用class。

            如果與STL結(jié)合,對于仿函數(shù)(functors)和特性(traits)可以不用class而是使用struct。

            注意:類和結(jié)構(gòu)體的成員變量使用不同的命名規(guī)則。

            6. 繼承(Inheritance

            使用組合(composition,譯者注,這一點也是GoF在《Design Patterns》里反復強調(diào)的)通常比使用繼承更適宜,如果使用繼承的話,只使用公共繼承。

            定義:當子類繼承基類時,子類包含了父基類所有數(shù)據(jù)及操作的定義。C++實踐中,繼承主要用于兩種場合:實現(xiàn)繼承(implementation inheritance),子類繼承父類的實現(xiàn)代碼;接口繼承(interface inheritance),子類僅繼承父類的方法名稱。

            優(yōu)點:實現(xiàn)繼承通過原封不動的重用基類代碼減少了代碼量。由于繼承是編譯時聲明(compile-time declaration),編碼者和編譯器都可以理解相應操作并發(fā)現(xiàn)錯誤。接口繼承可用于程序上增強類的特定API的功能,在類沒有定義API的必要實現(xiàn)時,編譯器同樣可以偵錯。

            缺點:對于實現(xiàn)繼承,由于實現(xiàn)子類的代碼在父類和子類間延展,要理解其實現(xiàn)變得更加困難。子類不能重寫父類的非虛函數(shù),當然也就不能修改其實現(xiàn)。基類也可能定義了一些數(shù)據(jù)成員,還要區(qū)分基類的物理輪廓(physical layout)

            結(jié)論:

            所有繼承必須是public的,如果想私有繼承的話,應該采取包含基類實例作為成員的方式作為替代。

            不要過多使用實現(xiàn)繼承,組合通常更合適一些。努力做到只在“是一個”("is-a",譯者注,其他"has-a"情況下請使用組合)的情況下使用繼承:如果Bar的確“是一種”Foo,才令Bar是Foo的子類。

            必要的話,令析構(gòu)函數(shù)為virtual,必要是指,如果該類具有虛函數(shù),其析構(gòu)函數(shù)應該為虛函數(shù)。

            譯者注:至于子類沒有額外數(shù)據(jù)成員,甚至父類也沒有任何數(shù)據(jù)成員的特殊情況下,析構(gòu)函數(shù)的調(diào)用是否必要是語義爭論,從編程設計規(guī)范的角度看,在含有虛函數(shù)的父類中,定義虛析構(gòu)函數(shù)絕對必要。

            限定僅在子類訪問的成員函數(shù)為protected,需要注意的是數(shù)據(jù)成員應始終為私有。

            當重定義派生的虛函數(shù)時,在派生類中明確聲明其為virtual。根本原因:如果遺漏virtual,閱讀者需要檢索類的所有祖先以確定該函數(shù)是否為虛函數(shù)(譯者注,雖然不影響其為虛函數(shù)的本質(zhì))

            7. 多重繼承(Multiple Inheritance

            真正需要用到多重實現(xiàn)繼承(multiple implementation inheritance)的時候非常少,只有當最多一個基類中含有實現(xiàn),其他基類都是以Interface為后綴的純接口類時才會使用多重繼承。

            定義:多重繼承允許子類擁有多個基類,要將作為純接口的基類和具有實現(xiàn)的基類區(qū)別開來。

            優(yōu)點:相比單繼承,多重實現(xiàn)繼承可令你重用更多代碼。

            缺點:真正需要用到多重實現(xiàn)繼承的時候非常少,多重實現(xiàn)繼承看上去是不錯的解決方案,通常可以找到更加明確、清晰的、不同的解決方案。

            結(jié)論:只有當所有超類(superclass)除第一個外都是純接口時才能使用多重繼承。為確保它們是純接口,這些類必須以Interface為后綴。

            注意:關于此規(guī)則,Windows下有種例外情況(譯者注,將在本譯文最后一篇的規(guī)則例外中闡述)。

            8. 接口(Interface

            接口是指滿足特定條件的類,這些類以Interface為后綴(非必需)。

            定義:當一個類滿足以下要求時,稱之為純接口:

            1) 只有純虛函數(shù)("=0")和靜態(tài)函數(shù)(下文提到的析構(gòu)函數(shù)除外);

            2) 沒有非靜態(tài)數(shù)據(jù)成員;

            3) 沒有定義任何構(gòu)造函數(shù)。如果有,也不含參數(shù),并且為protected;

            4) 如果是子類,也只能繼承滿足上述條件并以Interface為后綴的類。

            接口類不能被直接實例化,因為它聲明了純虛函數(shù)。為確保接口類的所有實現(xiàn)可被正確銷毀,必須為之聲明虛析構(gòu)函數(shù)(作為第1條規(guī)則的例外,析構(gòu)函數(shù)不能是純虛函數(shù))。具體細節(jié)可參考Stroustrup的《The C++ Programming Language, 3rd edition》第12.4節(jié)。

            優(yōu)點:以Interface為后綴可令他人知道不能為該接口類增加實現(xiàn)函數(shù)或非靜態(tài)數(shù)據(jù)成員,這一點對于多重繼承尤其重要。另外,對于Java程序員來說,接口的概念已經(jīng)深入人心。

            缺點:Interface后綴增加了類名長度,為閱讀和理解帶來不便,同時,接口特性作為實現(xiàn)細節(jié)不應暴露給客戶。

            結(jié)論:。只有在滿足上述需要時,類才以Interface結(jié)尾,但反過來,滿足上述需要的類未必一定以Interface結(jié)尾。

            9. 操作符重載(Operator Overloading

            除少數(shù)特定環(huán)境外,不要重載操作符。

            定義:一個類可以定義諸如+、/等操作符,使其可以像內(nèi)建類型一樣直接使用。

            優(yōu)點:使代碼看上去更加直觀,就像內(nèi)建類型(如int)那樣,重載操作符使那些Equals()、Add()等黯淡無光的函數(shù)名好玩多了。為了使一些模板函數(shù)正確工作,你可能需要定義操作符。

            缺點:雖然操作符重載令代碼更加直觀,但也有一些不足

            1) 混淆直覺,讓你誤以為一些耗時的操作像內(nèi)建操作那樣輕巧;

            2) 查找重載操作符的調(diào)用處更加困難,查找Equals()顯然比同等調(diào)用==容易的多;

            3) 有的操作符可以對指針進行操作,容易導致bugs,F(xiàn)oo + 4做的是一件事,而&Foo + 4可能做的是完全不同的另一件事,對于二者,編譯器都不會報錯,使其很難調(diào)試;

            4) 重載還有令你吃驚的副作用,比如,重載操作符&的類不能被前置聲明。

            結(jié)論:

            一般不要重載操作符,尤其是賦值操作(operator=)比較陰險,應避免重載。如果需要的話,可以定義類似Equals()、CopyFrom()等函數(shù)。

            然而,極少數(shù)情況下需要重載操作符以便與模板或“標準”C++類銜接(如operator<<(ostream&, const T&)),如果被證明是正當?shù)纳锌山邮埽阋M可能避免這樣做。尤其是不要僅僅為了在STL容器中作為key使用就重載operator==或operator<,取而代之,你應該在聲明容器的時候,創(chuàng)建相等判斷和大小比較的仿函數(shù)類型。

            有些STL算法確實需要重載operator==時可以這么做,不要忘了提供文檔說明原因。

            參考拷貝構(gòu)造函數(shù)函數(shù)重載

            10. 存取控制(Access Control

            將數(shù)據(jù)成員私有化,并提供相關存取函數(shù),如定義變量foo_及取值函數(shù)foo()、賦值函數(shù)set_foo()。

            存取函數(shù)的定義一般內(nèi)聯(lián)在頭文件中。

            參考繼承函數(shù)命名

            11. 聲明次序(Declaration Order

            在類中使用特定的聲明次序:public:在private:之前,成員函數(shù)在數(shù)據(jù)成員(變量)前。

            定義次序如下:public:、protected:、private:,如果那一塊沒有,直接忽略即可。

            每一塊中,聲明次序一般如下:

            1) typedefs和enums;

            2) 常量;

            3) 構(gòu)造函數(shù);

            4) 析構(gòu)函數(shù);

            5) 成員函數(shù),含靜態(tài)成員函數(shù);

            6) 數(shù)據(jù)成員,含靜態(tài)數(shù)據(jù)成員。

            宏DISALLOW_COPY_AND_ASSIGN置于private:塊之后,作為類的最后部分。參考拷貝構(gòu)造函數(shù)

            .cc文件中函數(shù)的定義應盡可能和聲明次序一致。

            不要將大型函數(shù)內(nèi)聯(lián)到類的定義中,通常,只有那些沒有特別意義的或者性能要求高的,并且是比較短小的函數(shù)才被定義為內(nèi)聯(lián)函數(shù)。更多細節(jié)參考譯文第一篇的內(nèi)聯(lián)函數(shù)

            12. 編寫短小函數(shù)(Write Short Functions

            傾向于選擇短小、凝練的函數(shù)。

            長函數(shù)有時是恰當?shù)模虼藢τ诤瘮?shù)長度并沒有嚴格限制。如果函數(shù)超過40行,可以考慮在不影響程序結(jié)構(gòu)的情況下將其分割一下。

            即使一個長函數(shù)現(xiàn)在工作的非常好,一旦有人對其修改,有可能出現(xiàn)新的問題,甚至導致難以發(fā)現(xiàn)的bugs。使函數(shù)盡量短小、簡單,便于他人閱讀和修改代碼。

            在處理代碼時,你可能會發(fā)現(xiàn)復雜的長函數(shù),不要害怕修改現(xiàn)有代碼:如果證實這些代碼使用、調(diào)試困難,或者你需要使用其中的一小塊,考慮將其分割為更加短小、易于管理的若干函數(shù)。

            ______________________________________

            譯者:關于類的注意事項,總結(jié)一下:

            1. 不在構(gòu)造函數(shù)中做太多邏輯相關的初始化;

            2. 編譯器提供的默認構(gòu)造函數(shù)不會對變量進行初始化,如果定義了其他構(gòu)造函數(shù),編譯器不再提供,需要編碼者自行提供默認構(gòu)造函數(shù);

            3. 為避免隱式轉(zhuǎn)換,需將單參數(shù)構(gòu)造函數(shù)聲明為explicit;

            4. 為避免拷貝構(gòu)造函數(shù)、賦值操作的濫用和編譯器自動生成,可目前聲明其為private且無需實現(xiàn);

            5. 僅在作為數(shù)據(jù)集合時使用struct;

            6. 組合>實現(xiàn)繼承>接口繼承>私有繼承,子類重載的虛函數(shù)也要聲明virtual關鍵字,雖然編譯器允許不這樣做;

            7. 避免使用多重繼承,使用時,除一個基類含有實現(xiàn)外,其他基類均為純接口;

            8. 接口類類名以Interface為后綴,除提供帶實現(xiàn)的虛析構(gòu)函數(shù)、靜態(tài)成員函數(shù)外,其他均為純虛函數(shù),不定義非靜態(tài)數(shù)據(jù)成員,不提供構(gòu)造函數(shù),提供的話,聲明為protected;

            9. 為降低復雜性,盡量不重載操作符,模板、標準類中使用時提供文檔說明;

            10. 存取函數(shù)一般內(nèi)聯(lián)在頭文件中;

            11. 聲明次序:public->protected->private;

            12. 函數(shù)體盡量短小、緊湊,功能單一。

            posted @ 2008-07-16 17:43 Fox 閱讀(4897) | 評論 (4)編輯 收藏

            原文地址:

            • 作用域

            1. 命名空間(Namespaces)

            在.cc文件中,提倡使用不具名的命名空間(unnamed namespaces,譯者注:不具名的命名空間就像不具名的類一樣,似乎被介紹的很少:-()。使用具名命名空間時,其名稱可基于項目或路徑名稱,不要使用using指示符。

            定義:命名空間將全局作用域細分為不同的、具名的作用域,可有效防止全局作用域的命名沖突。

            優(yōu)點:命名空間提供了(可嵌套)命名軸線(name axis,譯者注:將命名分割在不同命名空間內(nèi)),當然,類也提供了(可嵌套)的命名軸線(譯者注:將命名分割在不同類的作用域內(nèi))。

            舉例來說,兩個不同項目的全局作用域都有一個類Foo,這樣在編譯或運行時造成沖突。如果每個項目將代碼置于不同命名空間中,project1::Foo和project2::Foo作為不同符號自然不會沖突。

            缺點:命名空間具有迷惑性,因為它們和類一樣提供了額外的(可嵌套的)命名軸線。在頭文件中使用不具名的空間容易違背C++的唯一定義原則(One Definition Rule (ODR))

            結(jié)論:根據(jù)下文將要提到的策略合理使用命名空間。

            1) 不具名命名空間(Unnamed Namespaces)

            在.cc文件中,允許甚至提倡使用不具名命名空間,以避免運行時的命名沖突:

            namespace {                                   // .cc 文件中

            // 命名空間的內(nèi)容無需縮進
            enum { UNUSED, EOF, ERROR };          // 經(jīng)常使用的符號
            bool AtEof() { return pos_ == EOF; }   // 使用本命名空間內(nèi)的符號EOF

            }  // namespace

            然而,與特定類關聯(lián)的文件作用域聲明在該類中被聲明為類型、靜態(tài)數(shù)據(jù)成員或靜態(tài)成員函數(shù),而不是不具名命名空間的成員。像上文展示的那樣,不具名命名空間結(jié)束時用注釋// namespace標識。

            不能在.h文件中使用不具名命名空間。

            2) 具名命名空間(Named Namespaces)

            具名命名空間使用方式如下:

            命名空間將除文件包含、全局標識的聲明/定義以及類的前置聲明外的整個源文件封裝起來,以同其他命名空間相區(qū)分。

            // .h文件
            namespace mynamespace {

            // 所有聲明都置于命名空間中
            // 注意不要使用縮進
            class MyClass {
            public:
              ...
              void Foo();
            };

            }  // namespace mynamespace

            // .cc文件
            namespace mynamespace {

            // 函數(shù)定義都置于命名空間中
            void MyClass::Foo() {
              ...
            }

            }  // namespace mynamespace

            通常的.cc文件會包含更多、更復雜的細節(jié),包括對其他命名空間中類的引用等。

            #include "a.h"

            DEFINE_bool(someflag, false, "dummy flag");

            class C;  // 全局命名空間中類C的前置聲明
            namespace a { class A; }  // 命名空間a中的類a::A的前置聲明

            namespace b {

            ...code for b...                // b中的代碼

            }  // namespace b

            不要聲明命名空間std下的任何內(nèi)容,包括標準庫類的前置聲明。聲明std下的實體會導致不明確的行為,如,不可移植。聲明標準庫下的實體,需要包含對應的頭文件。

            最好不要使用using指示符,以保證命名空間下的所有名稱都可以正常使用。

            // 禁止——污染命名空間
            using namespace foo;

            在.cc文件、.h文件的函數(shù)、方法或類中,可以使用using。

            // 允許:.cc文件中
            // .h文件中,必須在函數(shù)、方法或類的內(nèi)部使用
            using ::foo::bar;

            在.cc文件、.h文件的函數(shù)、方法或類中,還可以使用命名空間別名。

            // 允許:.cc文件中
            // .h文件中,必須在函數(shù)、方法或類的內(nèi)部使用

            namespace fbz = ::foo::bar::baz;

            2. 嵌套類(Nested Class)

            當公開嵌套類作為接口的一部分時,雖然可以直接將他們保持在全局作用域中,但將嵌套類的聲明置于命名空間中是更好的選擇。

            定義:可以在一個類中定義另一個類,嵌套類也稱成員類(member class)

            class Foo {

            private:
              // Bar是嵌套在Foo中的成員類
              class Bar {
                ...
              };

            };

            優(yōu)點:當嵌套(成員)類只在被嵌套類(enclosing class)中使用時很有用,將其置于被嵌套類作用域作為被嵌套類的成員不會污染其他作用域同名類。可在被嵌套類中前置聲明嵌套類,在.cc文件中定義嵌套類,避免在被嵌套類中包含嵌套類的定義,因為嵌套類的定義通常只與實現(xiàn)相關。

            缺點:只能在被嵌套類的定義中才能前置聲明嵌套類。因此,任何使用Foo::Bar*指針的頭文件必須包含整個Foo的聲明。

            結(jié)論:不要將嵌套類定義為public,除非它們是接口的一部分,比如,某個方法使用了這個類的一系列選項。

            3. 非成員函數(shù)(Nonmember)、靜態(tài)成員函數(shù)(Static Member)和全局函數(shù)(Global Functions)

            使用命名空間中的非成員函數(shù)或靜態(tài)成員函數(shù),盡量不要使用全局函數(shù)。

            優(yōu)點:某些情況下,非成員函數(shù)和靜態(tài)成員函數(shù)是非常有用的,將非成員函數(shù)置于命名空間中可避免對全局作用域的污染。

            缺點:將非成員函數(shù)和靜態(tài)成員函數(shù)作為新類的成員或許更有意義,當它們需要訪問外部資源或具有重要依賴時更是如此。

            結(jié)論:

            有時,不把函數(shù)限定在類的實體中是有益的,甚至需要這么做,要么作為靜態(tài)成員,要么作為非成員函數(shù)。非成員函數(shù)不應依賴于外部變量,并盡量置于某個命名空間中。相比單純?yōu)榱朔庋b若干不共享任何靜態(tài)數(shù)據(jù)的靜態(tài)成員函數(shù)而創(chuàng)建類,不如使用命名空間。

            定義于同一編譯單元的函數(shù),被其他編譯單元直接調(diào)用可能會引入不必要的耦合和連接依賴;靜態(tài)成員函數(shù)對此尤其敏感。可以考慮提取到新類中,或者將函數(shù)置于獨立庫的命名空間中。

            如果你確實需要定義非成員函數(shù),又只是在.cc文件中使用它,可使用不具名命名空間或static關聯(lián)(如static int Foo() {...})限定其作用域。

            4. 局部變量(Local Variables)

            將函數(shù)變量盡可能置于最小作用域內(nèi),在聲明變量時將其初始化。

            C++允許在函數(shù)的任何位置聲明變量。我們提倡在盡可能小的作用域中聲明變量,離第一次使用越近越好。這使得代碼易于閱讀,易于定位變量的聲明位置、變量類型和初始值。特別是,應使用初始化代替聲明+賦值的方式。

            int i;
            i = f();        // 壞——初始化和聲明分離
            nt j = g();   // 好——初始化時聲明

            注意:gcc可正確執(zhí)行for (int i = 0; i < 10; ++i)(i的作用域僅限for循環(huán)),因此其他for循環(huán)中可重用i。if和while等語句中,作用域聲明(scope declaration)同樣是正確的。

            while (const char* p = strchr(str, '/')) str = p + 1;

            注意:如果變量是一個對象,每次進入作用域都要調(diào)用其構(gòu)造函數(shù),每次退出作用域都要調(diào)用其析構(gòu)函數(shù)。

            // 低效的實現(xiàn)
            for (int i = 0; i < 1000000; ++i) {
              Foo f;  // 構(gòu)造函數(shù)和析構(gòu)函數(shù)分別調(diào)用1000000次!
              f.DoSomething(i);
            }

            類似變量放到循環(huán)作用域外面聲明要高效的多:

            Foo f;  // 構(gòu)造函數(shù)和析構(gòu)函數(shù)只調(diào)用1次
            for (int i = 0; i < 1000000; ++i) {
              f.DoSomething(i);
            }

            5. 全局變量(Global Variables)

            class類型的全局變量是被禁止的,內(nèi)建類型的全局變量是允許的,當然多線程代碼中非常數(shù)全局變量也是被禁止的。永遠不要使用函數(shù)返回值初始化全局變量。

            不幸的是,全局變量的構(gòu)造函數(shù)、析構(gòu)函數(shù)以及初始化操作的調(diào)用順序只是被部分規(guī)定,每次生成有可能會有變化,從而導致難以發(fā)現(xiàn)的bugs。

            因此,禁止使用class類型的全局變量(包括STL的string, vector等等),因為它們的初始化順序有可能導致構(gòu)造出現(xiàn)問題。內(nèi)建類型和由內(nèi)建類型構(gòu)成的沒有構(gòu)造函數(shù)的結(jié)構(gòu)體可以使用,如果你一定要使用class類型的全局變量,請使用單件模式(singleton pattern)

            對于全局的字符串常量,使用C風格的字符串,而不要使用STL的字符串:

            const char kFrogSays[] = "ribbet";

            雖然允許在全局作用域中使用全局變量,使用時務必三思。大多數(shù)全局變量應該是類的靜態(tài)數(shù)據(jù)成員,或者當其只在.cc文件中使用時,將其定義到不具名命名空間中,或者使用靜態(tài)關聯(lián)以限制變量的作用域。

            記住,靜態(tài)成員變量視作全局變量,所以,也不能是class類型!

            ______________________________________

            譯者:這一篇主要提到的是作用域的一些規(guī)則,總結(jié)一下:

            1. .cc中的不具名命名空間可避免命名沖突、限定作用域,避免直接使用using提示符污染命名空間;

            2. 嵌套類符合局部使用原則,只是不能在其他頭文件中前置聲明,盡量不要public;

            3. 盡量不用全局函數(shù)和全局變量,考慮作用域和命名空間限制,盡量單獨形成編譯單元;

            4. 多線程中的全局變量(含靜態(tài)成員變量)不要使用class類型(含STL容器),避免不明確行為導致的bugs

            作用域的使用,除了考慮名稱污染、可讀性之外,主要是為降低耦合度,提高編譯、執(zhí)行效率。

            posted @ 2008-07-14 15:49 Fox 閱讀(7675) | 評論 (27)編輯 收藏

            原文地址:

            • 背景

            Google的開源項目大多使用C++開發(fā)。每一個C++程序員也都知道,C++具有很多強大的語言特性,但這種強大不可避免的導致它的復雜,這種復雜會使得代碼更易于出現(xiàn)bug、難于閱讀和維護。

            本指南的目的是通過詳細闡述在C++編碼時要怎樣寫、不要怎樣寫來規(guī)避其復雜性。這些規(guī)則可在允許代碼有效使用C++語言特性的同時使其易于管理。

            風格,也被視為可讀性,主要指稱管理C++代碼的習慣。使用術語風格有點用詞不當,因為這些習慣遠不止源代碼文件格式這么簡單。

            使代碼易于管理的方法之一是增強代碼一致性,讓別人可以讀懂你的代碼是很重要的,保持統(tǒng)一編程風格意味著可以輕松根據(jù)“模式匹配”規(guī)則推斷各種符號的含義。創(chuàng)建通用的、必需的習慣用語和模式可以使代碼更加容易理解,在某些情況下改變一些編程風格可能會是好的選擇,但我們還是應該遵循一致性原則,盡量不這樣去做。

            本指南的另一個觀點是C++特性的臃腫。C++是一門包含大量高級特性的巨型語言,某些情況下,我們會限制甚至禁止使用某些特性使代碼簡化,避免可能導致的各種問題,指南中列舉了這類特性,并解釋說為什么這些特性是被限制使用的。

            由Google開發(fā)的開源項目將遵照本指南約定。

            注意:本指南并非C++教程,我們假定讀者已經(jīng)對C++非常熟悉。

            • 頭文件

            通常,每一個.cc文件(C++的源文件)都有一個對應的.h文件(頭文件),也有一些例外,如單元測試代碼和只包含main()的.cc文件。

            正確使用頭文件可令代碼在可讀性、文件大小和性能上大為改觀。

            下面的規(guī)則將引導你規(guī)避使用頭文件時的各種麻煩。

            1. #define的保護

            所有頭文件都應該使用#define防止頭文件被多重包含(multiple inclusion),命名格式當是:<PROJECT>_<PATH>_<FILE>_H_

            為保證唯一性,頭文件的命名應基于其所在項目源代碼樹的全路徑。例如,項目foo中的頭文件foo/src/bar/baz.h按如下方式保護:

            #ifndef FOO_BAR_BAZ_H_
            #define FOO_BAR_BAZ_H_
            ...
            #endif // FOO_BAR_BAZ_H_

            2. 頭文件依賴

            使用前置聲明(forward declarations)盡量減少.h文件中#include的數(shù)量。

            當一個頭文件被包含的同時也引入了一項新的依賴(dependency),只要該頭文件被修改,代碼就要重新編譯。如果你的頭文件包含了其他頭文件,這些頭文件的任何改變也將導致那些包含了你的頭文件的代碼重新編譯。因此,我們寧可盡量少包含頭文件,尤其是那些包含在其他頭文件中的。

            使用前置聲明可以顯著減少需要包含的頭文件數(shù)量。舉例說明:頭文件中用到類File,但不需要訪問File的聲明,則頭文件中只需前置聲明class File;無需#include "file/base/file.h"

            在頭文件如何做到使用類Foo而無需訪問類的定義?

            1) 將數(shù)據(jù)成員類型聲明為Foo *或Foo &;

            2) 參數(shù)、返回值類型為Foo的函數(shù)只是聲明(但不定義實現(xiàn));

            3) 靜態(tài)數(shù)據(jù)成員的類型可以被聲明為Foo,因為靜態(tài)數(shù)據(jù)成員的定義在類定義之外。

            另一方面,如果你的類是Foo的子類,或者含有類型為Foo的非靜態(tài)數(shù)據(jù)成員,則必須為之包含頭文件。

            有時,使用指針成員(pointer members,如果是scoped_ptr更好)替代對象成員(object members)的確更有意義。然而,這樣的做法會降低代碼可讀性及執(zhí)行效率。如果僅僅為了少包含頭文件,還是不要這樣替代的好。

            當然,.cc文件無論如何都需要所使用類的定義部分,自然也就會包含若干頭文件。

            譯者注:能依賴聲明的就不要依賴定義。

            3. 內(nèi)聯(lián)函數(shù)

            只有當函數(shù)只有10行甚至更少時才會將其定義為內(nèi)聯(lián)函數(shù)(inline function)

            定義(Definition):當函數(shù)被聲明為內(nèi)聯(lián)函數(shù)之后,編譯器可能會將其內(nèi)聯(lián)展開,無需按通常的函數(shù)調(diào)用機制調(diào)用內(nèi)聯(lián)函數(shù)。

            優(yōu)點:當函數(shù)體比較小的時候,內(nèi)聯(lián)該函數(shù)可以令目標代碼更加高效。對于存取函數(shù)(accessor、mutator)以及其他一些比較短的關鍵執(zhí)行函數(shù)。

            缺點:濫用內(nèi)聯(lián)將導致程序變慢,內(nèi)聯(lián)有可能是目標代碼量或增或減,這取決于被內(nèi)聯(lián)的函數(shù)的大小。內(nèi)聯(lián)較短小的存取函數(shù)通常會減少代碼量,但內(nèi)聯(lián)一個很大的函數(shù)(譯者注:如果編譯器允許的話)將戲劇性的增加代碼量。在現(xiàn)代處理器上,由于更好的利用指令緩存(instruction cache),小巧的代碼往往執(zhí)行更快。

            結(jié)論:一個比較得當?shù)奶幚硪?guī)則是,不要內(nèi)聯(lián)超過10行的函數(shù)。對于析構(gòu)函數(shù)應慎重對待,析構(gòu)函數(shù)往往比其表面看起來要長,因為有一些隱式成員和基類析構(gòu)函數(shù)(如果有的話)被調(diào)用!

            另一有用的處理規(guī)則:內(nèi)聯(lián)那些包含循環(huán)或switch語句的函數(shù)是得不償失的,除非在大多數(shù)情況下,這些循環(huán)或switch語句從不執(zhí)行。

            重要的是,虛函數(shù)和遞歸函數(shù)即使被聲明為內(nèi)聯(lián)的也不一定就是內(nèi)聯(lián)函數(shù)。通常,遞歸函數(shù)不應該被聲明為內(nèi)聯(lián)的(譯者注:遞歸調(diào)用堆棧的展開并不像循環(huán)那么簡單,比如遞歸層數(shù)在編譯時可能是未知的,大多數(shù)編譯器都不支持內(nèi)聯(lián)遞歸函數(shù))。析構(gòu)函數(shù)內(nèi)聯(lián)的主要原因是其定義在類的定義中,為了方便抑或是對其行為給出文檔。

            4. -inl.h文件

            復雜的內(nèi)聯(lián)函數(shù)的定義,應放在后綴名為-inl.h的頭文件中。

            在頭文件中給出內(nèi)聯(lián)函數(shù)的定義,可令編譯器將其在調(diào)用處內(nèi)聯(lián)展開。然而,實現(xiàn)代碼應完全放到.cc文件中,我們不希望.h文件中出現(xiàn)太多實現(xiàn)代碼,除非這樣做在可讀性和效率上有明顯優(yōu)勢。

            如果內(nèi)聯(lián)函數(shù)的定義比較短小、邏輯比較簡單,其實現(xiàn)代碼可以放在.h文件中。例如,存取函數(shù)的實現(xiàn)理所當然都放在類定義中。出于實現(xiàn)和調(diào)用的方便,較復雜的內(nèi)聯(lián)函數(shù)也可以放到.h文件中,如果你覺得這樣會使頭文件顯得笨重,還可以將其分離到單獨的-inl.h中。這樣即把實現(xiàn)和類定義分離開來,當需要時包含實現(xiàn)所在的-inl.h即可。

            -inl.h文件還可用于函數(shù)模板的定義,從而使得模板定義可讀性增強。

            要提醒的一點是,-inl.h和其他頭文件一樣,也需要#define保護。

            5. 函數(shù)參數(shù)順序(Function Parameter Ordering)

            定義函數(shù)時,參數(shù)順序為:輸入?yún)?shù)在前,輸出參數(shù)在后。

            C/C++函數(shù)參數(shù)分為輸入?yún)?shù)和輸出參數(shù)兩種,有時輸入?yún)?shù)也會輸出(譯者注:值被修改時)。輸入?yún)?shù)一般傳值或常數(shù)引用(const references),輸出參數(shù)或輸入/輸出參數(shù)為非常數(shù)指針(non-const pointers)。對參數(shù)排序時,將所有輸入?yún)?shù)置于輸出參數(shù)之前。不要僅僅因為是新添加的參數(shù),就將其置于最后,而應該依然置于輸出參數(shù)之前。

            這一點并不是必須遵循的規(guī)則,輸入/輸出兩用參數(shù)(通常是類/結(jié)構(gòu)體變量)混在其中,會使得規(guī)則難以遵循。

            6. 包含文件的名稱及次序

            將包含次序標準化可增強可讀性、避免隱藏依賴(hidden dependencies,譯者注:隱藏依賴主要是指包含的文件中編譯時),次序如下:C庫、C++庫、其他庫的.h、項目內(nèi)的.h。

            項目內(nèi)頭文件應按照項目源代碼目錄樹結(jié)構(gòu)排列,并且避免使用UNIX文件路徑.(當前目錄)和..(父目錄)。例如,google-awesome-project/src/base/logging.h應像這樣被包含:

            #include "base/logging.h"

            dir/foo.cc的主要作用是執(zhí)行或測試dir2/foo2.h的功能,foo.cc中包含頭文件的次序如下:

                dir2/foo2.h(優(yōu)先位置,詳情如下)
                C系統(tǒng)文件
                C++系統(tǒng)文件
                其他庫頭文件
                本項目內(nèi)頭文件

            這種排序方式可有效減少隱藏依賴,我們希望每一個頭文件獨立編譯。最簡單的實現(xiàn)方式是將其作為第一個.h文件包含在對應的.cc中。

            dir/foo.cc和dir2/foo2.h通常位于相同目錄下(像base/basictypes_unittest.cc和base/basictypes.h),但也可在不同目錄下。

            相同目錄下頭文件按字母序是不錯的選擇。

            舉例來說,google-awesome-project/src/foo/internal/fooserver.cc的包含次序如下:

            #include "foo/public/fooserver.h"  // 優(yōu)先位置

            #include <sys/types.h>
            #include <unistd.h>

            #include <hash_map>
            #include <vector>

            #include "base/basictypes.h"
            #include "base/commandlineflags.h"
            #include "foo/public/bar.h"

            ______________________________________

            譯者:英語不太好,翻譯的也就不太好。這一篇主要提到的是頭文件的一些規(guī)則,總結(jié)一下:

            1. 避免多重包含是學編程時最基本的要求;

            2. 前置聲明是為了降低編譯依賴,防止修改一個頭文件引發(fā)多米諾效應;

            3. 內(nèi)聯(lián)函數(shù)的合理使用可提高代碼執(zhí)行效率;

            4. -inl.h可提高代碼可讀性(一般用不到吧:D);

            5. 標準化函數(shù)參數(shù)順序可以提高可讀性和易維護性(對函數(shù)參數(shù)的堆棧空間有輕微影響,我以前大多是相同類型放在一起);

            6. 包含文件的名稱使用.和..雖然方便卻易混亂,使用比較完整的項目路徑看上去很清晰、很條理,包含文件的次序除了美觀之外,最重要的是可以減少隱藏依賴,使每個頭文件在“最需要編譯”(對應源文件處:D)的地方編譯,有人提出庫文件放在最后,這樣出錯先是項目內(nèi)的文件,頭文件都放在對應源文件的最前面,這一點足以保證內(nèi)部錯誤的及時發(fā)現(xiàn)了。

            posted @ 2008-07-10 17:35 Fox 閱讀(10903) | 評論 (3)編輯 收藏

            sudo使普通用戶具有root用戶的權限,減少root登錄,還有日志記錄……,主要是為了安全,暫時沒有體會@.@。

            1. sudo

            1) 安裝sudo:apt-get install sudo

            2) 添加sudo:

              a. chmod +w /etc/sudoers,允許寫
              b. vim /etc/sudoers
              c. 添加:newsudo   ALL=(ALL) NOPASSWD: ALL
              d. chmod 0440 /etc/sudoers,更改sudoers文件權限,使之可以修改如vimrc等文件

            2. 配置Vim

            1) 安裝最新Vim:apt-get install vim

            2) 語法加亮:vim /etc/vim/vimrc,去掉systax on前的注釋"

            3) 顯示行號:set nu

            4) 自動縮進:set autoindent

              C語言縮進:set cindent

            3. 配置Profile

            1) vim /etc/profile

            2) 添加:alias ls='ls --color=auto'

            3) logout

            更多零零散散的配置就不寫了,因為有TX在我的上一篇回帖說我寫的很水,說實話,我有點被冤枉。就我目前對Debian的認知水平,也就到這份上了。有句話說的好:工欲善其事,必先利其器。這次哥哥我重新殺回到Debian來,絕對不會只是玩玩兒,我會負責的

            后面就會開始用make慢慢寫一些東西了。說到底,這一系列日記只是對工具的一個熟悉過程而已。

            PS:

            GR今天在說,我似乎應該把時間花到有用的東西上面,言下之意,我當下做的事沒什么意義,呵呵,其實,轉(zhuǎn)到Linux平臺下熟悉一下,也算是有意義的事,對我來講,閑的無聊的時候看毛片也算有意義的事:D。

            當然,話說回來,最近關乎學習的另外一件有意義的事情是看看Kevin推薦的《C++ Templates》和《Modern C++ Design》。

            之所以婆婆媽媽的寫下來,主要因為我這個人羞恥感不強 + 健忘,寫下來還不去做會讓我覺得難堪;-)。

            posted @ 2008-06-06 01:33 Fox 閱讀(2752) | 評論 (7)編輯 收藏

            題記:哥(我)基本是Linux盲,剛開始接觸,有點像對females的亢奮感。

            心血來潮,再次裝了VMware,兩個機子分別裝了簡體中文和英文的Debian,只有base-system

            前前后后裝了不下五、六遍,算是對Debian的安裝過程熟悉一下,有點弱智:D。裝好系統(tǒng)之后,只裝了Vimapt-get install vim

            Vim是一個很好玩的東西,自從我在06年結(jié)束了TC 2.0的時代之后,已經(jīng)很久沒有在純文本模式下做過任何操作了(偶爾的裝過Debian,用過Vim都不算吧:D)。雖然我一向聲稱更喜歡命令操作而不是鼠標移動,但那畢竟是在用了六、七年的Windows下(雖然也不長:(),看來多少是有點葉公好龍的感覺。

            INSERT mode下,費了半天勁,敲了十來行代碼,總覺得自己的操作太笨。Google了一下,國內(nèi)有一個比較好的專門介紹Vim中文文檔的site

            原來,在NORMAL mode下,:help可以搞定一切。只是這個help太長了,一時半會兒看不完。退出Vim(:q!)后,命令行運行vimtutor是一個基本教程,還有中文版的,打開全是亂碼。

            apt-get install zhcon

            期間要我插入netinst的disk,但我是用VMware,干脆把/etc/apt/sources.list的cdrom去掉,直接用網(wǎng)絡了:-(。

            裝好之后,/usr/share/vim/vim70/tutor下的tutor.zh.big5tutor.zh.euc還是亂碼顯示,用Vim打開,:set fileencoding=utf-8之后也一樣,難道是因為我選擇的英文版嗎?后面看了tw一篇非常詳盡的圖解,獲益匪淺。原來是沒有啟動zhcon#--"!

            這兒有個小插曲,執(zhí)行zhcon,機器就掛掉了,屢試不爽。Google到的解決方案:zhcon --utf8 --drv=vga

            感覺執(zhí)行zhcon之后,文本顯示速度巨慢,慢到不可忍受,遂放棄,就當學習英文好了:-)。

            之后,把筆記本上的Debian裝成簡體中文,晚上回來后,筆記本不能上網(wǎng)了,拿GR的機器裝Debian,發(fā)現(xiàn)用的netinst的iso總是出錯,近乎崩潰。該死的艾普寬帶又綁定MAC,筆記本上不成網(wǎng)。只好乖乖的學習Vim的使用:D。

            執(zhí)行vimtutor,打開Vim的Tutor,一晚上練了前面四課,還是比較有效果。

            對Tutor中常常note的一句話很有體會:do not try to memorize, learn by usage。所以,如果你想知道怎么用Vim,裝上

            PS01:

            1) logout時,遇到There are stopped jobs,才想起來自己之前不小心錯誤退出Vim,用job看了一下,果然Vim已經(jīng)stopped,fg之后,正常退出Vim。

            2) 剛開始根本沒有習慣Vim的光標移動:上下左右對應kjhl,慢慢習慣了之后,在Windows下老是想用kjhl操作,才發(fā)覺使用kjhl比用方向鍵要方便的多,不是嗎?

            PS02:

            1) 近11點又和GR去門口吃冷鍋串串,小high一下,每天晚上整點小吃,再整上兩瓶,用我在公司推廣的流行語說:整的好哦!

            2) 再補上一句:明天哥還帶你來!

            3) 后面冒出一句:操你大爺!

            4) 早睡早起身體好!

            本周簽名:談政治就像看毛片,過癮,但要偷偷的,多了反胃……

            posted @ 2008-06-05 00:21 Fox 閱讀(2583) | 評論 (5)編輯 收藏

            寫的太雜,實在沒法寫題目,就用這一周的簽名吧,很合現(xiàn)在的心境。

            Kevin眼中的我,大概是個重視理論算法勝過編程實踐的人,而我的算法和理論基礎尚差的出奇(可能這就是知恥而后勇吧:D),可見我的編程實踐又會多么的差了。Bugs更是對我整日沉浸于這些不著邊際的“空中樓閣”頗有微詞,甚至嗤之以鼻。今日若不是要把自己前段時間的豆腐渣粉飾一番,我依然不愿去考慮多線程的具體實現(xiàn),或者說不是不愿,是不敢,總有一種臨深履薄之感。

            縱然如此,為了更好的完成工作,我還是拉來Kevin,勞他為我講解一下多線程,可能是因為我從未仔細看過boost等C++開源庫的原因吧,我對于結(jié)構(gòu)封裝本身并沒有多少概念。說句實話,看到那些模板我就頭大,心里想:本來一個簡單的東西,為什么要搞的那么復雜呢?當然,我知道,這是因為我對其缺乏了解,在對一樣東西沒有完全理解就妄測其好壞是自卑的表現(xiàn):D,所以也請Kevin原諒我的無知,順便致謝;-)。

            還是稍微提一下多線程的東西吧,因為這一次改動并不很大,因此只言片語難以面面俱到,也請各位TX不必較真兒。這兒只是說一下我是怎么偷懶把之前沒有使用多線程的I/O部分修改成多線程I/O的,I/O的細節(jié)不再詳述,而且這臺機器上面因為沒有VS,僅憑記憶,如果有什么差錯,請幫我指出來了:-)。

            在項目啟動后的初始化中初始化I/O線程:

             1 void SomeApp::Init(void)
             2 {  
             3       // do some other things 
             4 
             5     CIOOperator::Init();  // InitializeCriticalSection for I/O queue(s)
             6 
             7     forint i=0; i<nIOThreadsNum; ++i ) 
             8     { 
             9         CIOOperator *pOpObj = new CIOOperator; 
            10         pOpObj->Create(); 
            11         m_vecIOThreads.push_back(pOpObj); 
            12     } 
            13 
            14       // do some other things 
            15 }


            在項目退出前結(jié)束I/O線程: 

             1 void SomeApp::Release(void
             2 {  
             3       // do some other things 
             4 
             5     vector<CIOOperator *>::iterator it = m_vecIOThreads.begin(); 
             6     for( ; i!=m_vecIOThreads.end(); ++it ) 
             7     { 
             8         it->End(); 
             9         delete (*it); 
            10     } 
            11     m_vecIOThreads.clear();
            12 
            13     CIOOperator::Release();  // DeleteCriticalSectionfor I/O queue(s)
            14 
            15       // do some other things 
            16 


            I/O線程函數(shù):  

             1 void ThreadFunc( void *pArgument )
             2 {
             3       // do some other things, like exception handling 
             4 
             5     (CIOOperator *)pArgument->Run();  // operating I/O queue(s) until exit
             6 
             7       // do some other things 
             8 
             9     _endthreadex();
            10 }

            說多線程復雜,無外乎線程的退出策略同步機制調(diào)試異常處理等等。多少還是需要一些知識(尤其是同步)和經(jīng)驗(尤其是調(diào)試)。

            再回來說一下最近比較關注的算法吧,雖然為某些人所不齒,甚至公然批評我最近比較松懈,實在令我難堪的緊。我又沒有消極怠工,難道編寫代碼是積極,學習算法就是消極嗎?鄉(xiāng)下來的,且不必理他。當然,多少還是需要注意一下分寸吧。

            在受到上次解決烙餅排序問題的打擊之后,我開始反思:自己思考和解決問題的角度怎么就那么簡單和狹隘?細細想來,從本科畢業(yè)之后,幾乎再沒翻過算法的書,幾乎再沒做過算法的題,寫代碼只是為了糊口,只能糊口的代碼自然只能以垃圾形容。意識粗糙,操作離譜,整個一下里巴人。

            在一番深深的自責之后,痛定思痛,痛何如哉,才感覺自己關于算法的思維空間已經(jīng)局限于if-else、do-while,連窮舉、分治、貪心、回溯這些以前念書時天天掛在嘴邊侃侃而談的常用算法都沒有概念了,遑論動態(tài)規(guī)劃、最小二乘法、線性回歸等復雜一些的(非)數(shù)值算法,關于數(shù)據(jù)結(jié)構(gòu)的思維空間也已經(jīng)局限于vector、list、map了,再也沒有回憶過stack、tree、graph這些讀死書、死讀書得來的所謂知識。 

            可悲啊,為什么拿到一個困難一點的問題,就只知道畫圖、編碼,而不知道組織算法呢?甚至連這個問題到底有無多項式時間解都不去考慮。然而,一提誰都知道:算法復雜性——數(shù)據(jù)結(jié)構(gòu)第0章就會提到的基礎,真正分析起來,卻是力不從心。

            所以,接著掃掃盲吧,實在沒有必要去搞很多艱深的東西,本來想接下來就寫NP難題,可是近來工作上的事情確實有些多,之前一篇已經(jīng)是被Alex(這家伙卻至今未開張……)“催出來”的了。

            手頭只有MIT英文版的《Introduction to Algorithms》ed.2,于是就從網(wǎng)上找了中文電子版,居然是上個世紀94年南京大學譯的第一版。看算法的話,多半是以這本書和Wikipedia為主了。

            PS: 另外做的一點事情,似乎和詞法分析異常處理等多少有些關聯(lián),內(nèi)容相當瑣碎,此刻不再贅述。

            對了,和工作、學習并不那么相干的事情就是,今天和幾個同事出去釣了幾個小時魚,收獲嘛,保密:D。

            近期考慮的關鍵詞:無縫世界 網(wǎng)游安全 算法導論 兄弟激情

            posted @ 2008-05-11 02:25 Fox 閱讀(1767) | 評論 (8)編輯 收藏

            僅列出標題
            共7頁: 1 2 3 4 5 6 7 
            国产一区二区久久久| 久久婷婷国产麻豆91天堂| 久久这里只有精品18| a高清免费毛片久久| 亚洲?V乱码久久精品蜜桃 | 国产成人精品久久二区二区| 一本色道久久88加勒比—综合| 亚洲精品97久久中文字幕无码| 2021久久精品国产99国产精品| 久久人人爽人爽人人爽av| 精品久久久噜噜噜久久久| 久久久免费观成人影院| 国产精品久久波多野结衣| 香蕉久久夜色精品国产尤物| 久久亚洲综合色一区二区三区| 思思久久精品在热线热| 久久国产福利免费| 东京热TOKYO综合久久精品| 久久婷婷色综合一区二区| 久久久免费观成人影院| 久久国产精品-久久精品| 久久九九精品99国产精品| 欧美精品乱码99久久蜜桃| 久久人人爽人人澡人人高潮AV | 精品国产日韩久久亚洲| 国产免费福利体检区久久 | 久久精品综合一区二区三区| 国产午夜精品久久久久免费视| 久久久亚洲AV波多野结衣| 亚洲日本va午夜中文字幕久久| 国产精品无码久久综合网| 中文字幕成人精品久久不卡| 国内精品伊人久久久久AV影院| 无码伊人66久久大杳蕉网站谷歌| 国产精品99久久久精品无码| 人人狠狠综合久久亚洲高清| 国产农村妇女毛片精品久久| 国产巨作麻豆欧美亚洲综合久久| 精品国产乱码久久久久久浪潮| 国产三级观看久久| 久久免费视频6|