<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C++高性能:優化代碼運行效率的藝術1 C++簡介

      1 C++ 簡介

      本書旨在為您提供編寫高效應用程序的堅實基礎,并深入了解在現代 C++ 中實現庫的策略。我嘗試以實用的方法來解釋當今 C++ 的工作原理,其中從 C++ 11 到 C++ 20 的現代特性已成為該語言的自然組成部分,而不是從歷史的角度來看待 C++。

      在本章中,我們將:

      • 介紹一些對于編寫健壯、高性能應用程序至關重要的 C++ 特性
      • 討論 C++ 相對于其他競爭語言的優缺點
      • 介紹本書中使用的庫和編譯器

      1.1 為什么選擇 C++?

      讓我們首先探討當今使用 C++ 的一些原因。簡而言之,C++ 是一種高度可移植的語言,提供零成本的抽象。此外,C++ 還使程序員能夠編寫和管理大型、富有表現力且健壯的代碼庫。在本節中,我們將探討零成本抽象的含義,比較 C++ 抽象與其他語言的抽象,并討論可移植性和健壯性,以及這些特性為何如此重要。

      讓我們先來了解一下零成本抽象。

      1.1.1 零成本抽象(Zero-cost abstractions)

      活躍的代碼庫不斷增長。開發代碼庫的開發人員越多,代碼庫就越龐大。為了管理日益增長的代碼庫復雜性,我們需要變量、函數和類等語言特性,以便能夠創建具有自定義名稱和接口的抽象,從而隱藏實現細節。

      C++ 允許我們定義自己的抽象,但它也自帶一些內置抽象。例如,C++ 函數的概念本身就是一種控制程序流的抽象。基于范圍的 for 循環是另一個內置抽象的例子,它使得更直接地迭代一系列值成為可能。作為程序員,我們在開發程序的過程中不斷添加新的抽象。同樣,新版本的 C++ 也為語言和標準庫引入了新的抽象。但不斷添加抽象和新的間接層級是有代價的——效率。零成本抽象正是為此而生。C++ 提供的許多抽象在運行時空間和時間方面的成本非常低。

      使用 C++,您可以根據需要自由地使用內存地址和其他與計算機相關的低級術語。然而,在大型軟件項目中,最好使用能夠處理應用程序正在執行的操作的術語來表達代碼,而讓庫來處理與計算機相關的術語。圖形應用程序的源代碼可能處理鉛筆、顏色和濾鏡,而游戲可能處理吉祥物、城堡和蘑菇。在性能至關重要的 C++ 庫代碼中,可以隱藏與計算機相關的低級術語(例如內存地址)。

      1.1.1.1 編程語言與機器碼抽象

      為了減輕程序員處理計算機相關術語的負擔,現代編程語言使用了抽象的概念,例如,字符串列表可以被處理并視為字符串列表,而不是地址列表,因為地址列表一旦出現哪怕是最輕微的拼寫錯誤,我們很容易就會忘記它的含義。抽象不僅使程序員避免了錯誤,還通過使用應用程序領域的概念,使代碼更具表達力。換句話說,代碼的表達方式比使用抽象的編程關鍵字更接近口語。

      如今,C++ 和 C 是兩種完全不同的語言。盡管如此,C++ 與 C 高度兼容,并繼承了 C 的許多語法和慣用語。為了舉例說明 C++ 抽象的概念,我將展示如何在 C 和 C++ 中解決一個問題。

      請看以下 C/C++ 代碼片段,它們分別對應以下問題:“這份書單中有多少本《哈姆雷特》?”

      我們將從 C 版本開始:

      // C version
      struct string_elem_t { const char* str_; string_elem_t* next_; };
      int num_hamlet(string_elem_t* books) {
        const char* hamlet = "Hamlet";
        int n = 0;
        string_elem_t* b; 
        for (b = books; b != 0; b = b->next_)
          if (strcmp(b->str_, hamlet) == 0)
            ++n;
        return n;
      }
      

      使用 C++ 的等效版本如下所示:

      // C++ version
      int num_hamlet(const std::forward_list<std::string>& books) {
        return std::count(books.begin(), books.end(), "Hamlet");
      }
      

      雖然 C++ 版本仍然更像一種機器人語言而非人類語言,但由于更高層次的抽象,許多編程術語已被摒棄。以下是前兩段代碼片段之間的一些顯著區別:

      • 指向原始內存地址的指針完全不可見
      • std::forward_liststd::string 容器使用 string_elem_t 替換了手動構建的鏈表
      • std::count() 函數替換了 for 循環和 if 語句
      • std::strng 類提供了比 char* 和 strcmp() 更高級別的抽象。
        基本上,兩個版本的 num_hamlet() 都會轉換為大致相同的機器碼,但 C++ 的語言特性使得庫可以隱藏與計算機相關的術語,例如指針。許多現代 C++ 語言特性都可以看作是 C 語言基本功能之上的抽象。

      1.1.1.2 其他語言中的抽象

      大多數編程語言都基于抽象,這些抽象會被轉換為機器碼并由 CPU 執行。C++ 已經發展成為一種高度表達的語言,就像當今許多其他流行的編程語言一樣。C++ 與大多數其他語言的區別在于,其他語言以犧牲運行時性能為代價來實現這些抽象,而 C++ 始終致力于在運行時以零成本實現其抽象。這并不意味著用 C++ 編寫的應用程序默認比用 C# 等編寫的等效應用程序更快。相反,這意味著通過使用 C++,您可以根據需要對發出的機器碼指令和內存占用進行細粒度的控制。

      公平地說,如今極少需要追求最佳性能,而像其他語言那樣為了縮短編譯時間、進行垃圾回收或提高安全性而犧牲性能,在很多情況下更為合理。

      1.1.1.3 零開銷原則

      “零開銷抽象”是一個常用術語,但它也存在一個問題——大多數抽象通常都會產生開銷。即使不是在程序運行過程中,也幾乎總是會在后續的某個階段產生開銷,例如較長的編譯時間、難以解讀的編譯錯誤消息等等。通常更有趣的話題是零開銷原則。C++ 的發明者 Bjarne Stroustrup 對零開銷原則的定義如下:

      • 你不使用的,無需付費
      • 你使用的代碼,你不可能寫得更好

      這是 C++ 的核心原則,也是該語言發展過程中非常重要的一個方面。你可能會問,為什么呢?基于此原則構建的抽象將被注重性能的程序員廣泛接受和使用,尤其是在性能至關重要的環境中。找到許多人認同并廣泛使用的抽象,可以使我們的代碼庫更易于閱讀和維護。

      相反,C++ 語言中不完全遵循零開銷原則的特性往往會被程序員、項目和公司拋棄。這類特性中最值得注意的兩個特性是異常(不幸的是)和運行時類型信息 (RTTI)。即使不使用,這兩個特性也會對性能產生影響。但我強烈建議使用異常,除非您有充分的理由不使用。與使用其他錯誤處理機制相比,在大多數情況下,異常的性能開銷可以忽略不計。

      1.1.2 可移植性(Portability)

      C++ 長期以來一直是一種流行且功能全面的語言。它與 C 高度兼容,并且語言中幾乎沒有被棄用的功能,無論好壞。C++ 的歷史和設計使其成為一種高度可移植的語言,而現代 C++ 的演進確保了它將在未來很長一段時間內保持這種狀態。 C++ 是一種充滿活力的語言,編譯器供應商目前正在快速實現新的語言特性,這令人矚目。

      1.1.3 健壯性(Robustness)

      除了性能、表達能力和可移植性之外,C++ 還提供了一系列語言特性,使程序員能夠編寫健壯的代碼。

      根據作者的經驗,健壯性并非指編程語言本身的強度——任何語言都可以編寫健壯的代碼。更確切地說,C++ 提供的一些特性,例如嚴格的資源所有權、常量正確性、值語義、類型安全以及對象的確定性析構,使編寫健壯代碼變得更加容易。也就是說,能夠編寫易于使用且難以濫用的函數、類和庫。

      1.1.4 當今的 C++

      總而言之,當今的 C++ 使程序員能夠編寫富有表現力且健壯的代碼庫,同時仍然可以選擇針對幾乎任何硬件平臺或實時需求。在當今最常用的語言中,只有 C++ 具備所有這些特性。

      1.2 C++ 與其他語言的比較

      自 C++ 首次發布以來,出現了眾多應用程序類型、平臺和編程語言。盡管如此,C++ 仍然是一種廣泛使用的語言,其編譯器適用于大多數平臺。截至目前,主要的例外是 Web 平臺,JavaScript 及其相關技術是其基礎。然而,Web 平臺正在發展,能夠執行以前僅在桌面應用程序中才有可能,而在此背景下,C++ 已通過 Emscripten、asm.js 和 WebAssembly 等技術進入 Web 應用程序。

      在本節中,我們將首先從性能角度比較競爭語言。接下來,我們將比較 C++ 與其他語言相比如何處理對象所有權和垃圾收集,以及如何避免 C++ 中的空對象。最后,我們將介紹 C++ 的一些缺點,用戶在考慮該語言是否適合其需求時應牢記這些缺點。

      1.2.1 競爭語言和性能

      為了理解 C++ 與其他編程語言相比如何實現其性能,讓我們討論一下 C++ 與大多數其他現代編程語言之間的一些根本區別。

      為簡單起見,本節將重點比較 C++ 與 Java,盡管大部分比較也適用于其他基于垃圾收集器的編程語言,例如Python、 C# 和 JavaScript。

      首先,Java 會先編譯為字節碼,然后在應用程序執行時將其編譯為機器碼,而大多數 C++ 實現則直接將源代碼編譯為機器碼。雖然字節碼和即時編譯器理論上可以實現與預編譯機器碼相同(甚至更好)的性能,但目前來看,它們通常無法達到這一水平。不過,公平地說,在大多數情況下,它們的性能已經足夠好了。

      其次,Java 處理動態內存的方式與 C++ 完全不同。在 Java 中,內存由垃圾收集器自動釋放,而 C++ 程序則手動或通過引用計數機制處理內存釋放。垃圾收集器確實可以防止內存泄漏,但會犧牲性能和可預測性。

      第三,Java 將所有對象放置在單獨的堆分配中,而 C++ 允許程序員將對象同時放置在堆棧和堆上。在 C++ 中,還可以在單個堆分配中創建多個對象。這可以帶來巨大的性能提升,原因有二:創建對象時無需始終分配動態內存,并且多個相關對象可以在內存中相鄰放置。

      以下示例展示了內存的分配方式。C++ 函數使用堆棧來存儲對象和整數;而 Java 將對象放置在堆中:

      image

      現在看下一個示例,分別了解使用 C++ 和 Java 時,Car 對象數組在內存中的存放方式:

      image

      C++ 中的 Vector 包含放置在一個連續內存塊中的實際 Car 對象,而 Java 中的對應對象是一個包含 Car 對象引用的連續內存塊。在 Java 中,這些對象是單獨分配的,這意味著它們可以位于堆的任何位置。

      這會影響性能,因為在本例中,Java 實際上需要在 Java 堆空間中執行五次分配。這也意味著,每當應用程序迭代列表時,C++ 都會獲得性能提升,因為訪問附近的內存位置比訪問內存中的幾個隨機位置更快。

      1.2.2 與性能無關的 C++ 語言特性

      人們很容易認為只有在性能是主要考慮因素時才應該使用 C++。然而,C++ 的手動內存處理是否只會增加代碼庫的復雜性,從而可能導致內存泄漏和難以追蹤的錯誤?

      這在幾個 C++ 版本之前可能確實如此,但現代 C++ 程序員依賴于標準庫中提供的容器和智能指針類型。過去 10 年新增的大量 C++ 特性使該語言更加強大,也更易于使用。

      我想在這里重點介紹一些 C++ 中一些古老但強大的特性,它們與健壯性而非性能相關,并且很容易被忽視:值語義、常量正確性、所有權、確定性析構函數和引用。

      1.2.2.1 值語義(Value semantics)

      C++ 支持值語義以及引用語義。值語義允許我們通過值傳遞對象,而不僅僅是傳遞對象的引用。在 C++ 中,值語義是默認的,這意味著傳遞類或結構體的實例時,其行為與傳遞 int、float 或任何其他基本類型相同。要使用引用語義,我們需要顯式使用引用或指針。

      C++ 類型系統使我們能夠顯式聲明對象的所有權。比較以下 C++ 和 Java 中一個簡單類的實現。我們先從 C++ 版本開始:

      // C++
      class Bagel {
      public:
        Bagel(std::set<std::string> ts) : toppings_(std::move(ts)) {}
      private:
        std::set<std::string> toppings_;
      };
      

      Java 中相應的實現可能如下所示:

      // Java
      class Bagel {
        public Bagel(ArrayList<String> ts) { toppings_ = ts; }
        private ArrayList<String> toppings_;
      }
      

      在 C++ 版本中,程序員聲明 toppings 完全由 Bagel 類封裝。如果程序員希望 topping 列表在多個 Bagel 之間共享,則需要將其聲明為某種類型的指針:如果所有權在多個 Bagel 之間共享,則聲明為 std::shared_ptr;如果其他人擁有 topping 列表并在程序執行時對其進行修改,則聲明為 std::weak_ptr。

      在 Java 中,對象通過共享所有權相互引用。因此,無法區分 topping 列表是否打算在多個 Bagel 之間共享,或者它是否在其他地方處理,或者(在大多數情況下)它完全由 Bagel 類擁有。

      比較以下函數;由于 Java(以及大多數其他語言)中每個對象默認都是共享的,程序員必須對諸如此類的細微錯誤采取預防措施:

      image

      1.2.2.2 常量正確性

      C++ 的另一個強大特性是能夠編寫完全符合常量規范的代碼,而 Java 和許多其他語言都缺乏這種特性。常量正確性意味著類的每個成員函數簽名都會明確告知調用者該對象是否會被修改;如果調用者嘗試修改聲明為 const 的對象,則編譯不會通過。在 Java 中,可以使用 final 關鍵字聲明常量,但這缺乏將成員函數聲明為 const 的能力。

      以下示例展示了如何使用 const 成員函數來防止對象的意外修改。在以下 Person 類中,成員函數 age() 被聲明為 const,因此不允許修改 Person 對象;而 set_age() 會修改該對象,因此不能被聲明為 const:

      class Person {
      public:
        auto age() const { return age_; }
        auto set_age(int age) { age_ = age; }
      private:
        int age_{};
      };
      

      還可以區分返回成員的可變引用和不可變引用。在以下 Team 類中,成員函數 leader() const 返回一個不可變的 Person 對象,而 leader() 返回一個可以修改的 Person 對象:

      class Team {
      public:
        auto& leader() const { return leader_; }
        auto& leader() { return leader_; }
      private:
        Person leader_{};
      };
      

      現在讓我們看看編譯器如何在我們嘗試修改不可變對象時幫助我們發現錯誤。在以下示例中,函數參數 teams 被聲明為 const,明確表明該函數不允許修改它們:

      void nonmutating_func(const std::vector<Team>& teams) {
        auto tot_age = 0;
        
        // Compiles, both leader() and age() are declared const
        for (const auto& team : teams) 
          tot_age += team.leader().age();
        // Will not compile, set_age() requires a mutable object
        for (auto& team : teams) 
          team.leader().set_age(20);
      }
      

      如果我們想要編寫一個可以修改 teams 對象的函數,只需刪除 const 即可。這會向調用者發出信號,表明該函數可以修改 teams 對象:

      void mutating_func(std::vector<Team>& teams) {
        auto tot_age = 0;
        
        // Compiles, const functions can be called on mutable objects
        for (const auto& team : teams) 
          tot_age += team.leader().age();
        // Compiles, teams is a mutable variable
        for (auto& team : teams) 
          team.leader().set_age(20);
      }
      

      1.2.2.3 對象所有權

      除了極少數情況外,C++ 程序員應該將內存處理交給容器和智能指針,而不要依賴手動內存處理。

      簡而言之,垃圾收集器Java 中的 ction 模型幾乎可以通過在 C++ 中為每個對象使用 std::shared_ptr 來模擬。需要注意的是,垃圾收集語言使用的分配跟蹤算法與 std::shared_ptr 不同。std::shared_ptr 是一個基于引用計數算法的智能指針,如果對象存在循環依賴,它將導致內存泄漏。垃圾收集語言擁有更復雜的方法來處理和釋放循環依賴對象。

      然而,強制嚴格所有權并非依賴于垃圾收集器,而是巧妙地避免了默認共享對象可能導致的細微錯誤,就像 Java 的情況一樣。

      如果程序員在 C++ 中最小化共享所有權,生成的代碼將更易于使用且更難被濫用,因為它可以強制類的用戶按照預期使用它。

      1.2.2.4 C++ 中的確定性銷毀

      在 C++ 中,對象的銷毀是確定性的。這意味著我們(可以)準確地知道對象何時被銷毀。對于像 Java 這樣的支持垃圾回收機制的語言來說,情況并非如此。在 Java 中,垃圾回收器會決定何時終止未引用的對象。

      在 C++ 中,我們可以可靠地撤銷對象生命周期內所做的事情。乍一看,這似乎是一件小事。但事實證明,它對如何在 C++ 中提供異常安全保障和處理資源(例如內存、文件句柄、互斥鎖等)有著巨大的影響。

      確定性析構也是使 C++ 具有可預測性的特性之一。這在程序員中非常受重視,也是性能關鍵型應用程序的必備條件。

      我們將在本書的后面部分花更多時間討論對象所有權、生命周期和資源管理。所以,如果現在還不太明白,也不必擔心。

      1.2.2.5 避免使用 C++ 引用導致空對象

      除了嚴格的所有權之外,C++ 還有引用的概念,這與 Java 中的引用不同。在內部,引用是一個指針,不允許為空或被重新指向;因此,將其傳遞給函數時不涉及復制。

      因此,C++ 中的函數簽名可以明確限制程序員傳遞空對象作為參數。在 Java 中,程序員必須使用文檔或注釋來指示非空參數。

      看一下這兩個用于計算球體體積的 Java 函數。如果傳遞空對象,第一個函數會拋出運行時異常,而第二個函數則會默默地忽略空對象。

      第一個 Java 實現如果傳遞空對象,則會拋出運行時異常:

      // Java
      float getVolume1(Sphere s) {
        float cube = Math.pow(s.radius(), 3);
        return (Math.PI * 4 / 3) * cube; 
      }
      

      第二個 Java 實現會默默地處理空對象:

      float getVolume2(Sphere s) { 
        float rad = s == null ? 0.0f : s.radius();
        float cube = Math.pow(rad, 3);
        return (Math.PI * 4 / 3) * cube;
      }
      

      在 Java 實現的兩個函數中,函數調用者必須檢查函數的實現,以確定是否允許使用空對象。

      在 C++ 中,第一個函數簽名通過使用不能為空的引用明確地只接受已初始化的對象。第二個版本使用指針作為參數,明確地表明可以處理空對象。

      作為引用傳遞的 C++ 參數表明不允許使用空值:

      auto get_volume1(const Sphere& s) {   
        auto cube = std::pow(s.radius(), 3.f);
        auto pi = 3.14f;
        return (pi * 4.f / 3.f) * cube;
      }
      

      C++ 參數以指針形式傳遞時,表示正在處理空值:

      auto get_volume2(const Sphere* s) {
        auto rad = s ? s->radius() : 0.f;
        auto cube = std::pow(rad, 3);
        auto pi = 3.14f;
        return (pi * 4.f / 3.f) * cube;
      }
      

      在 C++ 中,能夠使用引用或值作為參數,可以立即告知 C++ 程序員該函數的預期用途。相反,在 Java 中,用戶必須檢查函數的實現,因為對象始終以指針形式傳遞,并且有可能為空。

      參考資料

      1.2.3 C++ 的缺點

      如果不提及 C++ 的一些缺點,將 C++ 與其他編程語言進行比較是不公平的。如前所述,C++ 需要學習的概念更多,因此更難以正確使用并充分發揮其潛力。然而,如果程序員能夠精通 C++,那么更高的復雜性就會轉化為優勢,代碼庫也會變得更加健壯,性能也會更好。

      然而,C++ 也存在一些缺點,這些缺點僅僅是缺點而已。其中最嚴重的缺點是編譯時間長和導入庫的復雜性。在 C++ 20 之前,C++ 一直依賴于過時的導入系統,導入的頭文件只需粘貼到包含它們的文件中即可。C++ 20 中引入的 C++ 模塊將解決該系統基于包含頭文件的某些問題,并且還將對大型項目的編譯時間有積極的影響。

      C++ 的另一個明顯缺點是缺乏提供的庫。其他語言通常包含大多數應用程序所需的所有庫,例如圖形、用戶界面、網絡、線程、資源處理等等,而 C++ 幾乎只提供了最基本的算法、線程以及(從 C++17 開始的)文件系統處理。對于其他所有功能,程序員都必須依賴外部庫。

      總而言之,盡管 C++ 的學習曲線比大多數其他語言更陡峭,但如果使用得當,C++ 的穩健性與許多其他語言相比是一個優勢。因此,盡管編譯時間較長且缺乏提供的庫,但我相信 C++ 非常適合大型項目,即使對于性能并非最高優先級的項目也是如此。

      1.3 本書中使用的庫和編譯器

      如前所述,C++ 提供的庫僅能滿足基本需求。因此,在本書中,我們將在必要時依賴外部庫。 C++ 世界中最常用的庫可能是 Boost 庫 (http://www.boost.org)。

      本書的某些部分在標準 C++ 庫不足的情況下使用了 Boost 庫。我們將僅使用 Boost 庫中僅包含頭文件的部分,這意味著您自己使用它們不需要任何特定的構建設置;您只需包含指定的頭文件即可。

      此外,我們將使用 Google Benchmark(一個微基準測試支持庫)來評估小代碼片段的性能。Google Benchmark 將在第 3 章“性能分析和測量”中介紹。

      本書的源代碼庫位于 https://github.com/PacktPublishing/Cpp-High-Performance-Second-Edition,并使用 Google Test 框架,以便您更輕松地構建、運行和測試代碼。

      還值得一提的是,本書使用了許多 C++20 中的新功能。在撰寫本文時,我們使用的編譯器(Clang、GCC 和 Microsoft Visual C++)尚未完全實現其中一些功能。文中介紹的一些功能完全缺失或僅處于實驗階段。您可以在 https://en.cppreference.com/w/cpp/compiler_support 找到關于主流 C++ 編譯器當前狀態的精彩最新摘要。

      1.4 小結

      在本章中,我重點介紹了 C++ 的一些特性和缺點,以及它是如何發展到今天的狀態。此外,我們還從性能和健壯性的角度討論了 C++ 與其他語言相比的優缺點。

      在下一章中,我們將探討一些對 C++ 發展產生重大影響的現代且重要的特性。

      posted @ 2025-08-03 20:08  磁石空杯  閱讀(204)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产午夜精品福利91| 美日韩精品综合一区二区| www久久只有这里有精品| 2019香蕉在线观看直播视频| 国产精品美女久久久久久麻豆| 日韩一区二区三区日韩精品| 国产精品久久久久久久网| a∨变态另类天堂无码专区| 色综合伊人色综合网站| 尤物国产精品福利在线网| 日韩毛片在线视频x| 日本高清中文字幕免费一区二区| 老熟妇国产一区二区三区 | 婷婷丁香五月深爱憿情网| 国产在线精品中文字幕| 成全高清在线播放电视剧| 久久精品国产99国产精品严洲| 高清一区二区三区不卡视频| 中文字字幕在线中文乱码| 人妻久久久一区二区三区| 国产美女高潮流白浆视频| 久章草在线毛片视频播放| 狠狠久久五月综合色和啪| 波多野结衣的av一区二区三区 | 毛片av在线尤物一区二区| 成人精品网一区二区三区| 日韩在线观看 一区二区| 日韩成av在线免费观看| 无码免费中文字幕视频| 国产区精品视频自产自拍| 四虎成人精品国产永久免费| 精品人妻人人做人人爽夜夜爽| 在线观看精品视频网站| 无遮无挡爽爽免费视频| 亚洲综合成人av在线| 又湿又紧又大又爽A视频男| 亚洲国产另类久久久精品黑人| 安平县| 无码精品一区二区免费AV| 欧美熟妇乱子伦XX视频| 成人福利国产午夜AV免费不卡在线 |