日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

C++模板基礎及代碼實戰

來源: 責編: 時間:2023-12-06 17:12:40 312觀看
導讀一、C++ 模板概覽1.泛型編程的支持C++ 不僅為面向對象編程提供了語言支持,還支持泛型編程。正如第6章《設計可重用性》中討論的,泛型編程的目標是編寫可重用的代碼。C++ 中支持泛型編程的基本工具是模板。雖然模板不嚴

一、C++ 模板概覽

1.泛型編程的支持

C++ 不僅為面向對象編程提供了語言支持,還支持泛型編程。正如第6章《設計可重用性》中討論的,泛型編程的目標是編寫可重用的代碼。C++ 中支持泛型編程的基本工具是模板。雖然模板不嚴格是面向對象的特性,但它們可以與面向對象編程結合產生強大的效果。V5U28資訊網——每日最新資訊28at.com

V5U28資訊網——每日最新資訊28at.com

2.模板的核心

在過程化編程范式中,主要編程單元是過程或函數。函數之所以有用,主要是因為它們允許你編寫與特定值無關的算法,因此可以用于許多不同的值。例如,C++ 中的 sqrt() 函數計算調用者提供的值的平方根。只計算一個數字(如四)的平方根的函數不會特別有用!sqrt() 函數是針對一個參數編寫的,該參數是調用者傳遞的任何值的代表。V5U28資訊網——每日最新資訊28at.com

對象導向編程范式增加了對象的概念,對象將相關數據和行為組合在一起,但它并沒有改變函數和方法參數化值的方式。V5U28資訊網——每日最新資訊28at.com

3.模板的進階參數化

模板將參數化概念進一步擴展,允許你對類型以及值進行參數化。C++ 中的類型包括 int、double 等基本類型,以及 SpreadsheetCell、CherryTree 等用戶定義的類。有了模板,你可以編寫不僅與它將要給定的值無關,而且與這些值的類型無關的代碼。例如,你可以編寫一個堆棧類定義,而不是編寫用于存儲 int、Cars 和 SpreadsheetCells 的單獨堆棧類,這個堆棧類定義可以用于任何這些類型。V5U28資訊網——每日最新資訊28at.com

4.模板的使用和重要性

盡管模板是一項驚人的語言特性,但 C++ 中的模板在語法上可能令人困惑,許多程序員避免自己編寫模板。然而,每個程序員至少需要知道如何使用模板,因為它們被廣泛用于庫,例如 C++ 標準庫。本章教你如何在 C++ 中支持模板,重點是在標準庫中出現的方面。在此過程中,你將了解一些除了使用標準庫之外,你可以在程序中運用的巧妙特性。V5U28資訊網——每日最新資訊28at.com

二、類模板

1.類模板的定義和應用

類模板定義了一個類,其中一些變量的類型、方法的返回類型和/或方法的參數被指定為模板參數。類模板主要用于容器,即存儲對象的數據結構。這一節通過運行示例 Grid 容器來說明。為了保持示例的合理長度并足夠簡單以闡明特定要點,本章的不同部分將為 Grid 容器添加不在后續部分使用的功能。V5U28資訊網——每日最新資訊28at.com

2.編寫類模板

假設你想要一個通用的游戲棋盤類,可以用作國際象棋棋盤、跳棋棋盤、井字棋棋盤或任何其他二維游戲棋盤。為了使其具有通用性,你應該能夠存儲國際象棋棋子、跳棋棋子、井字棋棋子或任何類型的游戲棋子。V5U28資訊網——每日最新資訊28at.com

三、不使用模板編寫代碼

1.通過多態性建立通用游戲棋盤

在沒有模板的情況下,構建通用游戲棋盤的最佳方法是使用多態性來存儲通用的 GamePiece 對象。然后,你可以讓每個游戲的棋子從 GamePiece 類繼承。例如,在國際象棋游戲中,ChessPiece 將是 GamePiece 的派生類。通過多態性,編寫為存儲 GamePiece 的 GameBoard 也可以存儲 ChessPiece。因為可能需要復制 GameBoard,所以 GameBoard 需要能夠復制 GamePiece。這種實現使用多態性,所以一種解決方案是在 GamePiece 基類中添加一個純虛擬的 clone() 方法,派生類必須實現它以返回具體 GamePiece 的副本。V5U28資訊網——每日最新資訊28at.com

