<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # Item 4: 確保 objects(對象)在使用前被初始化 作者:Scott Meyers 譯者:fatalerror99 (iTePub's Nirvana) 發布:http://blog.csdn.net/fatalerror99/ C++ 看上去在對象的值的初始化方面變化莫測。例如,如果你這樣做, ``` int x; ``` 在某些情形下,x 會被初始化(為 0),但是在其它情形下,也可能沒有。如果你這樣做, ``` class Point { int x, y; }; ... Point p; ``` p 的 data members(數據成員)有時會被初始化(為 0),但有時沒有。如果你從一個不存在 uninitialized objects(未初始化對象)的語言來到 C++,請注意這個問題,因為它非常重要。 讀取一個 uninitialized values(未初始化值)會引起 undefined behavior(未定義行為)。在一些平臺上,讀一個 uninitialized value(未初始化值)會引起程序中止,更可能的情況是得到一個你所讀的那個位置上的 semi-random bits(半隨機二進制位),最終導致不可預測的程序行為和惱人的調試。 現在,有一些描述關于什么時候能保證 object initialization(對象初始化)會發生什么時候不能保證的規則。不幸的是,這些規則很復雜——我覺得它復雜得無法記住。通常,如果你使用 C++ 的 C 部分(參見 Item 1),而且 initialization(初始化)可能會花費一些運行時間,它就不能保證發生。如果你使用 C++ 的 non-C 部分,事情會有些變化。這就是為什么一個 array(數組)(來自 C++ 的 C 部分)不能確保它的元素被初始化,但是一個 vector(來自 C++ 的 STL 部分)就能夠確保。 處理這種事情的表面不確定狀態的最好方法就是總是在使用之前初始化你的對象。對于 built-in types(內建類型)的 non-member objects(非成員對象),需要你手動做這件事。例如: ``` int x = 0; // manual initialization of an int const char * text = "A C-style string"; // manual initialization of a // pointer (see also Item 3) double d; // "initialization" by reading from std::cin >> d; // an input stream ``` 除此之外的幾乎全部情況,initialization(初始化)的重任就落到了 constructors(構造函數)的身上。這里的規則很簡單:確保 all constructors(所有的構造函數)都初始化了 object(對象)中的每一樣東西。 這個規則很容易遵守,但重要的是不要把 assignment(賦值)和 initialization(初始化)搞混。考慮下面這個表現一個通訊錄條目的 class(類)的 constructor(構造函數): ``` class PhoneNumber { ... }; class ABEntry { // ABEntry = "Address Book Entry" public: ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones); private: std::string theName; std::string theAddress; std::list<PhoneNumber> thePhones; int num TimesConsulted; }; ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones) { theName = name; // these are all assignments, theAddress = address; // not initializations thePhones = phones; numTimesConsulted = 0; } ``` 這樣做雖然使得 ABEntry objects(對象)具有了你所期待的值,但還不是最好的做法。C++ 的規則規定一個 object(對象)的 data members(數據成員)在進入 constructor(構造函數)的函數體之前被初始化。在 ABEntry 的 constructor(構造函數)內,theName,theAddress 和 thePhones 不是 being initialized(被初始化),而是 being assigned(被賦值)。initialization(初始化)發生得更早——在進入 ABEntry 的 constructor(構造函數)的函數體之前,它們的 default constructors(缺省的構造函數)已經被自動調用。但不包括 numTimesConsulted,因為它是一個 built-in type(內建類型)。不能保證它在被賦值之前被初始化。 一個更好的寫 ABEntry constructor(構造函數)的方法是用 member initialization list(成員初始化列表)來代替 assignments(賦值): ``` ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones) : theName(name), theAddress(address), // these are now all initializations thePhones(phones), numTimesConsulted(0) {} // the ctor body is now empty ``` 這個 constructor(構造函數)的最終結果和前面那個相同,但是通常它有更高的效率。assignment-based(基于賦值)的版本會首先調用 default constructors(缺省構造函數)初始化 theName,theAddress 和 thePhones,然而很快又在 default-constructed(缺省構造)的值之上賦予新值。那些 default constructions(缺省構造函數)所做的工作被浪費了。而 member initialization list(成員初始化列表)的方法避免了這個問題,因為 initialization list(初始化列表)中的 arguments(參數)就可以作為各種 data members(數據成員)的 constructor(構造函數)所使用的 arguments(參數)。在這種情況下,theName 從 name 中 copy-constructed(拷貝構造),theAddress 從 address 中 copy-constructed(拷貝構造),thePhones 從 phones 中 copy-constructed(拷貝構造)。對于大多數類型來說,只調用一次 copy constructor(拷貝構造函數)的效率比先調用一次 default constructor(缺省構造函數)再調用一次 copy assignment operator(拷貝賦值運算符)的效率要高(有時會高很多)。 對于 numTimesConsulted 這樣的 built-in type(內建類型)的 objects(對象),initialization(初始化)和 assignment(賦值)沒有什么不同,但為了統一性,最好是經由 member initialization(成員初始化)來 initialize(初始化)每一件東西。類似地,當你只想 default-construct(缺省構造)一個 data member(數據成員)時也可以使用 member initialization list(成員初始化列表),只是不必指定 initialization argument(初始化參數)而已。例如,如果 ABEntry 有一個不取得 parameters(參數)的 constructor(構造函數),它可以像這樣實現: ``` ABEntry::ABEntry() :theName(), // call theName's default ctor; theAddress(), // do the same for theAddress; thePhones(), // and for thePhones; numTimesConsulted(0) // but explicitly initialize {} // numTimesConsulted to zero ``` 因為對于那些在 member initialization list(成員初始化列表)中的,沒有 initializers(初始化器)的,user-defined types(用戶自定義類型)的 data members(數據成員),編譯器會自動調用其 default constructors(缺省構造函數),所以一些程序員會認為上面的方法有些過分。這也不難理解,但是一個方針是:在 initialization list(初始化列表)中總是列出每一個 data member(數據成員),這就可以避免一旦發生疏漏就必須回憶起可能是哪一個 data members(數據成員)沒有被初始化。例如,因為 numTimesConsulted 是一個 built-in type(內建類型),如果將它從 member initialization list(成員初始化列表)中刪除,就為 undefined behavior(未定義行為)打開了方便之門。 有時,即使是 built-in types(內建類型),initialization list(初始化列表)也必須使用。比如,const 或 references(引用)data members(數據成員)是必須 be initialized(被初始化)的,它們不能 be assigned(被賦值)(參見 Item 5)。為了避免記憶什么時候 data members(數據成員)必須在 member initialization list(成員初始化列表)中初始化,而什么時候又是可選的,最簡單的方法就是總是使用 initialization list(初始化列表)。它有時是必須的,而且它通常都比 assignments(賦值)更有效率。 很多 classes(類)有多個 constructors(構造函數),而每一個 constructor(構造函數)都有自己的 member initialization list(成員初始化列表)。如果有很多 data members(數據成員)和/或 base classes(基類),成倍增加的 initialization lists(初始化列表)的存在引起令人郁悶的重復(在列表中)和厭煩(在程序員中)。在這種情況下,不能不講道理地從列表中刪除那些 assignment(賦值)和 true initialization(真正的初始化)一樣工作的 data members(數據成員)項目,而是將 assignments(賦值)移到一個單獨的(當然是 private(私有)的)函數中,以供所有 constructors(構造函數)調用。這個方法對于那些 true initial values(真正的初始值)是從文件中讀入或從數據庫中檢索出來的 data members(數據成員)特別有幫助。然而,通常情況下,true member initialization(真正的成員初始化)(經由一個 initialization list(初始化列表))比經由 assignment(賦值)來進行的 pseudo-initialization(假初始化)更可取。 C++ 并非變幻莫測的方面是一個 object(對象)的數據被初始化的順序。這個順序總是相同的:base classes(基類)在 derived classes(派生類)之前被初始化(參見 Item 12),在一個 class(類)內部,data members(數據成員)按照它們被聲明的順序被初始化。例如,在 ABEntry 中,theName 總是首先被初始化,theAddress 是第二個,thePhones 第三,numTimesConsulted 最后。即使它們在 member initialization list(成員初始化列表)中以一種不同的順序排列(這不幸合法),這依然是成立的。為了避免讀者混淆,以及一些模糊不清的行為引起錯誤的可能性,initialization list(初始化列表)中的 members(成員)的排列順序應該總是與它們在 class(類)中被聲明的順序保持一致。 一旦處理了 built-in types(內建類型)的 non-member objects(非成員對象)的顯式初始化,而且確保你的 constructors(構造函數)使用 member initialization list(成員初始化列表)初始化了它的 base classes(基類)和 data members(數據成員),那就只剩下一件事情需要費心了。那就是——深呼吸先——定義在不同 translation units(轉換單元)中的 non-local static objects(非局部靜態對象)的 initialization(初始化)的順序。 讓我們一片一片地把這個詞組拆開。 一個 static object(靜態對象)的生存期是從它創建開始直到程序結束。stack and heap-based objects(基于堆棧的對象)就被排除在外了。包括 global objects(全局對象),objects defined at namespace scope(定義在命名空間范圍內的對象),objects declared static inside classes(在類內部聲明為靜態的對象),objects declared static inside functions(在函數內部聲明為靜態的對象)和 objects declared static at file scope(在文件范圍內被聲明為靜態的對象)。static objects inside functions(在函數內部的靜態對象)以 local static objects(局部靜態對象)(因為它局部于函數)為人所知,其它各種 static objects(靜態對象)以 non-local static objects(非局部靜態對象)為人所知。程序結束時 static objects(靜態對象)會自動銷毀,也就是當 main 停止執行時會自動調用它們的 destructors(析構函數)。 一個 translation unit(轉換單元)是可以形成一個單獨的 object file(目標文件)的 source code(源代碼)。基本上是一個單獨的 source file(源文件),再加上它全部的 #include 文件。 我們關心的問題是這樣的:包括至少兩個分別編譯的 source files(源文件),每一個中都至少包含一個 non-local static object(非局部靜態對象)(也就是說,global(全局)的,at namespace scope(命名空間范圍)的,static in a class(類內)的或 at file scope(文件范圍)的 object(對象))。實際的問題是這樣的:如果其中一個 translation unit(轉換單元)內的一個 non-local static object(非局部靜態對象)的 initialization(初始化)用到另一個 translation unit(轉換單元)內的non-local static object(非局部靜態對象),它所用到的 object(對象)可能沒有被初始化,因為 the relative order of initialization of non-local static objects defined in different translation units is undefined(定義在不同轉換單元內的非局部靜態對象的初始化的相對順序是沒有定義的)。 一個例子可以幫助我們。假設你有一個 FileSystem class(類),可以使 Internet 上的文件看起來就像在本地。因為你的 class(類)使得世界看起來好像只有一個單獨的 file system(文件系統),你可以在 global(全局)或 namespace(命名空間)范圍內創建一個專門的 object(對象)來代表這個單獨的 file system(文件系統): ``` class FileSystem { // from your library public: ... std::size_t numDisks() const; // one of many member functions ... }; extern FileSystem tfs; // object for clients to use; // "tfs" = "the file system" ``` 一個 FileSystem object(對象)絕對是舉足輕重的,所以在 theFileSystem object(對象)被創建之前就使用將會損失慘重。 現在假設一些客戶為一個 file system(文件系統)中的目錄創建了一個 class(類),他們的 class(類)使用了 theFileSystem object(對象): ``` class Directory { // created by library client public: Directory( params ); ... }; Directory::Directory( params ) { ... std::size_t disks = tfs.numDisks(); // use the tfs object ... } ``` 更進一步,假設這個客戶決定為臨時文件創建一個單獨的 Directory object(對象): ``` Directory tempDir( params ); // directory for temporary files ``` 現在 initialization order(初始化順序)的重要性變得明顯了:除非 tfs 在 tempDir 之前初始化,否則,tempDir 的 constructor(構造函數)就會在 tfs 被初始化之前試圖使用它。但是,tfs 和 tempDir 是被不同的人于不同的時間在不同的 source files(源文件)中創建的——它們是定義在不同 translation units(轉換單元)中的 non-local static objects(非局部靜態對象)。你怎么能確保 tfs 一定會在 tempDir 之前被初始化呢? 你不能。重申一遍,the relative order of initialization of non-local static objects defined in different translation units is undefined(定義在不同轉換單元內的非局部靜態對象的初始化的相對順序是沒有定義的)。這是有原因的。決定 non-local static objects(非局部靜態對象)的“恰當的”初始化順序是困難的,非常困難,以至于無法完成。在最常見的形式下——多個 translation units(轉換單元)和 non-local static objects(非局部靜態對象)通過 implicit template instantiations(隱式模板實例化)產生(這本身可能也是經由 implicit template instantiations(隱式模板實例化)引起的)——不僅不可能確定一個正確的 initialization(初始化)順序,甚至不值得去尋找可能確定正確順序的特殊情況。 幸運的是,一個小小的設計改變從根本上解決了這個問題。全部要做的就是將每一個 non-local static object(非局部靜態對象)移到它自己的函數中,在那里它被聲明為 static(靜態)。這些函數返回它所包含的 objects(對象)的引用。客戶可以調用這些函數來代替直接涉及那些 objects(對象)。換一種說法,就是用 local static objects(局部靜態對象)取代 non-local static objects(非局部靜態對象)。(aficionados of design patterns(設計模式迷們)會認出這是 Singleton 模式的通用實現)。 這個方法建立在 C++ 保證 local static objects(局部靜態對象)的初始化發生在因為調用那個函數而第一次遇到那個 object(對象)的 definition(定義)時候。所以,如果你用調用返回 references to local static objects(局部靜態對象的引用)的函數的方法取代直接訪問 non-local static objects(非局部靜態對象)的方法,你將確保你取回的 references(引用)引向 initialized objects(已初始化的對象)。作為一份額外收獲,如果你從不調用這樣一個仿效 non-local static object(非局部靜態對象)的函數,你就不會付出創建和銷毀這個 object(對象)的代價,而一個 true non-local static objects(真正的非局部靜態對象)則不會有這樣的效果。 以下就是這項技術在 tfs 和 tempDir 上的應用: ``` class FileSystem { ... }; // as before FileSystem& tfs() // this replaces the tfs object; it could be { // static in the FileSystem class static FileSystem fs; // define and initialize a local static object return fs; // return a reference to it } class Directory { ... }; // as before Directory::Directory( params ) // as before, except references to tfs are { // now to tfs() ... std::size_t disks = tfs().numDisks(); ... } Directory& tempDir() // this replaces the tempDir object; it { // could be static in the Directory class static Directory td; // define/initialize local static object return td; // return reference to it } ``` 這個改良系統的客戶依然可以按照他們已經習慣的方法編程,只是他們現在應該用 tfs() 和 tempDir() 來代替 tfs 和 tempDir。也就是說,他們應該使用返回 references to objects(對象引用)的函數來代替使用 objects themselves(對象自身)。 按照以下步驟來寫 reference-returning functions(返回引用的函數)總是很簡單:在第 1 行定義并初始化一個 local static object(局部靜態對象),在第 2 行返回它。這樣的簡單使它們成為 inlining(內聯化)的完美的候選者,特別是在它們被頻繁調用的時候(參見 Item 30)。在另一方面,這些函數包含 static object(靜態對象)的事實使它們在 multithreaded systems(多線程系統)中會出現問題。更進一步,任何種類的 non-const static object(非常量靜態對象)—— local(局部)的或 non-local(非局部)的——在 multiple threads(多線程)存在的場合都會發生麻煩。解決這個麻煩的方法之一是在程序的 single-threaded(單線程)的啟動部分手動調用所有的 reference-returning functions(返回引用的函數)。以此來避免 initialization-related(與初始化相關)的混亂環境。 當然,用 reference-returning functions(返回引用的函數)來防止 initialization order problems(初始化順序問題)的想法首先依賴于你的 objects(對象)有一個合理的 initialization order(初始化順序)。如果你有一個系統,其中 object A 必須在 object B 之前初始化,但是 A 的初始化又依賴于 B 已經被初始化,你將遇到問題,坦白地講,你遇到大麻煩了。然而,如果你避開了這種病態的境遇,這里描述的方法會很好地為你服務,至少在 single-threaded applications(單線程應用)中是這樣。 避免在初始化之前使用 objects(對象),你只需要做三件事。首先,手動初始化 built-in types(內建類型)的 non-member objects(非成員對象)。第二,使用 member initialization lists(成員初始化列表)初始化一個 object(對象)的所有部分。最后,在設計中繞過搞亂定義在分離的 translation units(轉換單元)中的 non-local static objects(非局部靜態對象)initialization order(初始化順序)的不確定性。 Things to Remember * 手動初始化 built-in type(內建類型)的 objects(對象),因為 C++ 只在某些時候才會自己初始化它們。 * 在 constructor(構造函數)中,用 member initialization list(成員初始化列表)代替函數體中的 assignment(賦值)。initialization list(初始化列表)中 data members(數據成員)的排列順序要與它們在 class(類)中被聲明的順序相同。 * 通過用 local static objects(局部靜態對象)代替 non-local static objects(非局部靜態對象)來避免跨 translation units(轉換單元)的 initialization order problems(初始化順序問題)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看