目录
1、模板
1.1、函数模板
1.2、类模板
1.3、模板类型形参
1.4、非模板类型参数
1.5、默认模板类型参数
1.6、友元模板函数
2、转换函数
转换函数的用途
示例
使用转换函数
注意事项
转换函数的优点和缺点
结论
3、智能指针
4、STL
数据结构
4.1、序列容器 (Sequence Containers): 元素按照顺序存储,可以通过索引访问。
4.2、关联容器(Associative Containers): 元素按照键值对存储,可以通过键快速查找元素。
4.3、配器 (Adapters): 容器适配器是对现有容器的包装,提供了不同的接口。
算法
迭代器
函数对象
C++ 的泛型编程主要通过模板(template)实现。模板允许编写能够处理多种数据类型的代码,而无需为每种数据类型编写单独的函数或类。 这通过参数化类型来实现,编译器会在编译时根据实际使用的类型生成具体的代码。
1、模板
1.1、函数模板
函数模板是 C++ 中的一种泛型编程机制,允许编写可以处理不同数据类型的函数。函数模板使用 template <typename T>
(或 template <class T>
) 声明类型参数 T,然后在函数体中使用 T 就像使用普通类型一样。
例如:
template <typename T>
T max(T a, T b) {return (a > b) ? a : b;
}
这个函数模板可以用来计算两个整数、浮点数或字符串的最大值。
1.2、类模板
类模板是 C++ 中的一种泛型编程机制,允许编写可以处理不同数据类型的类。类模板使用 template <typename T>
(或 template <class T>
) 声明类型参数 T,然后在类成员函数和成员变量中使用 T。
例如:
template <typename T>
class Container {
public:Container() {}void push_back(T value) { data_.push_back(value); }
private:std::vector<T> data_;
};
这个类模板可以用来创建容器类,可以存储整数、浮点数、字符串等不同数据类型。
1.3、模板类型形参
模板类型形参是 C++ 中的一种机制,允许将类型参数传递给模板函数或类。模板类型形参使用 typename
关键字声明。
例如:
template <typename T>
T foo(T value) {return value;
}
这个函数模板使用了模板类型形参 T,表示函数可以处理不同数据类型的值。
1.4、非模板类型参数
非模板类型参数是 C++ 中的一种机制,允许将非类型参数传递给模板函数或类。非模板类型参数使用 const
关键字声明。
例如:
template <typename T>
void foo(T value, const int size) {// 使用 size 参数
}
这个函数模板使用了非模板类型参数 size,表示函数可以处理不同数据类型的值,并且有一个非类型参数 size。
1.5、默认模板类型参数
默认模板类型参数是 C++ 中的一种机制,允许将默认值赋给模板类型形参。默认模板类型参数使用 =
关键字声明。
例如:
template <typename T = int>
T foo(T value) {return value;
}
这个函数模板使用了默认模板类型参数 T = int,表示如果不指定类型参数 T,函数将使用整数类型。
1.6、友元模板函数
友元模板函数是 C++ 中的一种机制,允许一个类访问另一个类的模板函数。友元模板函数使用 friend
关键字声明。
例如:
template <typename T>
class Container {
public:friend T foo(T value) {return value;}
};
这个类模板使用了友元模板函数 foo,表示容器类可以访问 foo 函数。
这些机制可以组合使用,以实现更加灵活和高效的泛型编程。
2、转换函数
转换函数是类的成员函数,用于将类对象转换为其他类型。转换函数没有返回类型,没有参数,并且不能声明为 const
、volatile
或 static
。转换函数的格式如下:
operator type();
其中,type
是目标类型。
转换函数的用途
转换函数的主要用途是实现类型转换,使得类对象可以在需要其他类型的地方使用。例如,可以将一个类对象转换为整数、浮点数或其他类对象。
示例
假设我们有一个表示距离的类 Distance
,我们希望能够将 Distance
对象转换为整数(以厘米为单位)。我们可以定义一个转换函数:
class Distance {
private:int meters;int centimeters;public:Distance(int m, int cm) : meters(m), centimeters(cm) {}// 转换函数operator int() const {return meters * 100 + centimeters;}
};
在这个例子中,operator int()
是一个转换函数,它将 Distance
对象转换为整数类型。
使用转换函数
转换函数可以隐式调用。例如:
Distance d1(1, 50); // 1 米 50 厘米
int totalCm = d1; // 隐式调用转换函数,totalCm 的值为 150
也可以显式调用转换函数:
Distance d1(1, 50); // 1 米 50 厘米
int totalCm = d1.operator int(); // 显式调用转换函数,totalCm 的值为 150
注意事项
- 隐式转换:转换函数可以导致隐式类型转换,这可能会引起意外的行为。因此,在设计类时要谨慎使用转换函数。
- 显式转换:如果希望避免隐式转换,可以使用
explicit
关键字来声明转换函数。例如:
class Distance {
private:int meters;int centimeters;public:Distance(int m, int cm) : meters(m), centimeters(cm) {}// 显式转换函数explicit operator int() const {return meters * 100 + centimeters;}
};
使用 explicit
关键字后,转换函数只能显式调用:
Distance d1(1, 50); // 1 米 50 厘米
int totalCm = static_cast<int>(d1); // 显式调用转换函数,totalCm 的值为 150
转换函数的优点和缺点
优点:
- 提供了一种方便的方式来实现类型转换。
- 可以使类对象在需要其他类型的地方使用。
缺点:
- 可能会引起意外的隐式类型转换,导致代码难以理解和调试。
- 在某些情况下,可能会导致性能开销。
结论
转换函数是 C++ 中实现类型转换的强大工具,但需要谨慎使用。通过合理设计和使用 explicit
关键字,可以避免隐式转换带来的问题,提高代码的可读性和可维护性。
3、智能指针
智能指针的概念
智能指针是 C++ 中的一种特殊类型的指针,它可以自动地管理内存的分配和释放。智能指针可以帮助开发者避免内存泄漏和野指针的问题。
智能指针的实现
智能指针通常是通过使用模板来实现的。智能指针的实现通常包括以下几个部分:
- 引用计数器:智能指针使用引用计数器来跟踪指针指向的对象的引用次数。如果对象的引用次数为 0,智能指针将自动释放对象的内存。
- 指针:智能指针包含一个指针,指向对象的内存地址。
- 管理函数:智能指针提供了一些管理函数,例如
reset()
、get()
、operator->()
等,可以用来管理指针和对象。
智能指针的分类
智能指针可以分为以下几种:
- unique_ptr:unique_ptr 是一种独占的智能指针,它只能被赋值一次。unique_ptr 使用引用计数器来管理对象的引用次数。
- shared_ptr:shared_ptr 是一种共享的智能指针,它可以被赋值多次。shared_ptr 使用引用计数器来管理对象的引用次数。
- weak_ptr:weak_ptr 是一种弱引用智能指针,它不持有对象的所有权。但是,它可以访问对象的内存。
智能指针的使用
智能指针可以在很多场景下使用,例如:
- 内存管理:智能指针可以用来管理内存的分配和释放,避免内存泄漏和野指针的问题。
- 对象管理:智能指针可以用来管理对象的生命周期,避免对象的内存泄漏和野指针的问题。
- 资源管理:智能指针可以用来管理资源的释放,例如文件、网络连接等。
智能指针的优点和缺点
优点:
- 自动内存管理:智能指针可以自动地管理内存的分配和释放,避免内存泄漏和野指针的问题。
- 简化代码:智能指针可以简化代码,避免使用原始指针和手动内存管理。
- 提高安全性:智能指针可以提高代码的安全性,避免内存泄漏和野指针的问题。
缺点:
- 性能开销:智能指针可能会带来性能开销,例如引用计数器的计算和对象的复制。
- 复杂性:智能指针可能会增加代码的复杂性,需要了解智能指针的实现机制和使用方法。
- 限制:智能指针可能会限制开发者的自由度,例如不能使用原始指针和手动内存管理。
结论
智能指针是 C++ 中的一种特殊类型的指针,它可以自动地管理内存的分配和释放。智能指针可以提高代码的安全性和简化代码,但是也可能会带来性能开销和复杂性。开发者需要根据具体情况选择合适的智能指针类型和使用方法。
4、STL
C++ 的 STL (Standard Template Library,标准模板库) 是一个强大的工具集,提供了许多常用的数据结构和算法,极大地提高了 C++ 程序的开发效率。它包含以下几个主要组件:
数据结构
4.1、序列容器 (Sequence Containers): 元素按照顺序存储,可以通过索引访问。
vector
: 动态数组,支持快速随机访问,插入和删除元素在尾部效率高,在中间效率低。deque
: 双端队列,支持快速在头部和尾部插入和删除元素。list
: 双向链表,支持快速在任意位置插入和删除元素,但随机访问效率低。forward_list
: 单向链表,只支持单向遍历,比list
更节省空间。array
: 固定大小的数组,在编译时确定大小。
4.2、关联容器(Associative Containers): 元素按照键值对存储,可以通过键快速查找元素。
set
: 存储唯一元素的集合,元素按顺序排列。multiset
: 存储元素的集合,允许重复元素,元素按顺序排列。map
: 键值对映射,键唯一,按键排序。multimap
: 键值对映射,键可以重复,按键排序。unordered_set
: 存储唯一元素的集合,元素无序,查找效率高。unordered_multiset
: 存储元素的集合,允许重复元素,元素无序,查找效率高。unordered_map
: 键值对映射,键唯一,无序,查找效率高。unordered_multimap
: 键值对映射,键可以重复,无序,查找效率高。
4.3、配器 (Adapters): 容器适配器是对现有容器的包装,提供了不同的接口。
stack
: 栈,后进先出 (LIFO)。queue
: 队列,先进先出 (FIFO)。priority_queue
: 优先级队列,元素按优先级排序。
算法
算法 (Algorithms): STL 提供了大量的算法,用于对容器中的数据进行操作,例如排序、查找、复制、删除等。这些算法都是泛型的,可以应用于各种容器。 常见的算法包括:
sort()
: 排序find()
: 查找copy()
: 复制remove()
: 删除transform()
: 变换accumulate()
: 求和min_element()
,max_element()
: 查找最小/最大元素
迭代器
迭代器 (Iterators): 迭代器是访问容器元素的一种方式,它提供了一种统一的接口来访问不同容器中的元素。迭代器类似于指针,但比指针更安全,更灵活。 迭代器类型包括:
- 输入迭代器 (Input Iterator)
- 输出迭代器 (Output Iterator)
- 前向迭代器 (Forward Iterator)
- 双向迭代器 (Bidirectional Iterator)
- 随机访问迭代器 (Random Access Iterator)
函数对象
函数对象 (Functors): 函数对象是重载了 operator()
的类对象,可以像函数一样使用。函数对象可以用来定制算法的行为。
STL 的优势:
- 代码重用性: STL 提供了大量的预定义组件,可以减少代码编写量,提高代码重用性。
- 效率: STL 的组件都是经过高度优化的,效率很高。
- 可扩展性: STL 的组件都是基于模板的,可以很容易地扩展到新的数据类型。
- 标准化: STL 是 C++ 标准的一部分,所有符合标准的编译器都支持 STL。
使用 STL 的建议:
- 选择合适的容器:根据实际需求选择合适的容器类型,例如,如果需要频繁插入和删除元素,则可以选择
list
或deque
;如果需要快速随机访问元素,则可以选择vector
。 - 理解迭代器:熟练掌握迭代器是使用 STL 的关键。
- 使用算法:STL 提供了大量的算法,可以简化代码,提高效率。
- 注意效率:虽然 STL 组件效率很高,但在某些情况下,仍然需要注意效率问题,例如,避免不必要的复制操作。
总而言之,C++ STL 是一个功能强大的工具集,熟练掌握 STL 可以大大提高 C++ 程序的开发效率和代码质量。 学习 STL 需要理解其核心组件之间的关系和使用方法,以及各种容器和算法的特性和适用场景。