太长了,分两篇写吧
书接上回
std::numeric_limits 是 C++ 标准库中定义的一个模板类,位于 头文件中。它提供了关于各种数值类型的属性和极值的信息。这些信息可以帮助我们在程序中进行数值处理时,了解特定类型的数值范围、精度以及其他一些与数值相关的特性。
类型 | 最小长度 |
---|---|
char | 1 byte |
short int | 2 bytes |
int | 2 bytes |
long int | 4 bytes |
long long int | 8 bytes |
float | 4 bytes |
double | 8 bytes |
long double | 8 bytes |
template<typename T>
class numeric_limits {
public:
static constexpr bool is_specialized = false;
static constexpr T min() noexcept {
return T();
}
static constexpr T max() noexcept {
return T();
}
// 其他成员函数和常量...
};
通用模板提供默认极值是指当使用 std::numeric_limits 的通用模板时,如果没有对特定类型进行特化,它将提供一组默认的极值。
对于基本数据类型(如整数、浮点数等),通用模板会根据类型的特性提供合适的默认极值。例如,对于有符号整数类型 int,std::numeric_limits::min() 返回的是该类型的最小值,std::numeric_limits::max() 返回的是该类型的最大值。
对于浮点数类型,std::numeric_limits::min() 返回的是该类型的最小正值,std::numeric_limits::max() 返回的是该类型的最大有限值。
通用模板还提供其他一些默认的极值信息,如精度、舍入误差等。这些默认的极值是基于类型的一般特性和标准规范而定义的。
#include
#include
int main() {
std::cout << "int 类型的最小值:" << std::numeric_limits<int>::min() << std::endl;
std::cout << "int 类型的最大值:" << std::numeric_limits<int>::max() << std::endl;
std::cout << "float 类型的最小精度:" << std::numeric_limits<float>::epsilon() << std::endl;
std::cout << "double 类型的正无穷大:" << std::numeric_limits<double>::infinity() << std::endl;
std::cout << "double 类型的静默非数值(NaN):" << std::numeric_limits<double>::quiet_NaN() << std::endl;
std::cout << "double 类型的位数:" << std::numeric_limits<double>::digits << std::endl;
return 0;
}
对于某些特定的数据类型,如用户自定义的类类型或特殊的数值类型,这些默认值可能不适用,因为这些类型可能具有不同的数值范围或特殊行为。为了满足这些特殊情况,可以对 std::numeric_limits 进行特化。
特化是通过提供特定类型的模板特化版本来实现的,以覆盖默认的属性和极值。通过特化 std::numeric_limits,我们可以为特定类型提供自定义的极值和属性信息。
以下是一个示例,展示了如何特化 std::numeric_limits 来为自定义类型 MyType 提供自定义的极值和属性信息:
#include
#include
class MyType {
// 自定义类型的定义和实现
};
namespace std {
template <>
class numeric_limits<MyType> {
public:
static constexpr bool is_specialized = true;
static constexpr MyType min() { return MyType{}; } // 自定义的最小值
static constexpr MyType max() { return MyType{}; } // 自定义的最大值
};
}
int main() {
std::cout << "MyType 类型的最小值:" << std::numeric_limits<MyType>::min() << std::endl;
std::cout << "MyType 类型的最大值:" << std::numeric_limits<MyType>::max() << std::endl;
return 0;
}
在上面的示例中,我们通过在 std 命名空间下定义一个特化版本的 std::numeric_limits 来为自定义类型 MyType 提供了自定义的最小值和最大值。通过特化,我们可以根据需要为任何类型提供自定义的极值和属性信息。
需要注意的是,特化 std::numeric_limits 是一种高级用法,通常在处理自定义类型或特殊需求时才会使用。对于大多数基本数据类型,通常使用默认的 std::numeric_limits 即可满足需求。
通用性numeric_limits和特化版本都被放在< limite >头文件中,特化版本涵盖所有数值基础类型。
原本的C常量定义于
类型特性(Type Traits)是C++中的一组模板类,用于在编译时获取和操作类型信息。它们提供了一种在编译时进行类型检查和操作的方式,使得编译器能够根据类型的属性进行条件编译、类型转换和模板特化等操作。
假设有一个函数可能传入整数型和浮点型的实参,通常需要写多个重载函数包括short,int,float,double等等类型,不但繁琐而且需要补充新的类型,而Type trait可以简化。
#include
#include
// 重载版本1:针对整数类型
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
process(T value) {
std::cout << "整数值:" << value << std::endl;
}
// 重载版本2:针对非整数类型
template <typename T>
typename std::enable_if<!std::is_integral<T>::value>::type
process(T value) {
std::cout << "非整数值:" << value << std::endl;
}
int main() {
process(10); // 调用重载版本1,传入整数类型
process(3.14); // 调用重载版本2,传入非整数类型
return 0;
}
这样只需要两份实现就包含了多种参数类型
共通类型(Common Type)指的是一组类型中能够进行操作并返回一个一致类型的最低公共类型。在C++中,可以使用 std::common_type 类型特性模板来获取一组类型的最低公共类型。
下面是一个示例代码,演示了如何使用 std::common_type 来处理共通类型:
#include
#include
template <typename T1, typename T2>
void process(T1 value1, T2 value2) {
using CommonType = typename std::common_type<T1, T2>::type;
CommonType result = value1 + value2;
std::cout << "结果:" << result << std::endl;
}
int main() {
process(10, 3.14); // 传入整数和浮点数
process(5.5, 2.7); // 传入两个浮点数
process(7, 8); // 传入两个整数
return 0;
}
在上述示例中,我们定义了一个 process 函数模板,接受两个参数 value1 和 value2,并使用 std::common_type 获取这两个参数的最低公共类型 CommonType。然后,我们使用 CommonType 来声明一个变量 result,对 value1 和 value2 进行加法运算并将结果赋值给 result。最后,我们输出结果到控制台。
在 main 函数中,我们分别调用了 process 函数传入不同类型的参数,包括整数和浮点数。std::common_type 会自动推导出最低公共类型,并将结果进行相应的类型转换和运算。
通过使用 std::common_type,我们可以处理一组具有不同类型的参数,并获取它们的最低公共类型,从而实现对共通类型的处理。这在泛型编程和模板元编程中非常有用,可以处理各种类型组合的操作。
注意bool和所有character都被视为整数类
std::reference_wrapper 是 C++ 标准库中的类模板,位于 头文件中。它用于包装引用类型,提供了一种轻量级的引用语义,允许以引用的方式传递和操作对象。
std::reference_wrapper 的主要作用是将引用类型封装成对象,使其具有对象语义。这样可以方便地将引用作为函数参数传递,存储在容器中,或者用于其他需要对象的场合,而不会引入额外的指针语义和内存管理。
使用 std::reference_wrapper 可以实现以下功能:
下面是一个示例代码,演示了如何使用
#include
#include
void increment(int& value) {
++value;
}
int main() {
int num = 10;
std::reference_wrapper<int> ref(num);
increment(ref);
std::cout << "num: " << num << std::endl;
return 0;
}
在上述示例中,我们定义了一个 increment 函数,接受一个引用参数并将其递增。在 main 函数中,我们创建了一个 std::reference_wrapper 对象 ref,将 num 的引用传递给它。然后,我们调用 increment 函数并传递 ref,实际上是传递了 num 的引用。最终,我们输出 num 的值,可以看到它已经被递增了。
通过使用 std::reference_wrapper,我们可以以对象的方式传递和操作引用类型,提供了更方便和灵活的引用语义。它在泛型编程、函数对象、STL 算法等场景中经常使用。
std::function 是 C++ 标准库中提供的一个类模板,用于封装不同类型的可调用对象,并提供一致的调用接口。
std::function 的类模板定义如下:
template<class R, class... Args>
class function<R(Args...)>;
其中,R 是返回类型,Args… 是参数类型列表。
通过使用 std::function,可以创建一个函数对象,该对象可以包装不同类型的可调用实体,例如函数指针、函数对象、成员函数指针、Lambda 表达式等。
std::function 提供了以下主要功能和用法:
下面是一个示例,演示了如何使用 std::function:
#include
#include
int add(int a, int b) {
return a + b;
}
class Foo {
public:
int multiply(int a, int b) {
return a * b;
}
};
int main() {
std::function<int(int, int)> func;
func = add;
int result1 = func(3, 4);
std::cout << "Result1: " << result1 << std::endl;
Foo foo;
func = std::bind(&Foo::multiply, &foo, std::placeholders::_1, std::placeholders::_2);
int result2 = func(3, 4);
std::cout << "Result2: " << result2 << std::endl;
return 0;
}
在上述示例中,我们首先定义了一个 add 函数和一个 Foo 类,其中 Foo 类具有一个成员函数 multiply。然后,我们声明了一个 std::function 对象 func,它的函数类型为 int(int, int)。
我们首先将 add 函数赋值给 func,然后使用 func 调用 add 函数并输出结果。接下来,我们使用 std::bind 将 Foo 类的成员函数 multiply 绑定到 func,并传递一个 Foo 类的对象指针和两个占位符作为参数。最后,我们使用 func 调用 Foo::multiply 并输出结果。
占位符(placeholders)是 C++ 标准库 中的特殊对象,用于占据函数对象的参数位置。在函数对象中,占位符表示该位置的参数将在实际调用时被替换为相应的值。
在 std::bind 的用法中,占位符 std::placeholders::_1、std::placeholders::_2、std::placeholders::_3,以及更多依次递增的占位符 _4、_5 等,用于标识函数对象的参数位置。通过占位符,我们可以在绑定函数时指定参数的位置,而不需要提供实际的参数值。
在示例代码中,我们使用了两个占位符 std::placeholders::_1 和 std::placeholders::_2:
func = std::bind(&Foo::multiply, &foo, std::placeholders::_1, std::placeholders::_2);
在这里,我们将 Foo::multiply 成员函数绑定到 func,并使用两个占位符指定参数的位置。这意味着当我们调用 func 时,参数将被传递到占位符所对应的位置。
在实际调用 func(3, 4) 时,占位符 std::placeholders::_1 对应第一个参数,即 3,占位符 std::placeholders::_2 对应第二个参数,即 4。因此,参数 3 和 4 将被传递给 Foo::multiply 成员函数作为实际的参数。
占位符的使用可以使函数对象更加灵活,尤其在函数对象的绑定和参数位置映射方面。它允许我们推迟参数的传递,并在调用函数对象时动态地提供参数值。
需要注意的是,占位符的个数和位置应与函数对象的参数个数和位置相匹配。否则,将导致参数传递的不正确或编译错误。
在示例中,使用占位符的目的是将绑定的函数对象与 foo 对象和参数位置关联起来。这样,当我们调用绑定的函数对象时,它会在 foo 对象上调用 multiply 函数,并将相应位置的参数传递给该函数。
通过 std::function,我们可以将不同类型的可调用对象封装为一个统一的对象,并以统一的方式进行调用。这提供了更大的灵活性和通用性,使得函数对象的使用更加方便和可扩展。
所有minmax()函数和所有带初值列的函数都始于c++11
当我们使用 头文件中的函数时,通常需要指定比较函数(或谓词),以确定元素的顺序或满足其他条件。比较函数是一个可调用对象,接受两个参数,并返回一个布尔值,用于比较两个元素的顺序。
以下是一个使用比较函数进行排序的例子:
#include
#include
#include
bool cmp(int a, int b) {
// 按照绝对值大小进行比较
return std::abs(a) < std::abs(b);
}
int main() {
std::vector<int> nums = {-3, 2, -1, 4, -5};
std::sort(nums.begin(), nums.end(), cmp);
for (const auto& num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,我们定义了一个比较函数 cmp,它按照元素的绝对值大小进行比较。然后,我们使用 std::sort 函数对 nums 容器进行排序,传入 cmp 函数作为比较函数。
排序结果会按照元素的绝对值从小到大进行排序,输出结果为:-1, 2, -3, 4, -5。
通过自定义比较函数,我们可以根据特定的条件对元素进行排序或比较。这在许多算法和操作中都非常有用,例如自定义的排序顺序、按照特定规则筛选元素等等。根据不同的需求,我们可以编写不同的比较函数来满足特定的排序或比较逻辑。
std::swap 是 C++ 标准库中的一个通用的交换函数,位于 头文件中。
std::swap 函数有多个重载版本,用于交换不同类型的对象。它的通用模板定义如下:
namespace std {
template <class T>
void swap(T& a, T& b);
}
其中,T 是要交换的对象的类型,a 和 b 是要交换的对象的引用。
当调用 std::swap(a, b) 时,根据实际的对象类型,编译器会选择适当的重载版本来执行交换操作。对于内置类型和标准库提供的类型,std::swap 已经有默认的特化版本定义。
此外,对于自定义类型,我们可以通过定义该类型的特化版本或提供自定义的 swap 函数来实现自定义类型的交换操作。例如:
namespace std {
template <>
void swap<MyType>(MyType& a, MyType& b) {
// 自定义类型的交换操作
}
}
通过在 std 命名空间中提供自定义类型 MyType 的特化版本,我们可以重载 std::swap 函数,以便在交换自定义类型对象时执行特定的交换操作。
以下是使用 std::swap 进行值交换的示例:
#include
#include
int main() {
int a = 10;
int b = 20;
std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
std::swap(a, b);
std::cout << "After swap: a = " << a << ", b = " << b << std::endl;
return 0;
}
在上述示例中,我们定义了两个整数变量 a 和 b,并分别初始化为 10 和 20。然后,我们使用 std::swap 函数交换了 a 和 b 的值。
输出结果为:
Before swap: a = 10, b = 20
After swap: a = 20, b = 10
通过使用 std::swap,我们可以方便地交换两个对象的值,而无需显式编写临时变量或自定义交换函数。这在许多情况下都非常有用,例如在排序算法、容器操作等需要交换值的场景中。自c++11起,标准库提供了针对array的重载版本。另外,对于自定义类型,可以通过重载 operator= 实现自定义的交换操作。
总结起来,std::swap 是 C++ 标准库提供的通用交换函数,可以用于交换不同类型的对象。对于内置类型和标准库提供的类型,已经有默认的特化版本定义。对于自定义类型,我们可以通过特化版本或自定义的 swap 函数来实现自定义类型的交换操作。
在 C++11 中,可以使用以下操作符来比较两个对象的大小关系:
!=(不等于):用于检查两个对象是否不相等。
>(大于):用于检查一个对象是否大于另一个对象。
<(小于):用于检查一个对象是否小于另一个对象。
>=(大于等于):用于检查一个对象是否大于或等于另一个对象。
<=(小于等于):用于检查一个对象是否小于或等于另一个对象。
这些操作符可以用于内置类型(例如整数、浮点数等)和用户自定义类型,前提是对应的操作符重载已经定义或者对象支持相关的比较操作。
以下是一个示例,展示如何使用这些操作符来比较两个整数:
#include
int main() {
int a = 10;
int b = 5;
if (a != b) {
std::cout << "a is not equal to b" << std::endl;
}
if (a > b) {
std::cout << "a is greater than b" << std::endl;
}
if (a < b) {
std::cout << "a is less than b" << std::endl;
}
if (a >= b) {
std::cout << "a is greater than or equal to b" << std::endl;
}
if (a <= b) {
std::cout << "a is less than or equal to b" << std::endl;
}
return 0;
}
输出结果为:
a is not equal to b
a is greater than b
a is not less than b
a is greater than or equal to b
a is not less than or equal to b
在上述示例中,我们比较了两个整数 a 和 b,并根据它们的大小关系输出相应的消息。注意,如果要比较自定义类型的对象,需要重载相应的比较操作符。
std::ratio 是 C++ 标准库中的一个模板类,用于表示有理数比例。它位于 头文件中。
std::ratio 模板类的定义如下:
template <intmax_t Num, intmax_t Denom = 1>
class ratio;
其中,Num 表示分子,Denom 表示分母,默认值为 1。Num 和 Denom 都必须是整数类型。
std::ratio 模板类提供了一种在编译时表示有理数比例的方式,它可以用于进行单位转换、计算比例关系等操作。它的主要作用是提供一种编译时的静态类型来表示常见的比例关系,而不需要在运行时进行计算。
以下是使用 std::ratio 的示例:
#include
#include
int main() {
using one_half = std::ratio<1, 2>;
using one_third = std::ratio<1, 3>;
using two_thirds = std::ratio_add<one_third, one_third>;
std::cout << "1/2 = " << one_half::num << "/" << one_half::den << std::endl;
std::cout << "1/3 = " << one_third::num << "/" << one_third::den << std::endl;
std::cout << "1/3 + 1/3 = " << two_thirds::num << "/" << two_thirds::den << std::endl;
return 0;
}
输出结果为:
1/2 = 1/2
1/3 = 1/3
1/3 + 1/3 = 2/3
在这个示例中,我们定义了三个 std::ratio 类型的别名:one_half 表示 1/2,one_third 表示 1/3,two_thirds 表示 1/3 + 1/3。
通过访问 std::ratio 类的 num 和 den 静态成员变量,我们可以获取有理数比例的分子和分母,并进行输出。
C++ 标准库中的 头文件提供了一些预定义的 std::ratio 单位,用于表示常见的比例关系
这些预定义单位使得在表示物理量时更加方便,可以直接使用它们进行单位转换和比例计算。例如,std::kilo 表示千,可以将一个值乘以 std::kilo 来将其转换为千倍。
需要注意的是,std::ratio 是一个编译时计算的类型,它的值在编译时确定,并且不支持运行时的算术运算。它主要用于在编译时进行单位转换和比例计算,例如在模板元编程和静态类型检查等方面有广泛的应用。
C++11 中引入了 头文件,提供了 Clock 和 Timer 相关的功能,用于测量时间和实现定时器。
C++11 中的 头文件引入了 std::chrono::duration 类模板,用于表示时间间隔或持续时间。std::chrono::duration 是一个通用的时间量类,可以用于表示不同单位的时间间隔,例如秒、毫秒、微秒等。
std::chrono::duration 的模板参数包括两个部分:Rep 和 Period。Rep 表示时间间隔的底层类型,通常是一个算术类型(如 int、double 等),而 Period 表示时间间隔的单位。Period 是一个 std::ratio 类型的实例,用于表示分子和分母的比例关系,决定了时间间隔的单位。
以下是一个使用 std::chrono::duration 的示例:
#include
#include
int main() {
// 定义一个持续时间为 5 秒的 duration
std::chrono::duration<int> durationSeconds(5);
// 定义一个持续时间为 2.5 秒的 duration
std::chrono::duration<double> durationSecondsDouble(2.5);
// 输出 duration 的数值和单位
std::cout << "durationSeconds: " << durationSeconds.count() << " seconds" << std::endl;
std::cout << "durationSecondsDouble: " << durationSecondsDouble.count() << " seconds" << std::endl;
// 将 duration 转换为毫秒
std::chrono::duration<int, std::milli> durationMilliseconds = std::chrono::duration_cast<std::chrono::duration<int, std::milli>>(durationSeconds);
// 输出转换后的 duration 的数值和单位
std::cout << "durationMilliseconds: " << durationMilliseconds.count() << " milliseconds" << std::endl;
return 0;
}
在这个示例中,我们创建了两个不同类型的 std::chrono::duration 对象:一个是持续时间为 5 秒的整数类型的 duration,另一个是持续时间为 2.5 秒的双精度浮点类型的 duration。我们使用 count() 成员函数来获取 duration 的数值,并使用 std::chrono::duration_cast 进行单位转换。
输出结果为:
durationSeconds: 5 seconds
durationSecondsDouble: 2.5 seconds
durationMilliseconds: 5000 milliseconds
通过使用 std::chrono::duration,可以方便地对时间间隔进行计算、转换和表示,而不需要手动管理不同单位之间的转换。
Clock 是一个时间测量的基准。C++11 提供了三种类型的时钟:
这些时钟提供了 now() 成员函数,用于获取当前时钟的时间点。
以下是使用 std::chrono::steady_clock 的示例:
#include
#include
int main() {
auto start = std::chrono::steady_clock::now();
// 执行一些操作
auto end = std::chrono::steady_clock::now();
auto duration = end - start;
std::cout << "耗时:" << std::chrono::duration<double>(duration).count() << " 秒" << std::endl;
return 0;
}
在这个示例中,我们使用 std::chrono::steady_clock 获取了开始和结束的时间点,并计算了它们之间的时间差,得到了程序的执行时间。
C++11 没有直接提供定时器的标准库类,但你可以使用 Clock 和一些其他的函数来实现定时器功能。例如,你可以使用 std::this_thread::sleep_for() 函数来让线程休眠一段时间实现简单的定时器功能。
以下是一个使用 std::this_thread::sleep_for() 实现定时器的示例:
#include
#include
#include
int main() {
int count = 0;
int target = 5;
while (count < target) {
std::this_thread::sleep_for(std::chrono::seconds(1));
count++;
std::cout << "定时器触发:" << count << std::endl;
}
std::cout << "定时器结束" << std::endl;
return 0;
}
在这个示例中,我们使用 std::this_thread::sleep_for() 函数来让程序休眠 1 秒钟,并输出定时器触发的次数。程序会在计数达到目标值时结束定时器。
需要注意的是,定时器的实现方式可能因操作系统和编译器而有所不同。以上示例仅提供了一种简单的实现方式,实际应用中可能需要更复杂的定时器功能,可以根据具体需求进行扩展和优化。
原本在< time.h >内的定义现在都被纳入了< ctime >
在 C++11 的 头文件中,可以使用 std::chrono::time_point 类模板来表示时间点(Time Point)。时间点是一个特定时刻的抽象概念,与日历时间(Calendar Time)相对应。
要在时间点和日历时间之间进行转换,可以使用以下函数和类型:
std::chrono::system_clock:std::chrono::system_clock 是一个时钟类型,用于表示系统的当前时间。可以使用它来获取当前的日历时间。
std::chrono::time_point:std::chrono::time_point 是一个表示时间点的类模板。可以使用它来存储和操作时间点的值。
std::chrono::duration:std::chrono::duration 是一个表示时间间隔的类模板。可以使用它来计算时间点之间的差异。
以下是时间点和日历时间之间的转换示例:
#include
#include
#include
int main() {
// 获取当前的日历时间
std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// 将日历时间转换为时间点
std::chrono::time_point<std::chrono::system_clock> timePoint = std::chrono::system_clock::from_time_t(currentTime);
// 将时间点转换为日历时间
std::time_t convertedTime = std::chrono::system_clock::to_time_t(timePoint);
// 输出转换后的日历时间
std::cout << "Converted time: " << std::asctime(std::localtime(&convertedTime));
return 0;
}
在这个示例中,我们首先使用 std::chrono::system_clock::now() 获取当前的时间点,然后使用 std::chrono::system_clock::to_time_t() 将时间点转换为日历时间(std::time_t 类型)。接着,我们使用 std::chrono::system_clock::from_time_t() 将日历时间转换回时间点。最后,我们使用 std::asctime() 将转换后的日历时间输出到标准输出流。
需要注意的是,时间点和日历时间的精度和范围可能会有所不同。时间点通常具有更高的精度和更大的范围,而日历时间受限于 std::time_t 类型的精度和范围。
使用时间点和日历时间之间的转换,可以方便地在不同的时间表示之间进行转换和操作,以满足不同的需求。
计时器常在线程中使用,这一部分在后面的线程章节详细描述
头文件定义了一些常见的库函数,如内存管理函数(如 malloc 和 free)、类型转换函数(如 atoi 和 atof)、随机数函数(如 rand 和 srand)以及其他一些常见的函数。
#include
#include
#include
#include
#include
int main() {
// 使用 cstddef 定义的类型
std::size_t size = 10;
std::ptrdiff_t diff = 5;
std::nullptr_t nullPtr = nullptr;
// 使用 cstdlib 的库函数
int randomNumber = std::rand();
std::cout << "Random number: " << randomNumber << std::endl;
// 使用 cstring 的字符串操作函数
char str1[] = "Hello";
char str2[10];
std::strcpy(str2, str1);
std::strcat(str2, " World");
std::cout << "Concatenated string: " << str2 << std::endl;
// 使用 string 类进行字符串操作
std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = s1 + " " + s2;
std::cout << "Concatenated string: " << s3 << std::endl;
return 0;
}
这个示例演示了如何使用 、、 和 头文件进行基本类型、库函数和字符串操作。注意,在使用 C++ 中的字符串时,推荐使用 std::string 类来代替传统的 C 风格字符串(以字符数组表示)以获得更安全和方便的字符串操作功能。