第19章 模板和泛型编程 模板的底层实现原理 模板实例化机制 模板是C++中实现泛型编程的核心机制,其底层实现涉及编译期实例化过程:
模板定义 :编译器解析模板代码,但不生成具体代码实例化触发 :当模板被使用时(如创建对象或调用函数)类型替换 :编译器将模板参数替换为具体类型代码生成 :为每个唯一的模板实例生成专门的代码优化 :对生成的代码应用常规编译优化模板实例化的编译器处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 template <typename T>T maximum (T a, T b) { return a > b ? a : b; } int main () { int a = 1 , b = 2 ; double c = 1.5 , d = 2.5 ; int max_int = maximum (a, b); double max_double = maximum (c, d); return 0 ; }
编译器会为 maximum<int> 和 maximum<double> 生成两个不同的函数实例,每个实例都是针对具体类型优化的代码。
模板的名称查找规则 依赖名称查找(ADL) :模板中的依赖名称会在实例化时查找两阶段查找 :第一阶段:解析模板时,查找非依赖名称 第二阶段:实例化时,查找依赖名称 模板参数推导 :编译器根据实参类型推导模板参数类型高级模板元编程 编译期计算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <unsigned int N>struct Factorial { static constexpr unsigned int value = N * Factorial<N - 1 >::value; }; template <>struct Factorial <0 > { static constexpr unsigned int value = 1 ; }; constexpr unsigned int fact_5 = Factorial<5 >::value;
类型 traits 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <typename T>struct is_integral { static constexpr bool value = false ; }; template <>struct is_integral <int > { static constexpr bool value = true ; }; template <>struct is_integral <long > { static constexpr bool value = true ; }; template <typename T>void process (T value) { if constexpr (is_integral<T>::value) { std::cout << "Processing integral type: " << value << std::endl; } else { std::cout << "Processing non-integral type" << std::endl; } }
SFINAE 技术 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template <typename T>auto test (T t) -> decltype (t.foo(), std::true_type()) { return std::true_type{}; } template <typename T>std::false_type test (...) ;template <typename T>struct has_foo : decltype (test <T>(std::declval <T>())) {}; template <typename T>void process_if_has_foo (T t) { if constexpr (has_foo<T>::value) { t.foo (); } else { std::cout << "Type does not have foo() method" << std::endl; } }
模板元编程的实际应用 编译期反射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <vector> template <typename T>struct TypeInfo { static constexpr const char * name () { return "unknown" ; } }; template <>struct TypeInfo <int > { static constexpr const char * name () { return "int" ; } }; template <>struct TypeInfo <double > { static constexpr const char * name () { return "double" ; } }; template <typename T>struct TypeInfo <std::vector<T>> { static constexpr const char * name () { return "std::vector<>" ; } }; template <typename T>void printTypeInfo () { std::cout << "Type: " << TypeInfo<T>::name () << std::endl; }
constexpr 函数的高级应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 constexpr size_t strlen_constexpr (const char * str) { return *str ? 1 + strlen_constexpr (str + 1 ) : 0 ; } constexpr bool strcmp_constexpr (const char * a, const char * b) { return *a == *b && (*a == '\0' || strcmp_constexpr (a + 1 , b + 1 )); } constexpr size_t hash_string (const char * str, size_t seed = 0 ) { return *str ? hash_string (str + 1 , seed * 31 + *str) : seed; }
可变参数模板 可变参数模板是C++11引入的重要特性,它允许模板接受任意数量的模板参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 template <typename ... Args>void print (Args... args) { } template <typename T>void print_single (T t) { std::cout << t << std::endl; } template <typename T, typename ... Args>void print (T first, Args... rest) { print_single (first); print (rest...); } template <typename ... Args>void print_fold (Args... args) { (std::cout << ... << args) << std::endl; } template <typename ... Args>void print_with_separator (Args... args) { ((std::cout << args << ", " ), ...); std::cout << std::endl; }
模板模板参数 模板模板参数允许将模板作为另一个模板的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <template <typename > class Container , typename T>void process_container (Container<T>& container) { for (auto & item : container) { std::cout << item << " " ; } std::cout << std::endl; } template <template <typename , typename > class Container , typename T, typename Allocator>void process_std_container (Container<T, Allocator>& container) { for (auto & item : container) { std::cout << item << " " ; } std::cout << std::endl; } std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; std::list<double > lst = {1.1 , 2.2 , 3.3 }; process_std_container (vec); process_std_container (lst);
别名模板 别名模板是C++11引入的特性,它允许为模板创建别名:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <typename T>using Vec = std::vector<T>;Vec<int > int_vector = {1 , 2 , 3 }; Vec<double > double_vector = {1.1 , 2.2 , 3.3 }; template <typename T = int >using Matrix = std::vector<std::vector<T>>;Matrix<> int_matrix; Matrix<double > double_matrix; template <typename T, template <typename > class Container >struct MyContainer { Container<T> data; }; template <typename T>using MyVectorContainer = MyContainer<T, Vec>;MyVectorContainer<int > container;
模板的性能优化 模板代码膨胀 :
原因:为每个模板实例生成单独的代码 影响:增加可执行文件大小和编译时间 解决方案:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 template <typename T>class TypeErasedContainer {private : struct Concept { virtual ~Concept () = default ; virtual void push_back (const void *) = 0 ; virtual void * get (size_t ) = 0 ; }; template <typename U> struct Model : Concept { std::vector<U> data; void push_back (const void * value) override { data.push_back (*static_cast <const U*>(value)); } void * get (size_t index) override { return &data[index]; } }; std::unique_ptr<Concept> impl; public : template <typename U> TypeErasedContainer () : impl (std::make_unique<Model<U>>()) {} template <typename U> void push_back (U value) { static_cast <Model<U>*>(impl.get ())->push_back (&value); } template <typename U> U& get (size_t index) { return *static_cast <U*>(static_cast <Model<U>*>(impl.get ())->get (index)); } };
编译时间优化 :
预编译头文件:将常用模板定义放入预编译头 显式实例化:减少重复实例化 模板拆分:将模板声明和定义分离 1 2 3 4 5 6 7 extern template class std ::vector<int >;extern template class std ::vector<double >; template class std ::vector<int >;template class std ::vector<double >;
运行时性能 :
内联展开:模板函数容易被编译器内联 编译期计算:使用constexpr和模板元编程 类型特化:为特定类型提供优化实现 现代C++中的模板特性 C++11模板改进 :
C++14模板改进 :
1 2 3 4 5 6 7 template <typename T>constexpr T pi = T (3.14159265358979323846 );double area = pi<double > * radius * radius;float circumference = 2 * pi<float > * radius;
C++17模板改进 :
折叠表达式 模板参数推导(类模板参数推导) 内联变量 if constexpr 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 std::pair p (1 , 2.5 ) ; std::vector v = {1 , 2 , 3 }; template <typename T>void process (T t) { if constexpr (std::is_integral_v<T>) { std::cout << "Integral: " << t << std::endl; } else if constexpr (std::is_floating_point_v<T>) { std::cout << "Floating point: " << t << std::endl; } else { std::cout << "Other type" << std::endl; } }
C++20模板改进 :
概念(Concepts) 约束模板参数 模块(Modules) 协程(Coroutines) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <typename T> concept Integral = std::is_integral_v<T>; template <typename T> concept FloatingPoint = std::is_floating_point_v<T>; template <Integral T>T add (T a, T b) { return a + b; } template <FloatingPoint T>T add (T a, T b) { return a + b; }
模板的最佳实践 模板设计原则 :
单一职责 :每个模板只负责一个功能可测试性 :确保模板可以被有效测试可扩展性 :设计灵活的模板接口性能考虑 :避免不必要的模板实例化模板编程技巧 :
标签分发 :使用标签类型选择不同的实现SFINAE :基于替换失败的重载解析CRTP : curiously recurring template pattern类型 traits :编译期类型信息获取模板的错误处理 :
static_assert :编译期断言概念检查 :使用C++20概念进行约束错误消息优化 :提供清晰的错误信息1 2 3 4 5 6 template <typename T>void process (T t) { static_assert (std::is_integral_v<T>, "T must be an integral type" ); }
模板的文档和注释 :
模板参数说明 :详细说明每个模板参数的用途约束条件 :说明模板参数的约束条件使用示例 :提供模板的使用示例模板元编程的高级技巧 编译期算法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <typename T, T... Ns>struct IntegerSequence { using type = IntegerSequence; static constexpr size_t size = sizeof ...(Ns); }; template <size_t N, size_t ... Is>struct MakeIntegerSequence : MakeIntegerSequence<N-1 , N-1 , Is...> {};template <size_t ... Is>struct MakeIntegerSequence <0 , Is...> : IntegerSequence<size_t , Is...> {};template <typename T, T... Ns>struct MaxValue ;template <typename T, T N>struct MaxValue <T, N> : std::integral_constant<T, N> {};template <typename T, T N1, T N2, T... Ns>struct MaxValue <T, N1, N2, Ns...> : std::integral_constant<T, N1 > N2 ? MaxValue<T, N1, Ns...>::value : MaxValue<T, N2, Ns...>::value> {};
表达式模板 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 template <typename T>class Vector {private : std::vector<T> data; public : Vector (size_t size) : data (size) {} T& operator [](size_t index) { return data[index]; } const T& operator [](size_t index) const { return data[index]; } size_t size () const { return data.size (); } }; template <typename T, typename Expr>class VectorExpr {public : T operator [](size_t index) const { return static_cast <const Expr&>(*this )[index]; } size_t size () const { return static_cast <const Expr&>(*this ).size (); } }; template <typename T, typename Lhs, typename Rhs>class VectorAdd : public VectorExpr<T, VectorAdd<T, Lhs, Rhs>> {private : const Lhs& lhs; const Rhs& rhs; public : VectorAdd (const Lhs& l, const Rhs& r) : lhs (l), rhs (r) {} T operator [](size_t index) const { return lhs[index] + rhs[index]; } size_t size () const { return lhs.size (); } }; template <typename T, typename Lhs, typename Rhs>VectorAdd<T, Lhs, Rhs> operator +(const VectorExpr<T, Lhs>& lhs, const VectorExpr<T, Rhs>& rhs) { return VectorAdd <T, Lhs, Rhs>(static_cast <const Lhs&>(lhs), static_cast <const Rhs&>(rhs)); } template <typename T, typename Expr>Vector<T> operator +(const Vector<T>& vec, const VectorExpr<T, Expr>& expr) { Vector<T> result (vec.size()) ; for (size_t i = 0 ; i < vec.size (); ++i) { result[i] = vec[i] + expr[i]; } return result; }
策略模式与模板 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 template <typename SortStrategy, typename Container>void sort (Container& container) { SortStrategy::sort (container); } truct BubbleSort { template <typename Container> static void sort (Container& container) { for (size_t i = 0 ; i < container.size () - 1 ; ++i) { for (size_t j = 0 ; j < container.size () - i - 1 ; ++j) { if (container[j] > container[j + 1 ]) { std::swap (container[j], container[j + 1 ]); } } } } }; truct QuickSort { template <typename Container> static void sort (Container& container) { if (container.size () <= 1 ) return ; auto pivot = container[container.size () / 2 ]; Container left, middle, right; for (auto & item : container) { if (item < pivot) left.push_back (item); else if (item == pivot) middle.push_back (item); else right.push_back (item); } sort <QuickSort>(left); sort <QuickSort>(right); container.clear (); container.insert (container.end (), left.begin (), left.end ()); container.insert (container.end (), middle.begin (), middle.end ()); container.insert (container.end (), right.begin (), right.end ()); } }; std::vector<int > vec = {3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 }; sort <BubbleSort>(vec); sort <QuickSort>(vec);
模板在标准库中的应用 STL容器 :
std::vector<T>:动态数组std::list<T>:双向链表std::map<K, V>:关联容器算法库 :
std::sort:排序算法std::find:查找算法std::transform:变换算法智能指针 :
std::unique_ptr<T>:独占所有权指针std::shared_ptr<T>:共享所有权指针std::weak_ptr<T>:弱引用指针函数对象 :
std::function<T>:通用函数包装器std::bind:函数绑定std::mem_fn:成员函数适配器模板编程的未来发展 C++23模板改进 :
C++26及以后 :
反射(Reflection) 元类(Metaclasses) 编译期字符串操作 模板编程的趋势 :
更强大的编译期计算能力 更简洁的语法 更好的错误消息 与现代C++特性的深度集成 模板是C++中最强大的特性之一,它不仅支持泛型编程,还为编译期计算和元编程提供了基础。随着C++标准的不断发展,模板的能力也在不断增强,为C++程序员提供了越来越多的工具和技巧。
掌握模板编程需要时间和实践,但它将为你打开C++编程的新境界,使你能够编写更灵活、更高效、更可维护的代码。 std::cout << “Base::value = “ << value << std::endl; // 可以访问,因为是保护继承 } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ## 可变参数模板与模板特化 ### 可变参数模板的实现 ```cpp // 递归终止条件 template <typename T> void print(T value) { std::cout << value << std::endl; } // 可变参数模板 template <typename T, typename... Args> void print(T first, Args... rest) { std::cout << first << " "; print(rest...); // 递归调用 } // 使用可变参数模板 print(1, 2.5, "Hello", true);
折叠表达式(C++17) 1 2 3 4 5 6 7 8 9 10 11 12 13 template <typename ... Args>void print (Args&&... args) { (std::cout << ... << args) << std::endl; } template <typename ... Args>void print_with_separator (Args&&... args) { const char * sep = "" ; ((std::cout << sep << args), sep = ", " )...; std::cout << std::endl; }
模板特化 类模板特化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 template <typename T>class Container {public : void add (T value) { std::cout << "Adding value: " << value << std::endl; } }; template <>class Container <std::string> {public : void add (std::string value) { std::cout << "Adding string: '" << value << "'" << std::endl; } }; template <typename T>class Container <T*> {public : void add (T* value) { if (value) { std::cout << "Adding pointer: " << *value << std::endl; } else { std::cout << "Adding null pointer" << std::endl; } } };
函数模板特化 1 2 3 4 5 6 7 8 9 10 11 template <typename T>T max (T a, T b) { return a > b ? a : b; } template <>const char * max <const char *>(const char * a, const char * b) { return std::strcmp (a, b) > 0 ? a : b; }
概念(Concepts)与模板约束 自定义概念 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <concepts> template <typename T>concept Numeric = std::integral<T> || std::floating_point<T>;template <typename T>concept Hashable = requires (T a) { { std::hash<T>{}(a) } -> std::convertible_to<std::size_t >; }; template <typename T>concept Sortable = requires (T a, T b) { { a < b } -> std::convertible_to<bool >; { a > b } -> std::convertible_to<bool >; { a == b } -> std::convertible_to<bool >; }; template <Numeric T>T sum (T a, T b) { return a + b; } template <Hashable T>void hash_value (T value) { std::size_t hash = std::hash<T>{}(value); std::cout << "Hash: " << hash << std::endl; } template <Sortable T>void sort_values (std::vector<T>& values) { std::sort (values.begin (), values.end ()); }
概念的组合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <typename T>concept NumericAndSortable = Numeric<T> && Sortable<T>;template <typename T>concept StringLike = std::same_as<T, std::string> || std::same_as<T, std::string_view> || std::same_as<T, const char *>; template <NumericAndSortable T>T find_min (const std::vector<T>& values) { return *std::min_element (values.begin (), values.end ()); } template <StringLike T>std::string to_string (T value) { if constexpr (std::same_as<T, const char *>) { return std::string (value); } else { return std::string (value); } }
C++20模板增强 模板Lambda 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> int main () { auto add = []<typename T>(T a, T b) { return a + b; }; std::cout << "Add integers: " << add (1 , 2 ) << std::endl; std::cout << "Add doubles: " << add (1.5 , 2.5 ) << std::endl; std::cout << "Add strings: " << add (std::string ("Hello" ), std::string (" World" )) << std::endl; auto multiply = []<typename T>(T a, T b) requires std::is_arithmetic_v<T> { return a * b; }; std::cout << "Multiply integers: " << multiply (3 , 4 ) << std::endl; std::cout << "Multiply doubles: " << multiply (2.5 , 4.0 ) << std::endl; auto print = []<typename ... Args>(Args&&... args) { (std::cout << ... << args) << std::endl; }; print ("The answer is " , 42 , ", and pi is " , 3.14159 ); return 0 ; }
带requires表达式的模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <concepts> #include <iostream> template <typename T> requires std::integral<T> || std::floating_point<T> T absolute (T value) { return value < 0 ? -value : value; } template <std::integral T> T square (T value) { return value * value; } template <typename T> requires std::copyable<T> && requires (T a, T b) { { a + b } -> std::same_as<T>; { a * b } -> std::same_as<T>; } T sum_and_product (T a, T b) { return a + b * a; } int main () { std::cout << "Absolute value of -5: " << absolute (-5 ) << std::endl; std::cout << "Absolute value of -3.14: " << absolute (-3.14 ) << std::endl; std::cout << "Square of 5: " << square (5 ) << std::endl; std::cout << "Sum and product of 3 and 4: " << sum_and_product (3 , 4 ) << std::endl; return 0 ; }
C++20模板增强的优点 更简洁的语法 :模板lambda简化了泛型lambda的写法更强的表达能力 :requires表达式提供了更灵活的模板约束方式更好的错误信息 :当模板参数不满足约束时,提供更清晰的错误信息更高的代码可读性 :使用概念和requires表达式使模板代码更易于理解更好的编译性能 :编译器可以更早地检查模板参数的有效性模板元编程高级技巧 编译期类型列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 template <typename ... Types>struct TypeList {};template <typename List>struct Length ;template <typename ... Types>struct Length <TypeList<Types...>> { static constexpr size_t value = sizeof ...(Types); }; template <typename List, size_t Index>struct TypeAt ;template <typename T, typename ... Types>struct TypeAt <TypeList<T, Types...>, 0 > { using type = T; }; template <typename T, typename ... Types, size_t Index>struct TypeAt <TypeList<T, Types...>, Index> { using type = typename TypeAt<TypeList<Types...>, Index - 1 >::type; }; using MyTypes = TypeList<int , double , std::string>;static constexpr size_t size = Length<MyTypes>::value; using SecondType = TypeAt<MyTypes, 1 >::type;
编译期算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 template <typename List, typename T>struct Contains ;template <typename T>struct Contains <TypeList<>, T> { static constexpr bool value = false ; }; template <typename T, typename ... Types>struct Contains <TypeList<T, Types...>, T> { static constexpr bool value = true ; }; template <typename U, typename T, typename ... Types>struct Contains <TypeList<U, Types...>, T> { static constexpr bool value = Contains<TypeList<Types...>, T>::value; }; template <typename List, template <typename > typename Predicate>struct Filter ;template <template <typename > typename Predicate>struct Filter <TypeList<>, Predicate> { using type = TypeList<>; }; template <typename T, typename ... Types, template <typename > typename Predicate>struct Filter <TypeList<T, Types...>, Predicate> { using Rest = typename Filter<TypeList<Types...>, Predicate>::type; using type = std::conditional_t < Predicate<T>::value, TypeList<T, Rest>, Rest >; }; using FilteredTypes = Filter<MyTypes, std::is_arithmetic>::type; static constexpr bool has_string = Contains<MyTypes, std::string>::value;
策略模式与模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 template <typename T>class SortStrategy {public : virtual void sort (std::vector<T>& data) = 0 ; virtual ~SortStrategy () = default ; }; template <typename T>class QuickSort : public SortStrategy<T> {public : void sort (std::vector<T>& data) override { std::sort (data.begin (), data.end ()); std::cout << "Sorted with QuickSort" << std::endl; } }; template <typename T>class BubbleSort : public SortStrategy<T> {public : void sort (std::vector<T>& data) override { for (size_t i = 0 ; i < data.size (); i++) { for (size_t j = 0 ; j < data.size () - i - 1 ; j++) { if (data[j] > data[j + 1 ]) { std::swap (data[j], data[j + 1 ]); } } } std::cout << "Sorted with BubbleSort" << std::endl; } }; template <typename T, template <typename > typename SortAlgorithm>class Sorter {public : void sort (std::vector<T>& data) { SortAlgorithm<T> algorithm; algorithm.sort (data); } }; std::vector<int > data = {5 , 2 , 8 , 1 , 9 }; Sorter<int , QuickSort> quickSorter; quickSorter.sort (data); Sorter<int , BubbleSort> bubbleSorter; bubbleSorter.sort (data);
模板 模板是C++中实现泛型编程的重要机制,它允许编写与类型无关的代码。
函数模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template <typename T>T max (T a, T b) { return (a > b) ? a : b; } template int max <int >(int , int );template double max <double >(double , double );int main () { int i = max (10 , 20 ); double d = max (3.14 , 2.71 ); std::string s = max (std::string ("hello" ), std::string ("world" )); std::cout << "Max int: " << i << std::endl; std::cout << "Max double: " << d << std::endl; std::cout << "Max string: " << s << std::endl; return 0 ; }
类模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template <typename T>class Stack {private : std::vector<T> elements; public : void push (const T& item) { elements.push_back (item); } void pop () { if (!empty ()) { elements.pop_back (); } } T& top () { return elements.back (); } bool empty () const { return elements.empty (); } size_t size () const { return elements.size (); } }; int main () { Stack<int > intStack; intStack.push (10 ); intStack.push (20 ); std::cout << "Top: " << intStack.top () << std::endl; intStack.pop (); std::cout << "Top after pop: " << intStack.top () << std::endl; Stack<std::string> stringStack; stringStack.push ("hello" ); stringStack.push ("world" ); std::cout << "Top: " << stringStack.top () << std::endl; stringStack.pop (); std::cout << "Top after pop: " << stringStack.top () << std::endl; return 0 ; }
模板特化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 template <typename T>class MyClass {public : void print () { std::cout << "General template" << std::endl; } }; template <>class MyClass <int > {public : void print () { std::cout << "Specialized for int" << std::endl; } }; template <>class MyClass <std::string> {public : void print () { std::cout << "Specialized for string" << std::endl; } }; int main () { MyClass<double > d; MyClass<int > i; MyClass<std::string> s; d.print (); i.print (); s.print (); return 0 ; }
标准模板库(STL) STL是C++标准库的一部分,提供了一系列的容器、迭代器、算法和函数对象,是代码重用的重要工具。
容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <vector> #include <list> #include <map> #include <set> #include <unordered_map> #include <unordered_set> int main () { std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; vec.push_back (6 ); std::list<int > lst = {1 , 2 , 3 , 4 , 5 }; lst.push_back (6 ); lst.push_front (0 ); std::map<std::string, int > map; map["one" ] = 1 ; map["two" ] = 2 ; std::set<int > set = {1 , 2 , 3 , 4 , 5 }; set.insert (6 ); std::unordered_map<std::string, int > umap; umap["one" ] = 1 ; umap["two" ] = 2 ; std::unordered_set<int > uset = {1 , 2 , 3 , 4 , 5 }; uset.insert (6 ); return 0 ; }
算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <algorithm> #include <vector> int main () { std::vector<int > vec = {5 , 2 , 8 , 1 , 9 , 3 }; std::sort (vec.begin (), vec.end ()); auto it = std::find (vec.begin (), vec.end (), 5 ); if (it != vec.end ()) { std::cout << "Found 5 at position: " << std::distance (vec.begin (), it) << std::endl; } int count = std::count (vec.begin (), vec.end (), 5 ); std::cout << "Count of 5: " << count << std::endl; auto maxIt = std::max_element (vec.begin (), vec.end ()); std::cout << "Max element: " << *maxIt << std::endl; auto minIt = std::min_element (vec.begin (), vec.end ()); std::cout << "Min element: " << *minIt << std::endl; std::reverse (vec.begin (), vec.end ()); return 0 ; }
迭代器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <vector> int main () { std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; std::cout << "Forward iteration: " << std::endl; for (auto it = vec.begin (); it != vec.end (); ++it) { std::cout << *it << " " ; } std::cout << std::endl; std::cout << "Reverse iteration: " << std::endl; for (auto it = vec.rbegin (); it != vec.rend (); ++it) { std::cout << *it << " " ; } std::cout << std::endl; std::cout << "Constant iteration: " << std::endl; for (auto it = vec.cbegin (); it != vec.cend (); ++it) { std::cout << *it << " " ; } std::cout << std::endl; return 0 ; }
函数对象 函数对象是重载了函数调用运算符()的类的实例,可以像函数一样被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Add {private : int value; public : Add (int v) : value (v) {} int operator () (int x) const { return x + value; } }; class Multiply {private : int factor; public : Multiply (int f) : factor (f) {} int operator () (int x) const { return x * factor; } }; int main () { Add add5 (5 ) ; int result1 = add5 (10 ); std::cout << "10 + 5 = " << result1 << std::endl; Multiply multiply3 (3 ) ; int result2 = multiply3 (10 ); std::cout << "10 * 3 = " << result2 << std::endl; std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; std::transform (vec.begin (), vec.end (), vec.begin (), add5); std::cout << "After adding 5: " << std::endl; for (int num : vec) { std::cout << num << " " ; } std::cout << std::endl; return 0 ; }
Lambda 表达式(C++11+) Lambda表达式是C++11引入的匿名函数对象,提供了一种简洁的方式来定义函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int main () { auto add = [](int a, int b) { return a + b; }; std::cout << "5 + 3 = " << add (5 , 3 ) << std::endl; int x = 10 ; auto addX = [x](int a) { return a + x; }; std::cout << "5 + x = " << addX (5 ) << std::endl; auto addXRef = [&x](int a) { return a + x; }; auto addAll = [=](int a) { return a + x; }; auto increment = [x]() mutable { return ++x; }; std::vector<int > vec = {5 , 2 , 8 , 1 , 9 , 3 }; std::sort (vec.begin (), vec.end (), [](int a, int b) { return a > b; }); std::cout << "Sorted in descending order: " << std::endl; for (int num : vec) { std::cout << num << " " ; } std::cout << std::endl; return 0 ; }
代码重用的最佳实践 1. 选择合适的代码重用机制 继承 :适用于”is-a”关系,需要多态的场景组合 :适用于”has-a”关系,需要松耦合的场景模板 :适用于泛型编程,需要与类型无关的代码函数对象 :适用于需要状态的函数Lambda表达式 :适用于简短的、一次性的函数2. 遵循设计原则 单一职责原则 :每个类只负责一项功能开放-封闭原则 :对扩展开放,对修改封闭里氏替换原则 :子类应该能够替换父类接口隔离原则 :客户端不应该依赖它不使用的接口依赖倒置原则 :依赖于抽象,而不是具体实现3. 合理使用STL 选择合适的容器 :根据访问模式和性能需求选择容器使用算法 :优先使用STL算法而不是手写循环使用迭代器 :通过迭代器访问容器元素使用函数对象和lambda :简化代码,提高可读性4. 代码组织 模块化 :将相关功能组织到模块中命名空间 :使用命名空间避免名称冲突头文件和源文件分离 :声明和实现分离文档 :为代码添加注释和文档常见错误和陷阱 1. 继承滥用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person {public : void eat () {} void sleep () {} }; class Student : public Person {public : void study () {} }; class Student {private : Person person; public : void eat () { person.eat (); } void sleep () { person.sleep (); } void study () {} };
2. 模板错误 1 2 3 4 5 6 7 8 9 10 11 12 template <typename T>class MyClass {}; template <>class MyClass <int > {};
3. STL使用错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; for (auto it = vec.begin (); it != vec.end (); ++it) { if (*it == 3 ) { vec.erase (it); } } std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; for (auto it = vec.begin (); it != vec.end ();) { if (*it == 3 ) { it = vec.erase (it); } else { ++it; } }
4. 函数对象错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Comparator {public : bool operator () (int a, int b) { return a < b; } }; class Comparator {public : bool operator () (int a, int b) const { return a < b; } };
小结 本章介绍了C++中的代码重用机制,包括:
继承与组合 :两种基本的代码重用方式,分别对应”is-a”和”has-a”关系私有继承和保护继承 :实现”is-implemented-in-terms-of”关系模板 :实现泛型编程,编写与类型无关的代码标准模板库(STL) :提供容器、算法、迭代器等工具函数对象 :重载函数调用运算符的类的实例Lambda表达式 :C++11引入的匿名函数对象代码重用的最佳实践 :选择合适的机制、遵循设计原则、合理使用STL等常见错误和陷阱 :继承滥用、模板错误、STL使用错误等代码重用是提高开发效率、减少代码冗余、提高代码质量的重要手段。通过合理使用C++提供的代码重用机制,可以编写出更简洁、更高效、更可维护的代码。在实际编程中,应根据具体情况选择合适的代码重用方式,遵循最佳实践,避免常见错误。
在后续章节中,我们将学习C++的其他高级特性,如异常处理、命名空间、模板特化等,进一步提高C++编程能力。