這是基本的 GamePiece 接口:V5U28資訊網——每日最新資訊28at.com

export class GamePiece {public:    virtual ~GamePiece() = default;    virtual std::unique_ptr<GamePiece> clone() const = 0;};

GamePiece 是一個抽象基類。具體類,如 ChessPiece,從它派生并實現 clone() 方法:V5U28資訊網——每日最新資訊28at.com

class ChessPiece : public GamePiece {public:    std::unique_ptr<GamePiece> clone() const override {        // 調用復制構造函數來復制這個實例        return std::make_unique<ChessPiece>(*this);    }};

2.GameBoard 的實現

GameBoard 的實現使用向量的向量和 unique_ptr 來存儲 GamePieces:V5U28資訊網——每日最新資訊28at.com

GameBoard::GameBoard(size_t width, size_t height) : m_width { width }, m_height { height } {    m_cells.resize(m_width);    for (auto& column : m_cells) {        column.resize(m_height);    }}GameBoard::GameBoard(const GameBoard& src) : GameBoard { src.m_width, src.m_height } {    // The ctor-initializer of this constructor delegates first to the    // non-copy constructor to allocate the proper amount of memory.    // The next step is to copy the data.    for (size_t i { 0 }; i < m_width; i++) {        for (size_t j { 0 }; j < m_height; j++) {            if (src.m_cells[i][j]) {                m_cells[i][j] = src.m_cells[i][j]->clone();            }        }    }}void GameBoard::verifyCoordinate(size_t x, size_t y) const {    if (x >= m_width) {        throw out_of_range { format("{} must be less than {}.", x, m_width) };    }    if (y >= m_height) {        throw out_of_range { format("{} must be less than {}.", y, m_height) };    }}void GameBoard::swap(GameBoard& other) noexcept {    std::swap(m_width, other.m_width);    std::swap(m_height, other.m_height);    std::swap(m_cells, other.m_cells);}void swap(GameBoard& first, GameBoard& second) noexcept {    first.swap(second);}GameBoard& GameBoard::operator=(const GameBoard& rhs) {    // Copy-and-swap idiom    GameBoard temp { rhs }; // Do all the work in a temporary instance.    swap(temp); // Commit the work with only non-throwing operations.    return *this;}const unique_ptr<GamePiece>& GameBoard::at(size_t x, size_t y) const {    verifyCoordinate(x, y);    return m_cells[x][y];}unique_ptr<GamePiece>& GameBoard::at(size_t x, size_t y) {    return const_cast<unique_ptr<GamePiece>&>(as_const(*this).at(x, y));}

在這個實現中,at() 返回指定位置的棋子的引用,而不是棋子的副本。GameBoard 作為二維數組的抽象,應該通過給出索引處的實際對象而不是對象的副本來提供數組訪問語義。V5U28資訊網——每日最新資訊28at.com

3.注意事項

這個實現為 at() 提供了兩個版本,一個返回非常量引用,另一個返回常量引用。V5U28資訊網——每日最新資訊28at.com

使用復制和交換習語(copy-and-swap idiom)用于賦值運算符,以及 Scott Meyer 的 const_cast() 模式來避免代碼重復。V5U28資訊網——每日最新資訊28at.com

4.GameBoard 類的使用

GameBoard chessBoard { 8, 8 };auto pawn { std::make_unique<ChessPiece>() };chessBoard.at(0, 0) = std::move(pawn);chessBoard.at(0, 1) = std::make_unique<ChessPiece>();chessBoard.at(0, 1) = nullptr;

這個 GameBoard 類運行得相當好,它可以用于國際象棋棋盤的創建和棋子的放置。V5U28資訊網——每日最新資訊28at.com

四、類模板實現的 Grid 類

1.GameBoard 的局限性

在上一節中的 GameBoard 類雖然實用,但有其局限性。首先,你無法使用 GameBoard 來按值存儲元素;它總是存儲指針。更嚴重的問題與類型安全有關。GameBoard 中的每個單元格都存儲一個 unique_ptr<GamePiece>。即使你存儲的是 ChessPieces,當你使用 at() 請求特定單元格時,你將得到一個 unique_ptr<GamePiece>。這意味著你必須將檢索到的 GamePiece 向下轉型為 ChessPiece 才能使用 ChessPiece 的特定功能。GameBoard 的另一個缺點是它不能用于存儲原始類型,如 int 或 double,因為單元格中存儲的類型必須派生自 GamePiece。V5U28資訊網——每日最新資訊28at.com

2.實現通用 Grid 類

因此,如果你能編寫一個通用的 Grid 類來存儲 ChessPieces、SpreadsheetCells、ints、doubles 等就很好了。在 C++ 中,你可以通過編寫類模板來實現這一點,這允許你編寫一個不指定一個或多個類型的類。然后客戶端通過指定他們想要使用的類型來實例化模板。這就是所謂的泛型編程。V5U28資訊網——每日最新資訊28at.com

3.泛型編程的優勢

泛型編程的最大優勢是類型安全。類及其方法中使用的類型是具體類型,而不是像上一節中多態解決方案那樣的抽象基類類型。例如,假設不僅有 ChessPiece,還有 TicTacToePiece:V5U28資訊網——每日最新資訊28at.com

class TicTacToePiece : public GamePiece {public:    std::unique_ptr<GamePiece> clone() const override {        // 調用復制構造函數來復制這個實例        return std::make_unique<TicTacToePiece>(*this);    }};

使用上一節中的多態解決方案,沒有什么能阻止你在同一個棋盤上存儲井字棋棋子和國際象棋棋子:V5U28資訊網——每日最新資訊28at.com

GameBoard chessBoard { 8, 8 };chessBoard.at(0, 0) = std::make_unique<ChessPiece>();chessBoard.at(0, 1) = std::make_unique<TicTacToePiece>();

這樣做的一個大問題是,你需要記住一個單元格存儲了什么,以便在調用 at() 時執行正確的向下轉型。V5U28資訊網——每日最新資訊28at.com

五、Grid 類模板的定義

1.類模板的語法

為了理解類模板,檢查其語法非常有幫助。以下示例展示了如何將 GameBoard 類修改為模板化的 Grid 類。代碼后面會詳細解釋語法。請注意,類名已從 GameBoard 改為 Grid。V5U28資訊網——每日最新資訊28at.com

2.使用值語義實現 Grid 類

與 GameBoard 實現中使用的多態指針語義相比,我選擇使用值語義而不是多態來實現這個解決方案。m_cells 容器存儲實際對象,而不是指針。與指針語義相比,使用值語義的一個缺點是不能有真正的空單元格;也就是說,單元格必須始終包含某個值。使用指針語義時,空單元格存儲 nullptr。 std::optional 在這里提供了幫助。它允許你在仍然有表示空單元格的方法的同時使用值語義。V5U28資訊網——每日最新資訊28at.com

template <typename T>class Grid {public:    explicit Grid(size_t width = DefaultWidth, size_t height = DefaultHeight);    virtual ~Grid() = default;    // Explicitly default a copy constructor and assignment operator.    Grid(const Grid& src) = default;    Grid& operator=(const Grid& rhs) = default;    // Explicitly default a move constructor and assignment operator.    Grid(Grid&& src) = default;    Grid& operator=(Grid&& rhs) = default;    std::optional<T>& at(size_t x, size_t y);    const std::optional<T>& at(size_t x, size_t y) const;    size_t getHeight() const { return m_height; }    size_t getWidth() const { return m_width; }    static const size_t DefaultWidth { 10 };    static const size_t DefaultHeight { 10 };private:    void verifyCoordinate(size_t x, size_t y) const;    std::vector<std::vector<std::optional<T>>> m_cells;    size_t m_width { 0 }, m_height { 0 };};

3.類模板的詳細解讀

export template <typename T>:這一行表示接下來的類定義是一個關于類型 T 的模板,并且它正在從模塊中導出。template 和 typename 是 C++ 中的關鍵字。如前所述,模板在類型上“參數化”,就像函數在值上“參數化”一樣。V5U28資訊網——每日最新資訊28at.com

使用模板類型參數名(如 T)來表示調用者將作為模板類型參數傳遞的類型。T 的名稱沒有特殊含義——你可以使用任何你想要的名稱。V5U28資訊網——每日最新資訊28at.com

4.關于模板類型參數的注意事項

出于歷史原因,你可以使用關鍵字 class 而不是 typename 來指定模板類型參數。因此,許多書籍和現有程序使用類似 template <class T> 的語法。然而,在這種情況下使用 class 這個詞是令人困惑的,因為它暗示類型必須是一個類,這實際上并不正確。類型可以是類、結構體、聯合、語言的原始類型,如 int 或 double 等。V5U28資訊網——每日最新資訊28at.com

六、Grid 類模板與 GameBoard 類的對比

1.數據成員的變化

在之前的 GameBoard 類中,m_cells 數據成員是指針的向量的向量,這需要特殊的復制代碼,因此需要拷貝構造函數和拷貝賦值操作符。在 Grid 類中,m_cells 是可選值的向量的向量,所以編譯器生成的拷貝構造函數和賦值操作符是可以的。V5U28資訊網——每日最新資訊28at.com

2.顯式默認構造函數和操作符

一旦你有了用戶聲明的析構函數,就不推薦編譯器隱式生成拷貝構造函數或拷貝賦值操作符,因此 Grid 類模板顯式地將它們默認化。它還顯式默認化了移動構造函數和移動賦值操作符。以下是顯式默認的拷貝賦值操作符:V5U28資訊網——每日最新資訊28at.com

Grid& operator=(const Grid& rhs) = default;

可以看到,rhs 參數的類型不再是 const GameBoard&,而是 const Grid&。在類定義內,編譯器會在需要時將 Grid 解釋為 Grid<T>,但如果你愿意,也可以顯式地使用 Grid<T>:V5U28資訊網——每日最新資訊28at.com

Grid<T>& operator=(const Grid<T>& rhs) = default;

然而,在類定義外,你必須使用 Grid<T>。當你編寫類模板時,你過去認為的類名(Grid)實際上是模板名。當你想談論實際的 Grid 類或類型時,你必須使用模板 ID,即 Grid<T>,這些是針對特定類型(如 int、SpreadsheetCell 或 ChessPiece)的 Grid 類模板的實例化。V5U28資訊網——每日最新資訊28at.com

3.at() 方法的更新

由于 m_cells 不再存儲指針,而是存儲可選值,at() 方法現在返回 std::optional<T> 而不是 unique_ptrs,即可以有類型 T 的值,也可以為空的 optionals:V5U28資訊網——每日最新資訊28at.com

std::optional<T>& at(size_t x, size_t y);const std::optional<T>& at(size_t x, size_t y) const;

七、Grid 類模板的方法定義

1.模板方法定義格式

每個 Grid 模板的方法定義都必須以 template <typename T> 說明符開頭。構造函數如下所示:V5U28資訊網——每日最新資訊28at.com

template <typename T>Grid<T>::Grid(size_t width, size_t height) : m_width { width }, m_height { height } {    m_cells.resize(m_width);    for (auto& column : m_cells) {        column.resize(m_height);    }}

注意:類模板的方法定義需要對使用該類模板的任何客戶端代碼可見。這對方法定義的位置施加了一些限制。通常,它們直接放在類模板定義本身的同一文件中。本章后面討論了繞過這一限制的一些方法。V5U28資訊網——每日最新資訊28at.com

2.類名和方法定義

請注意,:: 前的類名是 Grid<T>,而不是 Grid。在所有方法和靜態數據成員定義中,你必須指定 Grid<T> 作為類名。構造函數的主體與 GameBoard 構造函數相同。其他方法定義也類似于 GameBoard 類中的對應方法,但有適當的模板和 Grid<T> 語法變化:V5U28資訊網——每日最新資訊28at.com

template <typename T>void Grid<T>::verifyCoordinate(size_t x, size_t y) const {    if (x >= m_width) {        throw std::out_of_range { std::format("{} must be less than {}.", x, m_width) };    }    if (y >= m_height) {        throw std::out_of_range { std::format("{} must be less than {}.", y, m_height) };    }}template <typename T>const std::optional<T>& Grid<T>::at(size_t x, size_t y) const {    verifyCoordinate(x, y);    return m_cells[x][y];}template <typename T>std::optional<T>& Grid<T>::at(size_t x, size_t y) {    return const_cast<std::optional<T>&>(std::as_const(*this).at(x, y));}

3.類模板方法的默認值

注意:如果類模板方法的實現需要某個模板類型參數的默認值(例如 T),則可以使用 T{} 語法。T{} 調用對象的默認構造函數(如果 T 是類類型),或生成零(如果 T 是基本類型)。這種語法稱為零初始化語法。它是為尚不知道類型的變量提供合理默認值的好方法。V5U28資訊網——每日最新資訊28at.com

八、使用 Grid 類模板

1.模板實例化

當你想要創建 Grid 對象時,不能單獨使用 Grid 作為類型;你必須指定將存儲在該 Grid 中的類型。為特定類型創建類模板對象稱為實例化模板。以下是一個示例:V5U28資訊網——每日最新資訊28at.com

Grid<int> myIntGrid; // 聲明一個存儲 int 的網格,使用構造函數的默認參數。Grid<double> myDoubleGrid { 11, 11 }; // 聲明一個 11x11 的 double 類型網格。myIntGrid.at(0, 0) = 10;int x { myIntGrid.at(0, 0).value_or(0) };Grid<int> grid2 { myIntGrid }; // 拷貝構造函數Grid<int> anotherIntGrid;anotherIntGrid = grid2; // 賦值操作符

請注意 myIntGrid、grid2 和 anotherIntGrid 的類型是 Grid<int>。你不能在這些網格中存儲 SpreadsheetCells 或 ChessPieces;如果嘗試這樣做,編譯器將生成錯誤。V5U28資訊網——每日最新資訊28at.com

2.使用 value_or()

還要注意 value_or() 的使用。at() 方法返回一個可選引用,可能包含值也可能不包含。value_or() 方法在可選項中有值時返回該值;否則,它返回給 value_or() 的參數。V5U28資訊網——每日最新資訊28at.com

3.模板類型的重要性

模板類型的指定非常重要;以下兩行都無法編譯:V5U28資訊網——每日最新資訊28at.com

Grid test; // 無法編譯Grid<> test; // 無法編譯

如果你想聲明一個接受 Grid 對象的函數或方法,你必須在 Grid 類型中指定存儲在網格中的類型:V5U28資訊網——每日最新資訊28at.com

void processIntGrid(Grid<int>& grid) { /* 省略正文以簡潔 */ }

或者,你可以使用本章后面討論的函數模板,編寫一個模板化的函數,該函數根據網格中元素的類型進行模板化。V5U28資訊網——每日最新資訊28at.com

注意:你可以使用類型別名來簡化完整的 Grid 類型的重復書寫,例如 Grid<int>:V5U28資訊網——每日最新資訊28at.com

using IntGrid = Grid<int>;void processIntGrid(IntGrid& grid) { /* 正文 */ }V5U28資訊網——每日最新資訊28at.com

4.Grid 類模板的多樣性

Grid 類模板可以存儲的不僅僅是 int。例如,你可以實例化一個存儲 SpreadsheetCells 的 Grid:V5U28資訊網——每日最新資訊28at.com

Grid<SpreadsheetCell> mySpreadsheet;SpreadsheetCell myCell { 1.234 };mySpreadsheet.at(3, 4) = myCell;

你也可以存儲指針類型:V5U28資訊網——每日最新資訊28at.com

Grid<const char*> myStringGrid;myStringGrid.at(2, 2) = "hello";

指定的類型甚至可以是另一個模板類型:V5U28資訊網——每日最新資訊28at.com

Grid<vector<int>> gridOfVectors;vector<int> myVector { 1, 2, 3, 4 };gridOfVectors.at(5, 6) = myVector;

你還可以在自由存儲區動態分配 Grid 模板實例:V5U28資訊網——每日最新資訊28at.com

auto myGridOnFreeStore { make_unique<Grid<int>>(2, 2) }; // 自由存儲區上的 2x2 網格。myGridOnFreeStore->at(0, 0) = 10;int x { myGridOnFreeStore->at(0, 0).value_or(0) };

本文鏈接:http://www.www897cc.com/showinfo-26-38739-0.htmlC++模板基礎及代碼實戰

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 如何在組織中有效地使用低代碼工具?

下一篇: Python小技巧:凍結參數,讓你的代碼變簡潔

標簽:
  • 熱門焦點
  • 對標蘋果的靈動島 華為帶來實況窗功能

    繼蘋果的靈動島之后,華為也在今天正式推出了“實況窗”功能。據今天鴻蒙OS 4.0的現場演示顯示,華為的實況窗可以更高效的展現出實時通知,比如鎖屏上就能看到外賣、打車、銀行
  • vivo TWS Air開箱體驗:真輕 臻好聽

    在vivo S15系列新機的發布會上,vivo的最新款真無線藍牙耳機vivo TWS Air也一同發布,本次就這款耳機新品給大家帶來一個簡單的分享。外包裝盒上,vivo TWS Air保持了vivo自家產
  • 一加首款折疊屏!一加Open渲染圖出爐:罕見單手可握小尺寸

    8月5日消息,此前就有爆料稱,一加首款折疊屏手機將會在第三季度上市,如今隨著時間臨近,新機的各種消息也開始浮出水面。據悉,這款新機將會被命名為&ldquo;On
  • 如何正確使用:Has和:Nth-Last-Child

    我們可以用CSS檢查,以了解一組元素的數量是否小于或等于一個數字。例如,一個擁有三個或更多子項的grid。你可能會想,為什么需要這樣做呢?在某些情況下,一個組件或一個布局可能會
  • 品牌洞察丨服務本地,美團直播成效幾何?

    來源:17PR7月11日,美團App首頁推薦位出現&ldquo;美團直播&rdquo;的固定入口。在直播聚合頁面,外賣&ldquo;神槍手&rdquo;直播間、美團旅行直播間、美團買菜直播間等均已上線,同時
  • 大廠卷向扁平化

    來源:新熵作者丨南枝 編輯丨月見大廠職級不香了。俗話說,兵無常勢,水無常形,互聯網企業調整職級體系并不稀奇。7月13日,淘寶天貓集團啟動了近年來最大的人力制度改革,目前已形成一
  • ESG的面子與里子

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之三伏大幕拉起,各地高溫預警不絕,但處于厄爾尼諾大&ldquo;烤&rdquo;之下的除了眾生,還有各大企業發布的ESG報告。ESG是&ldquo;環境保
  • 消息稱小米汽車開始篩選交付中心:需至少120個車位

    IT之家 7 月 7 日消息,日前,有微博簡介為“汽車行業從業者、長三角一體化擁護者”的微博用戶 @長三角行健者 發文表示,據經銷商集團反饋,小米汽車目前
  • 華為Mate60系列模具曝光:采用碩大圓形后置相機模組+拼接配色方案

    據此前多方爆料,今年華為將開始恢復一年雙旗艦戰略,除上半年推出的P60系列外,往年下半年的Mate系列也將迎來更新,有望在9-10月份帶來全新的華為Mate60
Top 主站蜘蛛池模板: 怀来县| 海南省| 长兴县| 闻喜县| 武川县| 宁陕县| 武隆县| 潍坊市| 车险| 大港区| 临清市| 沙田区| 临澧县| 景泰县| 固始县| 屏山县| 济南市| 巧家县| 会宁县| 商南县| 苍梧县| 淮滨县| 青川县| 韶关市| 大余县| 磐石市| 鹿泉市| 东莞市| 南昌市| 怀安县| 万山特区| 来宾市| 土默特右旗| 土默特左旗| 迭部县| 开江县| 牙克石市| 陕西省| 泽州县| 米易县| 马公市|