天天看點

C++ Primer 中文版 第十六章 模闆與泛型程式設計

作者:明政面朝大海春暖花開

《C++ Primer 中文版(第 5 版)》是一本權威的C++程式設計教材,涵蓋了廣泛的主題,包括模闆與泛型程式設計。下面是對一些與模闆相關的内容的解釋和代碼示例:

  1. 函數模闆:函數模闆是一種通用的函數定義,可以用于不同的資料類型。通過使用模闆參數,可以在編譯時生成多個函數執行個體。例如:
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int result1 = max(5, 10);  // 使用int類型的模闆執行個體
double result2 = max(3.14, 2.71);  // 使用double類型的模闆執行個體
           
  1. 類模闆:類模闆是用于建立通用類的模闆定義。類模闆可以包含成員函數、成員變量和類型成員。例如:
template<typename T>
class MyStack {
public:
    void push(T element);
    T pop();
private:
    std::vector<T> data;
};

MyStack<int> stack;  // 使用int類型的類模闆執行個體
stack.push(10);
int element = stack.pop();
           
  1. 模闆參數:模闆參數是在模闆定義中使用的占位符,用于指定在執行個體化時将被替換的類型或值。模闆參數可以是類型參數、非類型參數或模闆參數包。例如:
template<typename T, int N>
class Array {
    T data[N];
};

Array<int, 5> arr;  // 使用int類型和5作為模闆參數執行個體化Array類模闆
           
  1. 成員模闆:成員模闆是定義在類或結構體内部的模闆函數或模闆類。成員模闆可以通路類的成員和模闆參數。例如:
template<typename T>
class MyContainer {
public:
    template<typename U>
    void add(U element);
};

MyContainer<int> container;
container.add(10);  // 執行個體化add函數模闆為void add<int>(int element);
           
  1. 控制執行個體化:C++編譯器在使用模闆時會根據需要執行個體化模闆。可以使用顯式執行個體化、顯式特例化和内聯等方式來控制模闆的執行個體化行為。例如:
template<typename T>
void foo(T value);

template void foo<int>(int value);  // 顯式執行個體化為int類型的foo函數模闆
template<> void foo<double>(double value);  // 顯式特例化為double類型的foo函數模闆
           
  1. 效率與靈活性:模闆程式設計可以提供高效的代碼,因為編譯器可以在編譯時生成特定類型的代碼。然而,模闆也可能導緻編譯時間增加和代碼膨脹。可以通過使用模闆特例化和模闆元程式設計等技術來提高效率和靈活性。

以上是《C++ Primer 中文版(第 5 版)》中與模闆相關的一些内容的解釋和代碼示例。這本書還包含更多關于模闆和泛型程式設計的詳細講解和示例代碼,建議閱讀該書以深入了解這些概念。

C++中,清單初始化是一種初始化對象的方式,可以在建立對象時使用花括号{}來傳遞參數。以下是對清單初始化相關概念的解釋及舉例:

  1. pair的清單初始化:

    pair是C++标準庫中的一個模闆類,用于存儲一對值。可以使用清單初始化來建立pair對象,将兩個值作為參數傳遞給pair的構造函數,然後将其打包成一個pair對象。

    例如:

  2. std::pair<int, std::string> myPair{ 42, "hello" };
  3. 無序容器:

    無序容器是C++标準庫中提供的一種資料結構,它使用哈希函數來存儲和通路元素,不保證元素的順序。無序容器也支援清單初始化,可以通過花括号{}來初始化容器中的元素。

    例如:

  4. std::unordered_set<int> mySet{ 1, 2, 3, 4, 5 };
  5. 智能指針:

    智能指針是C++中一種用于管理動态配置設定對象的指針的類模闆。智能指針可以自動處理資源的釋放,避免記憶體洩漏等問題。智能指針也支援清單初始化,可以在建立智能指針時使用花括号{}來傳遞參數。

    例如:

  6. std::shared_ptr<int> mySharedPtr{ new int(42) };
  7. 動态配置設定對象的清單初始化:

    動态配置設定對象時,也可以使用清單初始化來初始化對象。

    例如:

  8. int* myDynamicInt = new int{ 42 };
  9. auto和動态配置設定:

    auto關鍵字可以用于自動推導變量的類型。當使用auto關鍵字和動态配置設定結合時,可以使用清單初始化來初始化動态配置設定的對象。

    例如:

  10. auto* myDynamicInt = new int{ 42 };
  11. unique_ptr類:

    unique_ptr是C++中的一種智能指針,它獨占所指向的對象,保證了資源的唯一擁有權。unique_ptr也支援清單初始化。

    例如:

  12. std::unique_ptr<int> myUniquePtr{ new int(42) };
  13. weak_ptr類:

    weak_ptr是C++中的一種智能指針,它指向一個由shared_ptr管理的對象,但不會增加對象的引用計數。weak_ptr也支援清單初始化。

    例如:

  14. std::weak_ptr<int> myWeakPtr{ mySharedPtr };
  15. 範圍for語句不能應用于動态配置設定數組:

    範圍for語句用于周遊容器中的元素,但不支援動态配置設定的數組。因為動态配置設定的數組不是一個容器,無法提供begin和end函數供範圍for語句使用。

    例如:

  16. int* myArray = new int[5]{ 1, 2, 3, 4, 5 }; for (auto element : myArray) { // 無法使用範圍for語句周遊動态配置設定的數組 }
  17. 動态配置設定數組的清單初始化:

    動态配置設定數組時,也可以使用清單初始化來初始化數組中的元素。

    例如:

  18. int* myArray = new int[5]{ 1, 2, 3, 4, 5 };

下面是對你提到的C++相關内容的詳細解釋和示例:

  1. 容器的cbegin和cend函數:
  2. cbegin函數傳回一個指向容器中第一個元素的常量疊代器。
  3. cend函數傳回一個指向容器中最後一個元素之後位置的常量疊代器。
  4. 示例代碼:
  5. #include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // 使用cbegin和cend輸出容器中的元素 for (auto it = numbers.cbegin(); it != numbers.cend(); ++it) { std::cout << *it << " "; } return 0; }
  6. 容器的清單初始化:

    C++11引入了清單初始化文法,可以用于初始化容器。

  7. 示例代碼:
  8. #include <iostream> #include <vector> #include <list> int main() { // 清單初始化vector std::vector<int> numbers = {1, 2, 3, 4, 5}; // 清單初始化list std::list<std::string> names = {"Alice", "Bob", "Charlie"}; // 輸出容器中的元素 for (const auto& num : numbers) { std::cout << num << " "; } std::cout << std::endl; for (const auto& name : names) { std::cout << name << " "; } std::cout << std::endl; return 0; }
  9. 容器的非成員函數swap:

    swap函數用于交換兩個容器的内容。

  10. 示例代碼:
  11. #include <iostream> #include <vector> int main() { std::vector<int> numbers1 = {1, 2, 3}; std::vector<int> numbers2 = {4, 5, 6}; // 交換兩個容器的内容 numbers1.swap(numbers2); // 輸出交換後的容器内容 for (const auto& num : numbers1) { std::cout << num << " "; } std::cout << std::endl; for (const auto& num : numbers2) { std::cout << num << " "; } std::cout << std::endl; return 0; }
  12. 容器insert成員的傳回類型:

    insert函數用于在容器中插入元素,并傳回一個指向插入的元素的疊代器。

  13. 示例代碼:
  14. #include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; auto it = numbers.insert(numbers.begin() + 2, 6); // 輸出插入後的容器内容 for (const auto& num : numbers) { std::cout << num << " "; } std::cout << std::endl; // 輸出插入的元素值 std::cout << "Inserted element: " << *it << std::endl; return 0; }
  15. 容器的emplace成員的傳回類型:

    emplace函數用于在容器中就地構造元素,并傳回一個指向新構造的元素的疊代器。

  16. 示例代碼:
  17. #include <iostream> #include <map> int main() { std::map<int, std::string> myMap; auto it = myMap.emplace(1, "Alice"); // 輸出插入後的容器内容 for (const auto& pair : myMap) { std::cout << pair.first << ": " << pair.second << std::endl; } // 輸出插入的元素值 std::cout << "Inserted element: " << it->first << ": " << it->second << std::endl; return 0; }
  18. shrink_to_fit:

    shrink_to_fit函數用于要求容器減小其記憶體占用,将容器的容量減小到與其大小相比對。

  19. 示例代碼:
  20. #include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; numbers.pop_back(); // 删除最後一個元素 numbers.shrink_to_fit(); // 減小容器的容量 std::cout << "Size: " << numbers.size() << std::endl; std::cout << "Capacity: " << numbers.capacity() << std::endl; return 0; }
  21. string的數值轉換函數:

    std::stoi、std::stol、std::stoll等函數可以将字元串轉換為數值類型。

  22. 示例代碼:
  23. #include <iostream> #include <string> int main() { std::string str = "12345"; int num = std::stoi(str); std::cout << "Number: " << num << std::endl; return 0; }
  24. Lambda表達式:

    Lambda表達式是C++11引入的一種匿名函數的文法,可以用于建立臨時的函數對象。

  25. 示例代碼:
  26. #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // 使用Lambda表達式列印容器中的元素 std::for_each(numbers.begin(), numbers.end(), [](int num) { std::cout << num << " "; }); std::cout << std::endl; return 0; }
  27. Lambda表達式中的尾置傳回類型:

    Lambda表達式可以使用尾置傳回類型來指定傳回值的類型。

  28. 示例代碼:
  29. #include <iostream> int main() { auto lambda = [](int a, int b) -> int { return a + b; }; int result = lambda(2, 3); std::cout << "Result: " << result << std::endl; return 0; }
  30. 标準庫bind函數:

    bind函數用于建立一個函數對象,可以将參數綁定到特定的值或者函數。

  31. 示例代碼:
  32. #include <iostream> #include <functional> int add(int a, int b) { return a + b; } int main() { auto addFive = std::bind(add, std::placeholders::_1, 5); int result = addFive(10); std::cout << "Result: " << result << std::endl; return 0; }
  33. 關聯容器的清單初始化:

    關聯容器(如std::map和std::set)也可以使用清單初始化文法進行初始化。

  34. 示例代碼:
  35. #include <iostream> #include <map> #include <set> int main() { // 清單初始化map std::map<int, std::string> myMap = {{1, "Alice"}, {2, "Bob"}, {3, "Charlie"}}; // 清單初始化set std::set<int> mySet = {1, 2, 3, 4, 5}; // 輸出map中的鍵值對 for (const auto& pair : myMap) { std::cout << pair.first << ": " << pair.second << std::endl; } std::cout << std::endl; // 輸出set中的元素 for (const auto& num : mySet) { std::cout << num << " "; } std::cout << std::endl; return 0; }

希望以上解釋和示例能夠對你有所幫助。如有任何進一步的問題,請随時提問。

繼續閱讀