时间:2025-04-18 11:07
人气:
作者:admin
[!tip]
在 C++98 中,所有的对象拷贝都使用了 拷贝构造函数 或 拷贝赋值运算符,这通常需要深拷贝资源,例如动态分配的内存或文件句柄。这会导致性能开销,尤其是对于临时对象而言(如函数返回值)。
为了解决这个问题,C++11 引入了移动语义,通过区分“拷贝”和“移动”两种语义,大幅提高性能
int a = 10; a 是左值10 或临时对象 a + b 的结果C++11 中,右值被细化为 纯右值(prvalue) 和 亡值(xvalue),其中亡值用于表示即将失去所有权的对象(如返回值)
class MyClass {
int* data;
public:
// 构造函数
MyClass(int val) : data(new int(val)) {}
// 移动构造函数
MyClass(MyClass&& other) noexcept : data(other.data) {
other.data = nullptr; // 释放所有权
}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) {
delete data; // 释放原有资源
data = other.data; // 转移所有权
other.data = nullptr;
}
return *this;
}
~MyClass() { delete data; }
};
C++11 新增了一种类型引用:右值引用,使用 && 表示,右值引用只绑定到右值。
int x = 0; // `x` is an lvalue of type `int`
int& xl = x; // `xl` is an lvalue of type `int&`
int&& xr = x; // compiler error -- `x` is an lvalue
int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
void f(int& x) {}
void f(int&& x) {}
f(x); // calls f(int&)
f(xl); // calls f(int&)
f(3); // calls f(int&&)
f(std::move(x)); // calls f(int&&)
f(xr2); // calls f(int&)
f(std::move(xr2)); // calls f(int&& x)
右值引用的主要目的是:
也被称为Universal References(万能引用、通用引用),与std::forward一起使用实现完美转发(Perfect Forwarding。转发引用是通过语法 T&& 创建的,其中 T 是模板类型参数,或使用 auto&&
主要作用是:根据传入参数的值类别(左值或右值)保持其原始类别,从而正确地传递给另一个函数(例如,左值保持为左值,临时对象作为右值转发)
转发引用允许引用根据类型绑定到左值或右值。转发引用遵循引用折叠规则:
T& & 变成 T&T& && 变成 T&T&& & 变成 T&T&& && 变成 T&&auto 类型推导与左值和右值:
int x = 0; // `x` is an lvalue of type `int`
auto&& al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto&& ar = 0; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
带有左值和右值的模板类型参数推导:
// Since C++14 or later:
void f(auto&& t) {
// ...
}
// Since C++11 or later:
template <typename T>
void f(T&& t) {
// ...
}
int x = 0;
f(0); // T is int, deduces as f(int &&) => f(int&&)
f(x); // T is int&, deduces as f(int& &&) => f(int&)
int& y = x;
f(y); // T is int&, deduces as f(int& &&) => f(int&)
int&& z = 0; // NOTE: `z` is an lvalue with type `int&&`.
f(z); // T is int&, deduces as f(int& &&) => f(int&)
f(std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
[!note]
有
int x = 10,为什么int&& y = x会出错,而auto&& y = x却没有问题?
int && y = x中,x是一个左值,而y是一个右值引用,右值引用只能绑定右值auto&& y = x中,auto&&是一个万能引用,x是左值时,其会自动推导为int&,x是右值时,其会自动推到为int&&,所以该式子会自动推导为int& y = x
复制构造函数和复制赋值运算符在进行复制时被调用,而随着 C++11 引入移动语义,现在有了用于移动的移动构造函数和移动赋值运算符。目的是提高性能,特别是当操作涉及临时对象时
移动构造函数的作用是“窃取”另一个对象的资源,而不是复制资源,从而避免了不必要的深拷贝操作。
特点:
T&&)作为参数。nullptr)#include <iostream>
#include <utility> // for std::move
class MyClass {
int* data;
public:
// 构造函数
MyClass(int val) : data(new int(val)) {
std::cout << "Constructed with value: " << val << std::endl;
}
// 移动构造函数
MyClass(MyClass&& other) noexcept : data(other.data) {
other.data = nullptr; // 释放源对象的所有权
std::cout << "Moved!" << std::endl;
}
// 析构函数
~MyClass() {
if (data) {
std::cout << "Destroyed: " << *data << std::endl;
delete data;
} else {
std::cout << "Destroyed empty object." << std::endl;
}
}
};
int main() {
MyClass obj1(10); // 正常构造
MyClass obj2(std::move(obj1)); // 使用移动构造
return 0;
}
... 语法创建一个参数包或展开一个参数包。它允许模板接受可变数量的参数。这是实现 泛型编程 和简化代码的重要特性
template<typename... Args>
void function(Args... args) {
// ...
}
...(省略号):
Args... 表示类型参数包,可以接收零个或多个类型。args... 表示非类型参数包,可以接收零个或多个值。...),否则无法直接操作其内容示例:
#include <iostream>
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << '\n'; // 参数包展开
}
int main() {
print(1, 2, 3, "Hello", 4.5); // 打印: 123Hello4.5
return 0;
}
#include <iostream>
// 基础函数:零参数时的终止条件
void print() {
std::cout << "End of arguments.\n";
}
// 可变参数模板
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " "; // 打印第一个参数
print(rest...); // 递归调用,展开剩余参数
}
int main() {
print(1, "Hello", 3.14, "World");
return 0;
}
#include <iostream>
template<typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...) << '\n'; // 左折叠
}
int main() {
print(1, "Hello", 3.14, "World");
return 0;
}
折叠表达式的形式:
(... op expor)(expr op ...)(init op ... op expr)(expr op ... op init)实现通用打印函数
#include <iostream>
template<typename... Args>
void log(const std::string& prefix, Args... args) {
std::cout << prefix << ": ";
(std::cout << ... << args) << '\n'; // 折叠表达式
}
int main() {
log("Info", "Hello", ", ", "World", "!", 123);
log("Error", "An error occurred: ", 404);
return 0;
}
转发参数
在 C++11 中,可变参数模板与 std::forward 结合,可实现 完美转发:
#include <iostream>
#include <utility>
void display(int x) {
std::cout << "Int: " << x << '\n';
}
void display(const std::string& str) {
std::cout << "String: " << str << '\n';
}
template<typename... Args>
void forwarder(Args&&... args) {
(display(std::forward<Args>(args)), ...); // 完美转发
}
int main() {
int x = 42;
std::string str = "Hello";
forwarder(x, str, 100, "World");
return 0;
}
构造函数的可变参数模板
可变参数模板常用于实现容器类的构造函数,支持任意数量的初始化参数:
#include <iostream>
#include <vector>
class MyContainer {
std::vector<int> data;
public:
template<typename... Args>
MyContainer(Args... args) : data{args...} {}
void print() const {
for (auto x : data) {
std::cout << x << " ";
}
std::cout << '\n';
}
};
int main() {
MyContainer c(1, 2, 3, 4, 5);
c.print(); // 输出: 1 2 3 4 5
return 0;
}
通过花括号 {} 提供一组值,并且这些值可以自动转换为 std::initializer_list 对象
特性:
构造函数的初始化
#include <iostream>
#include <initializer_list>
class MyClass {
std::vector<int> data;
public:
MyClass(std::initializer_list<int> list) {
std::cout << "Initialized with: ";
for (auto value : list) {
std::cout << value << " ";
}
std::cout << std::endl;
}
// 传统构造函数
MyClass(int n, int value) : data(n, value) {
std::cout << "Initialized with size and value.\n";
}
};
int main() {
MyClass obj1{1, 2, 3, 4}; // 使用初始化列表
MyClass obj2{10, 20}; // 使用另一个初始化列表
MyClass obj2(5, 10); // 使用传统方法初始化
return 0;
}
函数参数的初始化
#include <iostream>
#include <initializer_list>
void print(std::initializer_list<std::string> list) {
for (const auto& item : list) {
std::cout << item << " ";
}
std::cout << std::endl;
}
int main() {
print({"Hello", "World", "!"}); // 使用初始化列表
return 0;
}
容器初始化
#include <iostream>
#include <vector>
#include <set>
int main() {
std::vector<int> vec = {1, 2, 3, 4}; // 初始化 std::vector
std::set<std::string> s = {"apple", "banana", "cherry"}; // 初始化 std::set
for (auto v : vec) std::cout << v << " ";
std::cout << std::endl;
for (auto item : s) std::cout << item << " ";
return 0;
}
| 特性 | std::initializer_list |
普通数组 | 变长模板参数 |
|---|---|---|---|
| 语法 | {} |
{} |
template<typename... Args> |
| 是否支持动态大小 | 固定大小 | 固定大小 | 支持 |
| 元素类型 | 必须相同 | 必须相同 | 可不同 |
| 是否提供容器接口 | 是 | 否 | 否 |
| 常用场景 | 容器初始化、构造函数 | 数组初始化 | 函数调用 |
static_assert 是 C++11 引入的一种编译期断言工具,用于在编译时检查某些条件是否满足。如果条件不满足,编译器会报错并停止编译。
static_assert(constexpr_condition, message);
constexpr_condition:
true,否则编译器会报错。message:
类型属性检查
确保某些类型满足特定条件,例如大小、对齐方式、是否是整数类型等
template<typename T>
void checkType() {
static_assert(std::is_integral<T>::value, "T must be an integral type");
}
int main() {
checkType<int>(); // OK
checkType<double>(); // 编译失败:T must be an integral type
return 0;
}
构造编译期约束
在编译期对模板参数进行检查,防止传入不符合条件的类型或值。
template<typename T>
struct MyStruct {
static_assert(sizeof(T) <= 4, "T must be at most 4 bytes");
};
MyStruct<int> obj; // OK
MyStruct<double> obj; // 编译失败:T must be at most 4 bytes
平台相关检查
static_assert(sizeof(void*) == 8, "This code requires a 64-bit environment");
枚举值的合法性
enum class Color { Red, Green, Blue, Max };
static_assert(static_cast<int>(Color::Max) <= 3, "Color enum out of range");
assert) vs 静态断言 (static_assert)| 特性 | assert |
static_assert |
|---|---|---|
| 检查时间 | 运行时 | 编译时 |
| 适用场景 | 动态条件检查 | 编译期条件检查 |
| 性能开销 | 存在运行时开销 | 无运行时开销 |
| 表达式类型 | 任意布尔表达式 | constexpr(编译期可计算) |
| 使用时机 | 需要根据运行时数据进行验证 | 条件可以在编译期验证 |
| 示例 | assert(a > b); |
static_assert(sizeof(T) <= 4); |
如果不提供第二个参数,编译器会自动生成一条默认的错误消息:
static_assert(sizeof(int) == 4); // 默认错误消息
检查模板实例的正确性
确保模板实例化时的类型或值满足特定约束。
template<int N>
struct Factorial {
static_assert(N >= 0, "Factorial is undefined for negative numbers");
static constexpr int value = N * Factorial<N - 1>::value;
};
// 特化终止递归
template<>
struct Factorial<0> {
static constexpr int value = 1;
};
int main() {
constexpr int result = Factorial<5>::value; // OK
constexpr int invalid = Factorial< -1>::value; // 编译失败
return 0;
}
条件性静态断言
结合 if constexpr,在特定情况下启用断言
template<typename T>
void process() {
if constexpr (std::is_integral<T>::value) {
static_assert(sizeof(T) <= 4, "Integral type must be at most 4 bytes");
} else {
static_assert(sizeof(T) > 4, "Non-integral type must be larger than 4 bytes");
}
}
int main() {
process<int>(); // OK
process<float>(); // OK
return 0;
}
用于让编译器根据表达式的上下文自动推导变量的类型,从而简化代码,提升代码的可读性和维护性
int x = 10;
auto y = x; // y 的类型自动推导为 int
简化变量声明
std::vector<int> vec = {1, 2, 3, 4};
auto it = vec.begin(); // 自动推导迭代器类型
与范围 for 循环结合
std::vector<int> vec = {1, 2, 3, 4};
for (auto value : vec) {
std::cout << value << " ";
}
如果需要修改值,可以使用 auto&:
for (auto& value : vec) {
value *= 2; // 修改 vec 中的值
}
结合 Lambda 表达式
auto 可以用于推导 Lambda 表达式返回的类型。
auto lambda = [](int a, int b) { return a + b; };
std::cout << lambda(2, 3); // 输出:5
函数返回类型的自动推导
auto 可以用于函数返回值的类型推导,结合 decltype 或直接返回值推导。
(C++11) 使用 decltype:
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
(C++14 起) 自动推导返回类型:
template<typename T1, typename T2>
auto add(T1 a, T2 b) {
return a + b;
}
协助模板类型推导
template<typename Container>
void printContainer(const Container& c) {
for (auto it = c.begin(); it != c.end(); ++it) {
std::cout << *it << " ";
}
}
指针和引用类型
auto 会去掉顶层的指针或引用属性,但可以显式添加。
普通类型
int x = 10;
auto y = x; // y 的类型为 int
引用类型
int x = 10;
auto& y = x; // y 是 int&,绑定到 x
指针类型:
int x = 10;
int* p = &x;
auto ptr = p; // ptr 的类型为 int*
常量和顶层 const
auto 会去掉顶层 const,但保留底层 const。
顶层 const
const int x = 10;
auto y = x; // y 的类型为 int(顶层 const 被去掉)
底层 const:
const int x = 10;
const int* p = &x;
auto ptr = p; // ptr 的类型为 const int*(底层 const 被保留)
带初始化列表时的行为
auto 和 std::initializer_list 有特殊关系。
auto x = {1, 2, 3}; // x 的类型是 std::initializer_list<int>
如果需要 x 是数组或容器类型,可以显式指定类型
函数声明中的 auto
Lambda 表达式的返回类型:
auto lambda = [](int a, int b) { return a + b; };
普通函数返回类型
auto add(int a, int b) {
return a + b; // 返回类型由编译器推导
}
类型推导可能不符合预期
必须清楚 auto 推导的规则,避免误解。
const int x = 42;
auto y = x; // y 的类型是 int,不是 const int
可能影响代码的可维护性
当代码需要明确的类型信息时,滥用 auto 会降低可读性
auto可以避免代码冗余
当类型复杂时,auto可以避免重复声明类型
std::map<int, std::vector<int>> myMap;
auto it = myMap.begin(); // 避免写 std::map<int, std::vector<int>>::iterator
在模板和泛型编程中,auto 能自动适应变化的类型,提高代码的通用性。
lambda 是一个未命名的函数对象,能够捕获作用域中的变量,使用lambda可以简洁定义内联函数。
Lambda表达式的语法结构:
[捕获列表](参数列表) -> 返回类型 {
函数体
};
捕获列表 [ ]:
用于指定 lambda 捕获外部作用域的变量。
支持的捕获方式:
[]:不捕获任何变量。
[=]:以值捕获所有外部作用域的变量。[&]:以引用捕获所有外部作用域的变量。[x]:以值捕获变量 x。[&x]:以引用捕获变量 x。[=, &x]:以值捕获其他变量,以引用捕获 x。[&, x]:以引用捕获其他变量,以值捕获 x[this]:通过引用捕获this参数列表 ( ):
返回类型 -> 返回类型(可选):
函数体 { ... }:
基本用法
#include <iostream>
using namespace std;
int main() {
auto add = [](int a, int b) -> int {
return a + b;
};
cout << "3 + 4 = " << add(3, 4) << endl; // 输出 3 + 4 = 7
return 0;
}
捕获变量
#include <iostream>
using namespace std;
int main() {
int x = 10, y = 20;
auto printSum = [x, y]() {
cout << "Sum: " << x + y << endl;
};
printSum(); // 输出 Sum: 30
auto modifyY = [&y]() {
y += 10;
cout << "Modified y: " << y << endl;
};
modifyY(); // 输出 Modified y: 30
return 0;
}
在 STL 算法中的使用
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
// 使用 lambda 表达式来定义筛选条件
auto evenNumbers = count_if(numbers.begin(), numbers.end(), [](int n) {
return n % 2 == 0;
});
cout << "Even numbers count: " << evenNumbers << endl; // 输出 2
return 0;
}
可变捕获(mutable)
[!tip]
默认情况下,值捕获不能在 lambda 内部修改,因为编译器生成的方法被标记为
const,mutable关键字允许修改捕获的变量。该关键字放置在参数列表之后(即使为空也必须存在)
#include <iostream>
using namespace std;
int main() {
int value = 42;
auto captureByValue = [value]() mutable {
value += 10;
cout << "Inside lambda: " << value << endl; // 输出 52
};
captureByValue();
cout << "Outside lambda: " << value << endl; // 输出 42
return 0;
}
泛型 Lambda(C++14 引入)
#include <iostream>
using namespace std;
int main() {
auto multiply = [](auto a, auto b) {
return a * b;
};
cout << "3 * 4 = " << multiply(3, 4) << endl; // 输出 12
cout << "2.5 * 4.2 = " << multiply(2.5, 4.2) << endl; // 输出 10.5
return 0;
}
decltypedecltype 是一个运算符,主要用于推断表达式的类型,可以在编译时确定一个变量或表达式的类型。如果 cv 限定符(const,volatile)和引用是表达式的一部分,则会保留它们。
decltype 直接返回表达式的类型,而不会对表达式进行计算。decltype 返回的是该变量的实际类型(可能包含引用)。auto 使用
auto 不同,decltype 用于类型推导,但不进行类型修改。auto 通常用于变量初始化,而 decltype 更适合于定义与已有变量或表达式相同类型的新变量。decltype(auto), c++14特性decltype 是模板元编程的重要工具,可以精确地推断函数返回值的类型。decltype(expression) variable_name;
expression 是需要推断类型的表达式。variable_name 是声明的新变量。基本用法
#include <iostream>
using namespace std;
int main() {
int a = 10;
decltype(a) b = 20; // b 的类型与 a 一致,都是 int
cout << "a: " << a << ", b: " << b << endl;
return 0;
}
推断表达式的类型
#include <iostream>
using namespace std;
int main() {
int x = 5;
decltype(x + 1.0) y = 3.14; // x + 1.0 的类型是 double,所以 y 的类型也是 double
cout << "y: " << y << endl;
return 0;
}
推断引用类型
#include <iostream>
using namespace std;
int main() {
int x = 10;
int& ref = x;
decltype(ref) y = x; // y 的类型是 int&,所以 y 是 x 的引用
y = 20; // 改变 y 的值也会改变 x 的值
cout << "x: " << x << ", y: " << y << endl;
return 0;
}
与 auto 的对比
#include <iostream>
using namespace std;
int main() {
const int x = 10;
auto a = x; // auto 会去掉 const,a 是 int
decltype(x) b = x; // decltype 保持 const,b 是 const int
a = 20; // 合法
// b = 30; // 非法,因为 b 是 const
cout << "a: " << a << ", b: " << b << endl;
return 0;
}
用于函数返回类型推导
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
decltype(add(1, 2)) result = 5; // 推断 add 返回值的类型
cout << "result: " << result << endl;
return 0;
}
int a = 1; // `a` is declared as type `int`
decltype(a) b = a; // `decltype(a)` is `int`
const int& c = a; // `c` is declared as type `const int&`
decltype(c) d = a; // `decltype(c)` is `const int&`
decltype(123) e = 123; // `decltype(123)` is `int`
int&& f = 1; // `f` is declared as type `int&&`
decltype(f) g = 1; // `decltype(f) is `int&&`
decltype((a)) h = g; // `decltype((a))` is int&
在C++中,有两种方式定义类型别名:
typedefusing(C++11 引入,推荐方式)typedef 方式typedef 是一种传统方式,语法如下:
typedef existing_type new_type_name;
示例:
include <iostream>
#include <vector>
typedef unsigned int uint;
typedef std::vector<int> IntVector;
int main() {
uint a = 42; // uint 作为 unsigned int 的别名
IntVector vec = {1, 2, 3}; // IntVector 是 std::vector<int> 的别名
std::cout << "a: " << a << std::endl;
for (int val : vec) {
std::cout << val << " ";
}
return 0;
}
using 方式using 是 C++11 引入的语法,更加简洁且功能更强。语法如下:
using new_type_name = existing_type;
示例:
#include <iostream>
#include <map>
#include <string>
using StringIntMap = std::map<std::string, int>;
int main() {
StringIntMap myMap; // StringIntMap 是 std::map<std::string, int> 的别名
myMap["apple"] = 3;
myMap["banana"] = 5;
for (const auto &pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
using 与模板兼容
#include <iostream>
#include <vector>
// 定义模板别名
template <typename T>
using Vec = std::vector<T>;
int main() {
Vec<int> numbers = {1, 2, 3}; // Vec<int> 等价于 std::vector<int>
for (int n : numbers) {
std::cout << n << " ";
}
return 0;
}
强类型枚举(Strongly-typed enums),也称为scoped enums
类型安全的枚举解决了 C 风格枚举的各种问题,包括:隐式转换、无法指定底层类型、作用域污染
强类型枚举的定义方式是使用关键字 enum class 或 enum struct,两者功能相同,语法为:
enum class EnumName : underlying_type { ... };
全局作用域污染:传统枚举的枚举成员直接暴露在外部作用域,容易和其他标识符发生冲突。
类型安全性差:传统枚举隐式转换为整数类型,可能导致意外的比较或运算。
未指定底层类型:默认底层类型是 int,但无法直接控制。
#include <iostream>
enum Color { Red, Green, Blue }; // 枚举成员暴露在全局作用域
int main() {
Color color = Red; // 直接使用成员
int number = color; // 隐式转换为 int,类型不安全
std::cout << "Color as int: " << number << std::endl;
return 0;
}
uint8_t、int32_t 等),有助于节省内存或与特定数据结构兼容。#include <iostream>
#include <cstdint> // for fixed-width integer types
enum class Color : uint8_t { Red, Green, Blue }; // 定义强类型枚举
int main() {
Color color = Color::Red; // 使用限定作用域的成员访问方式
// int number = color; // 错误:强类型枚举不能隐式转换为整数
if (color == Color::Red) {
std::cout << "The color is Red!" << std::endl;
}
return 0;
}
| 特性 | 传统枚举 (Unscoped Enum) | 强类型枚举 (Strongly-typed Enum) |
|---|---|---|
| 作用域 | 全局作用域污染 | 成员属于枚举类型作用域 |
| 类型安全性 | 允许隐式转换为整数类型 | 不允许隐式转换 |
| 底层类型控制 | 默认 int,不可修改 |
可显式指定底层类型 |
| 适用性 | 适用于简单场景 | 适用于需要类型安全和作用域隔离的场景 |
[!IMPORTANT]
由于强类型枚举不能隐式转换为整数类型,以下操作需要显式转换:
- 将枚举值转换为整数:
static_cast<int>(EnumValue)- 将整数转换为枚举值:
static_cast<EnumType>(intValue)
#include <iostream>
enum class Days { Monday, Tuesday, Wednesday };
int main() {
Days today = Days::Tuesday;
// 显式转换为整数
int dayNumber = static_cast<int>(today);
std::cout << "Day number: " << dayNumber << std::endl;
return 0;
}
Attributes 是 C++11 引入的一种语言特性,用于向编译器提供额外的元信息,以帮助优化代码、检查错误或修改编译行为。它们不会直接影响程序的逻辑,但可以增强代码的可读性、可维护性,并减少潜在的错误。
语法:
[[attribute_name]]
[[attr1, attr2]]。[[nodiscard]]
指示调用者必须使用函数的返回值,避免因忽略返回值而导致潜在问题。
[[nodiscard]] int calculateSum(int a, int b) {
return a + b;
}
int main() {
calculateSum(2, 3); // 警告:返回值被忽略
int result = calculateSum(2, 3); // 正确
return 0;
}
[[maybe_unused]]
用于抑制未使用变量或参数的警告。
void func([[maybe_unused]] int unusedParam) {
// unusedParam 没有被使用
}
[[deprecated]]
标记为不建议使用的函数、类或变量,编译时会发出警告。
[[deprecated("Use newFunction() instead")]]
void oldFunction() {
// ...
}
void newFunction() {
// ...
}
int main() {
oldFunction(); // 警告:该函数已弃用
newFunction();
return 0;
}
[[fallthrough]]
在 switch 语句中明确表示允许无意的穿透(fall-through)。
void check(int value) {
switch (value) {
case 1:
// Intentional fall-through
[[fallthrough]];
case 2:
std::cout << "Value is 1 or 2\n";
break;
default:
std::cout << "Other value\n";
break;
}
}
[[likely]] 和 [[unlikely]]
提示编译器某个分支更可能或不太可能被执行(C++20 引入)
void process(int value) {
if ([[likely]] value > 0) {
std::cout << "Positive value\n";
} else {
std::cout << "Non-positive value\n";
}
}
为了避免冲突,C++允许开发者使用命名空间为自定义属性命名。
namespace MyAttributes {
[[nodiscard]] struct my_attr {};
}
[[MyAttributes::my_attr]] void myFunction() {
// Function with custom attribute
}
[[nodiscard]] 和 [[deprecated]] 提高了代码的健壮性。[[likely]] 和 [[unlikely]] 可帮助编译器生成更高效的分支预测代码。[[fallthrough]] 明确标注意图,避免误解。[[maybe_unused]] 在必要时避免警告。constexpr 是 C++11 引入的关键字,用于定义在编译时可以求值的表达式或函数。通过 constexpr,可以在编译时进行常量求值,优化性能并减少运行时计算开销。
constexpr 的主要用途constexpr 的规则constexpr 变量
使用 constexpr 修饰的变量必须具有常量值。
变量的初始化表达式必须在编译时可求值。
constexpr int compileTimeValue = 42; // 编译时确定
const int runTimeValue = 42; // 运行时常量,但不一定在编译时求值
constexpr int squared(int x) {
return x * x;
}
int main() {
constexpr int result = squared(4); // 编译时计算
return result; // 编译器直接用 16 替代 result
}
constexpr 函数
constexpr 函数可以在编译时或运行时使用。constexpr 函数)。注意:
constexpr int add(int a, int b) {
return a + b;
}
int main() {
constexpr int result1 = add(2, 3); // 编译时计算
int x = 5;
int result2 = add(x, 3); // 运行时计算,因为 x 是运行时变量
return result1 + result2;
}
constexpr 和对象
constexpr 也可以用于对象的构造函数,使得类对象在编译时创建。
class Point {
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int getX() const { return x_; }
constexpr int getY() const { return y_; }
private:
int x_, y_;
};
int main() {
constexpr Point p(3, 4); // 编译时构造对象
constexpr int x = p.getX(); // 编译时获取值
return 0;
}
constexpr 与 const 的区别| 特性 | const |
constexpr |
|---|---|---|
| 作用 | 定义常量 | 定义编译时常量或常量函数 |
| 编译时求值 | 不要求必须在编译时求值 | 要求初始化表达式在编译时求值 |
| 函数修饰 | 不适用于函数 | 适用于函数 |
| 类对象的支持 | 可定义 const 对象 |
可定义编译时类对象 |
在 C++14 中,constexpr 进一步增强,支持更复杂的操作,包括:
if、switch 和循环。constexpr 函数中声明为 mutable。constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
int main() {
constexpr int fact5 = factorial(5); // 编译时计算 5!
return fact5;
}
C++20 进一步扩展了 constexpr 的能力:
constexpr 函数中使用动态内存分配(new 和 delete)。constexpr 中使用标准库容器(如 std::vector 和 std::string)。#include <vector>
constexpr int sumElements(const std::vector<int>& vec) {
int sum = 0;
for (int val : vec) {
sum += val;
}
return sum;
}
int main() {
constexpr std::vector<int> numbers = {1, 2, 3, 4, 5};
constexpr int total = sumElements(numbers); // 编译时计算
return total;
}
允许一个构造函数调用同一个类中的另一个构造函数
语法示例:
class ClassName {
public:
ClassName(arguments) : ClassName(other_arguments) {
// Additional initialization, if any
}
};
示例:
#include <iostream>
#include <string>
class Person {
public:
// 主构造函数
Person(const std::string& name, int age) : name(name), age(age) {
std::cout << "Primary constructor called\n";
}
// 委托构造函数
Person(const std::string& name) : Person(name, 0) {
std::cout << "Delegating constructor called\n";
}
void display() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
private:
std::string name;
int age;
};
int main() {
Person p1("Alice", 30); // 调用主构造函数
p1.display();
Person p2("Bob"); // 调用委托构造函数
p2.display();
return 0;
}
注意事项
多重委托构造:
#include <iostream>
class Rectangle {
public:
// 主构造函数
Rectangle(int width, int height) : width(width), height(height) {
std::cout << "Rectangle(int, int) called\n";
}
// 默认构造函数,委托给 Rectangle(int, int)
Rectangle() : Rectangle(0, 0) {
std::cout << "Rectangle() called\n";
}
// 构造正方形,委托给 Rectangle(int, int)
Rectangle(int side) : Rectangle(side, side) {
std::cout << "Rectangle(int) called\n";
}
void display() const {
std::cout << "Width: " << width << ", Height: " << height << std::endl;
}
private:
int width, height;
};
int main() {
Rectangle r1; // 默认构造函数
Rectangle r2(5); // 构造正方形
Rectangle r3(4, 6); // 主构造函数
r1.display();
r2.display();
r3.display();
return 0;
}
允许开发者为数值、字符串等字面量定义自己的后缀,从而创建更具可读性和语义化的代码
用户自定义字面量以一个 _ 开头的后缀标识。可以为整数、浮点数、字符、字符串等字面量定义后缀。
Type operator "" _suffix(arguments);
suffix 是用户定义的后缀名称(必须以 _ 开头)。
参数类型由字面量的类型决定:
123):unsigned long long1.23):long double'c'):char"hello"):const char* 或 std::string为长度单位定义字面量
将数字字面量转换为单位化的类型:
#include <iostream>
constexpr long double operator "" _cm(long double value) {
return value; // 以厘米为单位
}
constexpr long double operator "" _m(long double value) {
return value * 100.0; // 转换为厘米
}
constexpr long double operator "" _km(long double value) {
return value * 100000.0; // 转换为厘米
}
int main() {
long double length = 1.0_km + 50.0_m + 25.0_cm;
std::cout << "Length in cm: " << length << " cm" << std::endl;
return 0;
}
自定义字符串字面量
将字符串字面量转换为 std::string:
#include <iostream>
#include <string>
std::string operator "" _s(const char* str, size_t) {
return std::string(str);
}
int main() {
std::string message = "Hello, world!"_s; // 使用自定义字符串字面量
std::cout << message << std::endl;
return 0;
}
时间单位转换
#include <iostream>
#include <chrono>
// 秒
constexpr std::chrono::seconds operator "" _sec(unsigned long long s) {
return std::chrono::seconds(s);
}
// 毫秒
constexpr std::chrono::milliseconds operator "" _ms(unsigned long long ms) {
return std::chrono::milliseconds(ms);
}
int main() {
auto duration = 10_sec + 500_ms; // 10秒500毫秒
std::cout << "Total milliseconds: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()
<< " ms" << std::endl;
return 0;
}
| 字面量类型 | 参数类型 | 示例 |
|---|---|---|
| 整数字面量 | unsigned long long |
123_ull |
| 浮点数字面量 | long double |
3.14_ld |
| 字符字面量 | char |
'c'_ch |
| 字符串字面量 | const char* 和 size_t |
"text"_str |
| 原始字符串字面量 | const char* 和 size_t |
R"text("_rawstr |
C++ 提供了一些内置的用户自定义字面量(C++14 开始),例如:
std::chrono 提供的 _h、_min、_s 等字面量。std::string_literals 提供的 _s。std::complex_literals 提供的 _i#include <iostream>
#include <chrono>
#include <string>
#include <complex>
using namespace std::chrono_literals; // 时间单位
using namespace std::string_literals; // 字符串
using namespace std::complex_literals; // 复数
int main() {
auto time = 10s; // 10 秒
auto str = "Hello!"s; // std::string
auto complex = 3.0 + 4.0i; // std::complex<double>
std::cout << "Time: " << time.count() << " seconds" << std::endl;
std::cout << "String: " << str << std::endl;
std::cout << "Complex: " << complex << std::endl;
return 0;
}
在派生类中显式地重写(override)基类的虚函数
#include <iostream>
class Base {
public:
virtual void showMessage() const {
std::cout << "Base message" << std::endl;
}
virtual ~Base() = default;
};
class Derived : public Base {
public:
void showMessage() const override { // 使用 override 明确覆盖基类的虚函数
std::cout << "Derived message" << std::endl;
}
};
int main() {
Base* obj = new Derived();
obj->showMessage(); // 输出: Derived message
delete obj;
return 0;
}
指定虚函数不能在派生类中被重写,或者类不能被继承
struct A {
virtual void foo();
};
struct B : A {
virtual void foo() final;
};
struct C : B {
virtual void foo(); // error -- declaration of 'foo' overrides a 'final' function
};
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
default 函数是用于显式声明类的默认行为的一种特性,通常与构造函数、析构函数和赋值运算符相关,使用 default 关键字可以让开发者控制哪些函数需要编译器自动生成的默认实现
默认函数:如果不显式定义,C++ 编译器会为类自动生成以下默认函数:
使用 = default 的目的:
#include <iostream>
class Example {
public:
Example() = default; // 显式声明默认构造函数
~Example() = default; // 显式声明默认析构函数
Example(const Example&) = default; // 默认复制构造函数
Example& operator=(const Example&) = default; // 默认复制赋值运算符
Example(Example&&) = default; // 默认移动构造函数
Example& operator=(Example&&) = default; // 默认移动赋值运算符
};
int main() {
Example e1; // 默认构造函数
Example e2 = e1; // 复制构造函数
Example e3 = std::move(e1); // 移动构造函数
e3 = e2; // 复制赋值运算符
e3 = std::move(e2); // 移动赋值运算符
return 0;
}
通过关键字 = delete 明确声明的函数,表示这些函数被删除,禁止调用。被标记为 = delete 的函数会让编译器在编译阶段检查并报告错误,从而防止意外使用
#include <iostream>
class NonCopyable {
public:
NonCopyable() = default; // 默认构造函数
~NonCopyable() = default; // 默认析构函数
NonCopyable(const NonCopyable&) = delete; // 禁用复制构造函数
NonCopyable& operator=(const NonCopyable&) = delete; // 禁用复制赋值运算符
};
int main() {
NonCopyable obj1;
// NonCopyable obj2 = obj1; // 错误:复制构造函数被删除
// obj1 = obj2; // 错误:复制赋值运算符被删除
return 0;
}
用于遍历容器(如数组、向量等)或范围
for (declaration : range) {
// 循环体
}
declaration:循环变量,表示容器中每个元素的类型。
range:要遍历的范围,可以是标准容器(如 std::vector)、数组、初始化列表或任何实现了 begin() 和 end() 的范围。
示例:
#include <iostream>
#include <vector>
int main() {
int arr[] = {1, 2, 3, 4, 5};
for (int num : arr) { // 遍历数组,值传递,会发生拷贝
std::cout << num << " ";
}
// 输出: 1 2 3 4 5
std::vector<std::string> fruits = {"apple", "banana", "cherry"};
for (const auto& fruit : fruits) { // 使用引用避免拷贝
std::cout << fruit << " ";
}
// 输出: apple banana cherry
return 0;
}
转换构造函数(Converting constructors)是一种特殊的构造函数,可以通过单个参数的隐式或显式转换,将其他类型的值转换为类类型的对象的构造函数。c++03中要求转换构造函数只有单个参数,或其它参数具有默认值,c++11中转换构造函数则可以拥有多个参数,可以使用列表初始化{}调用拥有多个参数的转换构造函数
示例:
#include <iostream>
using namespace std;
class A{
public:
A(int){cout << "A(int)" << endl;}
A(const char* s, int){cout << "A(const char* s, int)" << endl;}
A(int x, int y){cout << "A(int x, int y)" << endl;}
};
int main(){
A a1 = 1; //A(int)
A a2(2); //A(int)
A a3{4,5}; //A(int x, int y)
A a4 = {"ciallo",5}; //A(const char* s, int)
A a5 = (A)1; //A(int)
A a6(1,2); //A(int x, int y)
}
如果构造函数接受std::initializer_list, 则会调用它
struct A {
A(int) {}
A(int, int) {}
A(int, int, int) {}
A(std::initializer_list<int>) {}
};
A a {0, 0}; // calls A::A(std::initializer_list<int>)
A b(0, 0); // calls A::A(int, int)
A c = {0, 0}; // calls A::A(std::initializer_list<int>)
A d {0, 0, 0}; // calls A::A(std::initializer_list<int>)
如果对构造函数使用了explicit修饰符,则禁止对构造函数进行隐式转换
#include <iostream>
using namespace std;
class A{
public:
explicit A(int){cout << "A(int)" << endl;}
};
int main(){
A a1 = 1; //编译错误
}
显式转换函数是通过在类成员函数前添加 explicit 关键字定义的特殊类型转换函数。这些函数只会在显式调用时生效,而不会在隐式上下文中自动调用。
class ClassName {
public:
explicit operator TargetType() const {
// Conversion logic here
}
};
#include <iostream>
#include <string>
class Temperature {
private:
double celsius; // Temperature in Celsius
public:
explicit Temperature(double c) : celsius(c) {}
// Explicit conversion to double
explicit operator double() const {
return celsius;
}
// Explicit conversion to string
explicit operator std::string() const {
return std::to_string(celsius) + "°C";
}
// A method to print temperature
void print() const {
std::cout << "Temperature: " << celsius << "°C\n";
}
};
int main() {
Temperature temp(36.6);
// Error: implicit conversion not allowed due to explicit keyword
// double value = temp;
// Explicit conversion to double
double value = static_cast<double>(temp);
std::cout << "Temperature in double: " << value << "\n";
// Explicit conversion to string
std::string strValue = static_cast<std::string>(temp);
std::cout << "Temperature in string: " << strValue << "\n";
return 0;
}
struct A {
operator bool() const { return true; }
};
struct B {
explicit operator bool() const { return true; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
bool bbb = static_cast<bool> b; //OK calls B::operator bool()
内联命名空间用于库的版本控制和名称管理。一个 inline 命名空间中的所有成员可以被视为声明在外围命名空间中
namespace Program {
namespace Version1 {
int getVersion() { return 1; }
bool isFirstVersion() { return true; }
}
inline namespace Version2 {
int getVersion() { return 2; }
}
}
int version {Program::getVersion()}; // Uses getVersion() from Version2
int oldVersion {Program::Version1::getVersion()}; // Uses getVersion() from Version1
bool firstVersion {Program::isFirstVersion()}; // Does not compile when Version2 is added
Non-static data member initializers
允许在声明非静态数据成员的地方对其进行初始化,这可能会简化默认初始化的构造函数
// C++11之前的默认初始化
class Human {
Human() : age{0} {}
private:
unsigned age;
};
// C++11的默认初始化
class Human {
private:
unsigned age {0};//括号初始化
int x = 10; //直接初始化
};
[!NOTE]
不能用于动态内存分配,但可以用来初始化指针为 nullptr
引用限定成员函数(Ref-qualified member functions)允许为类的成员函数指定调用的对象类型约束,即只能通过特定类型的对象(左值、右值)调用。这是通过在函数签名后使用 & 或&& 限定符来实现的
class ClassName {
public:
void func() &; // 只能通过左值调用
void func() &&; // 只能通过右值调用
};
& 限定符:表示该成员函数只能通过左值对象调用。&& 限定符:表示该成员函数只能通过右值对象调用。#include <utility>
struct Bar {
// ...
};
struct Foo {
Bar& getBar() & { return bar; }
const Bar& getBar() const& { return bar; }
Bar&& getBar() && { return std::move(bar); }
const Bar&& getBar() const&& { return std::move(bar); }
private:
Bar bar;
};
int main(){
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move(foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move(foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
}
允许在函数定义中将返回类型写在参数列表之后,而不是函数名之前。这种语法主要用于以下场景:
auto function_name(parameters) -> return_type;
示例
#include <iostream>
#include <type_traits>
#include <vector>
// 简单返回类型
auto add(int a, int b) -> int {
return a + b;
}
// 返回类型依赖模板参数
template <typename T, typename U>
auto multiply(T a, U b) -> decltype(a * b) {
return a * b;
}
// 返回复杂类型
auto get_vector() -> std::vector<int> {
return {1, 2, 3};
}
int main() {
std::cout << add(3, 4) << "\n"; // 输出: 7
std::cout << multiply(3, 4.5) << "\n"; // 输出: 13.5
auto vec = get_vector();
for (auto x : vec) {
std::cout << x << " "; // 输出: 1 2 3
}
std::cout << "\n";
return 0;
}
noexcept 指定符指定一个函数是否可能抛出异常。它是 throw() 的改进版本。
void func1() noexcept; // does not throw
void func2() noexcept(true); // does not throw
void func3() throw(); // does not throw
void func4() noexcept(false); // may throw
[!CAUTION]
声明为
noexcept的函数中调用可能抛出异常的代码是危险的void unsafe() noexcept { throw std::runtime_error("Error!"); // 导致std::terminate被调用 }
C++提供了noexcept操作符,用于检查一个表达式是否为noexcept
#include <iostream>
void safeFunction() noexcept {}
void unsafeFunction() {
throw std::runtime_error("Unsafe function");
}
int main() {
std::cout << std::boolalpha;
std::cout << "safeFunction is noexcept: " << noexcept(safeFunction()) << "\n";
std::cout << "unsafeFunction is noexcept: " << noexcept(unsafeFunction()) << "\n";
return 0;
}
可以以原始形式输入转义字符
// msg1 and msg2 are equivalent.
const char* msg1 = "\nHello,\n\tworld!\n";
const char* msg2 = R"(
Hello,
world!
)";
std::move 是 C++ 标准库中的一个函数模板,定义在头文件 <utility> 中。它的作用是将一个对象显式地 转换为右值引用,以触发对象的移动语义(而不是拷贝语义)
std::move 的定义:
template <typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast<typename remove_reference<T>::type&&>(arg);
}
应用场景:
std::move。std::move 可以使容器在插入/移动对象时避免多余的拷贝操作。std::move 可以将其转为右值,允许资源转移示例:
#include <cstring>
#include <iostream>
#include <utility> // for std::move
#include <string>
class MyString {
char* data;
size_t length;
public:
// 构造函数
MyString(const char* str) : length(strlen(str)), data(new char[length + 1]) {
std::strcpy(data, str);
std::cout << "Constructed: " << data << std::endl;
}
// 移动构造函数
MyString(MyString&& other) noexcept : data(other.data), length(other.length) {
other.data = nullptr;
other.length = 0;
std::cout << "Moved!" << std::endl;
}
// 析构函数
~MyString() {
if (data) {
std::cout << "Destroyed: " << data << std::endl;
delete[] data;
}
}
void print() const {
if (data) std::cout << data << std::endl;
else std::cout << "Empty!" << std::endl;
}
};
int main() {
MyString s1("Hello, World!"); // 正常构造
MyString s2(std::move(s1)); // 使用 std::move 触发移动构造
s1.print(); // s1 已被移动,输出 "Empty!"
s2.print(); // s2 持有原来的资源,输出 "Hello, World!"
return 0;
}
#include <iostream>
#include <vector>
#include <string>
int main() {
std::vector<std::string> vec;
std::string str = "Hello, World!";
std::cout << "Pushing back copy:" << std::endl;
vec.push_back(str); // 使用拷贝构造,str 内容未转移
std::cout << "Pushing back move:" << std::endl;
vec.push_back(std::move(str)); // 使用移动构造,str 内容被转移
std::cout << "Vector contents: " << vec[0] << ", " << vec[1] << std::endl;
std::cout << "Original string: " << str << std::endl; // str 可能为空或未定义状态
return 0;
}
#include <iostream>
#include <utility>
void printInt(int& n) {
std::cout << "Lvalue: " << n << std::endl;
}
void printInt(int&& n) {
std::cout << "Rvalue: " << n << std::endl;
}
int main() {
int x = 10;
printInt(x); // 调用左值版本
printInt(std::move(x)); // 调用右值版本
printInt(20); // 调用右值版本
return 0;
}
std::unique_str的使用#include <iostream>
#include <memory> // for std::unique_ptr and std::move
#include <vector>
void processUniquePtr(std::unique_ptr<int> p) {
std::cout << "Processing value: " << *p << std::endl;
}
int main() {
// 创建一个 std::unique_ptr,管理动态分配的 int
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
std::cout << "ptr1 owns: " << *ptr1 << std::endl;
// 将所有权转移到 ptr2
std::unique_ptr<int> ptr2 = std::move(ptr1);
// ptr1 现在为空,ptr2 拥有资源
if (!ptr1) {
std::cout << "ptr1 is now null." << std::endl;
}
std::cout << "ptr2 owns: " << *ptr2 << std::endl;
//传递 std::unique_ptr 到函数
std::unique_ptr<int> ptr = std::make_unique<int>(100);
// 使用 std::move 将所有权转移到函数参数
processUniquePtr(std::move(ptr));
if (!ptr) {
std::cout << "ptr is now null after move." << std::endl;
}
//将 std::unique_ptr 放入容器
std::vector<std::unique_ptr<int>> vec;
// 创建两个 unique_ptr
std::unique_ptr<int> ptr3 = std::make_unique<int>(10);
std::unique_ptr<int> ptr4 = std::make_unique<int>(20);
// 使用 std::move 将所有权转移到 vector 中
vec.push_back(std::move(ptr3));
vec.push_back(std::move(ptr4));
// ptr1 和 ptr2 现在为空
if (!ptr3 && !ptr4) {
std::cout << "ptr3 and ptr4 are now null." << std::endl;
}
// 访问 vector 中的元素
for (const auto& ptr : vec) {
std::cout << "Vector element: " << *ptr << std::endl;
}
return 0;
}
std::forward主要作用是:根据传入参数的值类别(左值或右值)保持其原始类别,从而正确地传递给另一个函数(例如,左值保持为左值,临时对象作为右值转发)
与Forwarding references一起使用,实现完美转发
定义:
template <typename T>
T&& forward(typename remove_reference<T>::type& arg) {
return static_cast<T&&>(arg);
}
T&& 的返回类型:
T 是左值引用,则返回左值引用。T 是右值,则返回右值引用。示例:
#include <iostream>
struct A {
A() = default;
A(const A& o) { std::cout << "copied" << std::endl; }
A(A&& o) { std::cout << "moved" << std::endl; }
};
template <typename T>
A wrapper(T&& arg) {
return A{std::forward<T>(arg)};
}
int main(){
wrapper(A{}); // moved
A a;
wrapper(a); // copied
wrapper(std::move(a)); // moved
}
T&& 会解析为 T& &(根据引用折叠规则,最终变为 T&)。T&& 会解析为 T&&。[!note]
当需要将参数arg转发给另一个函数时,直接使用arg可能导致值类别被错误地修改。例如,右值可能被误认为左值传递,导致调用拷贝构造函数而非移动构造函数
若使用return A{arg},即使arg作为万能引用正确传递了引用类型,右值引用仍然会被作为左值使用,将会调用拷贝构造函数,输出三次copied
注意事项
std::forward 只能用于函数模板中的转发操作,因为它依赖于模板参数 T 的推导std::forward 可能导致意外行为std::forward,普通函数的参数值类别是确定的,没有必要通过 std::forward 转发std::forward 与 std::move 的区别| 特性 | std::forward |
std::move |
|---|---|---|
| 用途 | 保留参数的值类别,用于完美转发。 | 将左值显式地转换为右值引用。 |
| 典型场景 | 用于模板参数和万能引用。 | 用于移动语义和资源转移。 |
| 是否保留值类别 | 是,参数的值类别由模板参数决定。 | 否,强制转换为右值引用。 |
| 返回类型 | 与模板参数 T 的值类别一致。 |
始终返回右值引用。 |
std::thread 库提供了一种标准方式来控制线程,例如生成和终止线程
void foo(bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true); // thread will run foo(true)
for (auto& thread : threadsVector) {
thread.join(); // Wait for threads to finish
}
将数值参数转换为 std::string
std::to_string(1.2); // == "1.2"
std::to_string(123); // == "123"
类型特征(Type traits)用于在编译时检查、修改或操作类型的属性和特性。
std::enable_if 和其他模板机制结合,构建更强大的模板代码。Type Traits 通常定义在 <type_traits> 头文件中。以下是一些常用的 Type Traits。
| Type Trait | 功能描述 |
|---|---|
std::is_integral<T> |
判断类型 T 是否为整型 |
std::is_floating_point<T> |
判断类型 T 是否为浮点型 |
std::is_pointer<T> |
判断类型 T 是否为指针类型 |
std::is_reference<T> |
判断类型 T 是否为引用类型 |
std::is_const<T> |
判断类型 T 是否为常量类型 |
std::is_same<T, U> |
判断类型 T 和 U 是否相同 |
std::is_array<T> |
判断类型 T 是否为数组类型 |
std::is_class<T> |
判断类型 T 是否为类类型 |
std::is_function<T> |
判断类型 T 是否为函数类型 |
#include <iostream>
#include <type_traits>
template <typename T>
void checkTypeTraits() {
if (std::is_integral<T>::value) {
std::cout << "T is an integral type.\n";
}
if (std::is_pointer<T>::value) {
std::cout << "T is a pointer type.\n";
}
if (std::is_const<T>::value) {
std::cout << "T is a const type.\n";
}
}
int main() {
checkTypeTraits<int>(); // 输出: T is an integral type.
checkTypeTraits<const int>(); // 输出: T is an integral type. T is a const type.
checkTypeTraits<int*>(); // 输出: T is a pointer type.
return 0;
}
| Type Trait | 功能描述 |
|---|---|
std::remove_const<T> |
移除类型 T 的 const 修饰符 |
std::remove_pointer<T> |
移除类型 T 的指针修饰符 |
std::remove_reference<T> |
移除类型 T 的引用修饰符 |
std::add_const<T> |
为类型 T 添加 const 修饰符 |
std::add_pointer<T> |
为类型 T 添加指针修饰符 |
std::add_reference<T> |
为类型 T 添加引用修饰符 |
std::decay<T> |
移除数组和函数类型中的修饰符 |
#include <iostream>
#include <type_traits>
template <typename T>
void modifyTypeTraits() {
using NonConstType = typename std::remove_const<T>::type;
using PointerType = typename std::add_pointer<T>::type;
using ReferenceType = typename std::add_lvalue_reference<T>::type;
std::cout << "Original type: " << typeid(T).name() << "\n";
std::cout << "Non-const type: " << typeid(NonConstType).name() << "\n";
std::cout << "Pointer type: " << typeid(PointerType).name() << "\n";
std::cout << "Reference type: " << typeid(ReferenceType).name() << "\n";
}
int main() {
modifyTypeTraits<const int>(); // 演示类型修改
return 0;
}
| Type Trait | 功能描述 |
|---|---|
std::conditional<Cond, T1, T2> |
如果 Cond 为 true,则类型为 T1,否则为 T2 |
std::enable_if<Cond, T> |
如果 Cond 为 true,启用模板 |
std::is_base_of<Base, Derived> |
判断 Base 是否为 Derived 的基类 |
std::integral_constant<T, v> |
编译时保存常量值 |
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
print(T value) {
std::cout << "Integral value: " << value << "\n";
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
print(T value) {
std::cout << "Floating-point value: " << value << "\n";
}
int main() {
print(42); // 输出: Integral value: 42
print(3.14); // 输出: Floating-point value: 3.14
// print("Hello"); // 编译错误,不符合任何模板
return 0;
}
封装了原始指针的类,提供自动化的内存管理功能,避免了手动管理内存可能引发的内存泄漏、悬垂指针等问题。智能指针通过RAII(资源获取即初始化)模式,确保在超出作用域时,资源会被安全释放
C++标准库提供了三种主要的智能指针,定义在头文件 <memory> 中
独占所有权的智能指针,一个对象只能被一个unique_ptr管理。
适合表示独占资源,不能复制
轻量级,适合高性能场景
通过std::move可以将所有权转移
自动释放资源,无需手动调用delete
#include <iostream>
#include <memory>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
int main() {
std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
// std::unique_ptr<Resource> ptr2 = ptr1; // 编译错误,不能复制
std::unique_ptr<Resource> ptr2 = std::move(ptr1); // 转移所有权
if (!ptr1) {
std::cout << "ptr1 is now empty\n";
}
//另一种初始化方式
std::unique_ptr<Resource> p1 { new Resource{} };
std::unique_ptr<Resource> p2 {std::move(p1)};
return 0;
}
共享所有权的智能指针,多个shared_ptr可以管理同一个对象,使用引用计数来管理资源生命周期。
使用引用计数,只有当引用计数为零时才会释放资源
适合表示共享资源。
支持赋值和拷贝
使用std::make_shared创建,避免多次分配内存
自动释放资源
#include <iostream>
#include <memory>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
int main() {
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
std::shared_ptr<Resource> ptr2 = ptr1; // 引用计数增加
std::cout << "Reference count: " << ptr1.use_count() << "\n";
ptr1.reset(); // 减少一个引用
std::cout << "Reference count after reset: " << ptr2.use_count() << "\n";
return 0; // ptr2超出作用域,资源自动释放
}
辅助shared_ptr使用的弱引用,不影响共享资源的生命周期。
不增加引用计数,只是对shared_ptr的弱引用
解决了shared_ptr的循环引用问题
需要通过lock获取一个有效的shared_ptr
可以判断指向的资源是否还存在
#include <iostream>
#include <memory>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
int main() {
std::shared_ptr<Resource> shared = std::make_shared<Resource>();
std::weak_ptr<Resource> weak = shared; // 创建弱引用
if (auto locked = weak.lock()) {
std::cout << "Resource is still alive\n";
}
shared.reset(); // 释放资源
if (weak.expired()) {
std::cout << "Resource has been released\n";
}
return 0;
}
std::shared_ptr之间的循环引用会导致资源无法释放。std::weak_ptr打破循环引用。std::unique_ptr。std::shared_ptr的引用计数管理有一定的性能开销。chrono 库包含一组处理持续时间、时钟和时间点的实用函数和类型。
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
元组(Tuples) 是 C++ 标准库提供的一种容器,定义在 <tuple> 头文件中,用于存储一组异构类型的数据。元组可以看作是扩展版的 std::pair,支持多个元素,且每个元素的类型可以不同
#include <tuple>
#include <iostream>
#include <string>
int main() {
// 创建元组
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
// 通过 std::make_tuple 创建
auto anotherTuple = std::make_tuple(1, 2.718, "World");
return 0;
}
可以使用 std::get<index>(tuple) 按索引访问元素。
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
// 按索引访问元素
std::cout << "First element: " << std::get<0>(myTuple) << "\n";
std::cout << "Second element: " << std::get<1>(myTuple) << "\n";
std::cout << "Third element: " << std::get<2>(myTuple) << "\n";
return 0;
}
使用 C++17 的结构化绑定直接解构元组
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
// 解构元组
auto [intValue, doubleValue, stringValue] = myTuple;
std::cout << "Integer: " << intValue << "\n";
std::cout << "Double: " << doubleValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
元组的元素可以通过 std::get<index> 修改
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
// 修改元素
std::get<0>(myTuple) = 100;
std::get<2>(myTuple) = "World";
std::cout << "Updated tuple: ("
<< std::get<0>(myTuple) << ", "
<< std::get<1>(myTuple) << ", "
<< std::get<2>(myTuple) << ")\n";
return 0;
}
使用 std::tuple_size
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
constexpr size_t tupleSize = std::tuple_size<decltype(myTuple)>::value;
std::cout << "Size of tuple: " << tupleSize << "\n";
return 0;
}
使用 std::tuple_cat 将多个元组拼接
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, std::string> tuple1(42, "Hello");
std::tuple<double, char> tuple2(3.14, 'A');
auto combinedTuple = std::tuple_cat(tuple1, tuple2);
std::cout << "Combined tuple: ("
<< std::get<0>(combinedTuple) << ", "
<< std::get<1>(combinedTuple) << ", "
<< std::get<2>(combinedTuple) << ", "
<< std::get<3>(combinedTuple) << ")\n";
return 0;
}
元组支持比较操作符(==, !=, <, <=, >, >=)
#include <tuple>
#include <iostream>
int main() {
std::tuple<int, std::string> tuple1(42, "Hello");
std::tuple<int, std::string> tuple2(42, "World");
if (tuple1 < tuple2) {
std::cout << "tuple1 is less than tuple2\n";
} else {
std::cout << "tuple1 is not less than tuple2\n";
}
return 0;
}
函数返回多个值
#include <tuple>
#include <iostream>
std::tuple<int, double, std::string> getValues() {
return std::make_tuple(42, 3.14, "Hello");
}
int main() {
auto [intValue, doubleValue, stringValue] = getValues();
std::cout << "Integer: " << intValue << "\n";
std::cout << "Double: " << doubleValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
std::tie 是 C++ 标准库中的一个实用工具,定义在头文件 <tuple> 中。它主要用于将元组的元素绑定到多个变量上,从而实现“解构”操作
解构元组
使用 std::tie 将元组中的元素绑定到多个变量。
#include <iostream>
#include <tuple>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
int intValue;
double doubleValue;
std::string stringValue;
// 使用 std::tie 解构元组
std::tie(intValue, doubleValue, stringValue) = myTuple;
std::cout << "Integer: " << intValue << "\n";
std::cout << "Double: " << doubleValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
忽略部分元素
使用 std::ignore 忽略不需要的元组元素。
#include <iostream>
#include <tuple>
int main() {
std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello");
int intValue;
std::string stringValue;
// 忽略中间的 double 元素
std::tie(intValue, std::ignore, stringValue) = myTuple;
std::cout << "Integer: " << intValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
与函数返回值结合
常用于从返回元组的函数中提取多个值,使用 std::tie 提取其中的值
#include <iostream>
#include <tuple>
// 返回多个值的函数
std::tuple<int, double, std::string> getValues() {
return std::make_tuple(42, 3.14, "Hello");
}
int main() {
int intValue;
double doubleValue;
std::string stringValue;
// 使用 std::tie 解构函数返回的元组
std::tie(intValue, doubleValue, stringValue) = getValues();
std::cout << "Integer: " << intValue << "\n";
std::cout << "Double: " << doubleValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
变量的引用关系:
std::tie 实际上绑定了变量的引用。如果对绑定的变量进行修改,会影响元组中的内容(前提是元组也是引用类型)
#include <iostream>
#include <tuple>
int main() {
int a = 42, b = 100;
std::tuple<int&, int&> myTuple = std::tie(a, b);
std::get<0>(myTuple) = 200; // 修改元组中的值
std::cout << "a: " << a << "\n"; // 输出 200
return 0;
}
不能用于非可修改的类型
如果需要解构const元组,std::tie无法使用,需要考虑使用结构化绑定(C++17)
推荐在需要引用的场景中使用
std::tie 更适合那些希望通过引用访问元组元素的情况。如果不需要引用,直接使用结构化绑定更简洁
结构化绑定是一种更现代、更直观的方式,用于解构元组,避免了对 std::tie 和 std::get 的依赖
#include <iostream>
#include <tuple>
int main() {
auto myTuple = std::make_tuple(42, 3.14, "Hello");
// 使用结构化绑定(推荐)
auto [intValue, doubleValue, stringValue] = myTuple;
std::cout << "Integer: " << intValue << "\n";
std::cout << "Double: " << doubleValue << "\n";
std::cout << "String: " << stringValue << "\n";
return 0;
}
std::array 是一个基于 C 风格数组构建的容器,用来表示一个固定大小的数组
std::array<Type, Size> name;
Type 是数组元素的类型。Size 是数组的大小,必须是一个常量表达式。at(index):安全地访问指定位置的元素,带越界检查。operator[]:访问元素,不做越界检查。front() / back():返回第一个或最后一个元素。size():返回数组的大小。fill(value):将数组中所有元素设置为 value。data():返回底层数组的指针。#include <iostream>
#include <array>
int main() {
// 定义一个大小为5的std::array
std::array<int, 5> myArray = {1, 2, 3, 4, 5};
// 使用[]访问元素
std::cout << "使用[]访问元素:" << std::endl;
for (size_t i = 0; i < myArray.size(); ++i) {
std::cout << myArray[i] << " ";
}
std::cout << std::endl;
// 使用at()访问元素
std::cout << "使用at()访问元素:" << std::endl;
for (size_t i = 0; i < myArray.size(); ++i) {
std::cout << myArray.at(i) << " ";
}
std::cout << std::endl;
// 修改元素
myArray[0] = 10;
myArray.at(1) = 20;
std::cout << "修改后数组内容: ";
for (const auto& value : myArray) {
std::cout << value << " ";
}
std::cout << std::endl;
// 使用front()和back()
std::cout << "第一个元素: " << myArray.front() << std::endl;
std::cout << "最后一个元素: " << myArray.back() << std::endl;
// 使用fill()重置元素
myArray.fill(42);
std::cout << "使用fill()后的数组内容: ";
for (const auto& value : myArray) {
std::cout << value << " ";
}
std::cout << std::endl;
// 获取底层指针
int* ptr = myArray.data();
std::cout << "底层数组地址: " << ptr << std::endl;
return 0;
}
| 功能 | C风格数组 | std::array |
|---|---|---|
| 类型安全性 | 较低 | 较高 |
| 越界检查 | 无 | at()有越界检查 |
| STL兼容性 | 需要手动适配 | 天生兼容 |
| 拷贝赋值操作 | 需手动实现 | 自动支持 |
| 是否动态分配内存 | 否 | 否 |
无序容器(unordered containers),基于哈希表(hash table)实现,用于存储无序的键值对或集合。
key-value)。std::map,但无序。std::unordered_map。std::set,但无序。std::unordered_set。std::hash。std::unordered_map 示例#include <iostream>
#include <unordered_map>
#include <string>
int main() {
// 创建unordered_map
std::unordered_map<std::string, int> umap;
// 插入元素
umap["Alice"] = 25;
umap["Bob"] = 30;
umap["Charlie"] = 35;
// 遍历unordered_map
for (const auto& pair : umap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// 查找元素
auto it = umap.find("Alice");
if (it != umap.end()) {
std::cout << "Found Alice, age: " << it->second << std::endl;
}
return 0;
}
std::unordered_set 示例#include <iostream>
#include <unordered_set>
int main() {
// 创建unordered_set
std::unordered_set<int> uset = {1, 2, 3, 4, 5};
// 插入元素
uset.insert(6);
// 查找元素
if (uset.find(3) != uset.end()) {
std::cout << "Found 3 in the set." << std::endl;
}
// 遍历unordered_set
for (int num : uset) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
| 函数 | 作用 |
|---|---|
insert() |
插入元素 |
erase() |
删除元素 |
find() |
查找元素,返回迭代器 |
clear() |
清空容器 |
size() |
返回容器中的元素个数 |
empty() |
判断容器是否为空 |
| 函数 | 作用 |
|---|---|
bucket_count() |
返回桶的数量 |
bucket_size(bucket) |
返回指定桶中元素的数量 |
load_factor() |
返回负载因子(size / bucket_count) |
rehash(n) |
重置桶数量,至少为 n |
reserve(n) |
重新分配空间,确保能容纳至少 n 个元素 |
| 特性 | 无序容器 (unordered_*) |
有序容器 (map, set) |
|---|---|---|
| 存储顺序 | 无序 | 按键值顺序 |
| 时间复杂度(查找) | 平均 O(1) | O(log N) |
| 底层结构 | 哈希表 | 红黑树 |
| 支持排序操作 | 不支持 | 支持 |
推荐使用 std::make_shared 来创建 std::shared_ptr 实例
用于创建对对象的引用包装器(reference wrapper),提供一种安全的方式将引用存储在容器或函数对象中。它返回一个 std::reference_wrapper 类型的对象
std::ref 的主要功能是为引用类型创建一个可拷贝的包装器。因为引用本身不是对象,不能直接存储在 STL 容器中或传递给某些函数对象,而 std::reference_wrapper 提供了这种能力。
std::ref:用于包装左值引用。std::cref:用于包装左值的 const 引用。#include <functional>
std::reference_wrapper<T> std::ref(T& t);
std::reference_wrapper<const T> std::cref(const T& t);
T& t 是需要包装的左值引用。
返回值是一个 std::reference_wrapper<T> 对象。
std::ref 可以用来间接存储引用。std::ref。在容器中存储引用
#include <iostream>
#include <vector>
#include <functional> // std::ref
int main() {
int a = 10, b = 20, c = 30;
// 用std::ref将引用存入容器
std::vector<std::reference_wrapper<int>> refs = {std::ref(a), std::ref(b), std::ref(c)};
// 修改容器中的值会影响原变量
for (auto& ref : refs) {
ref.get() += 1; // 使用.get()访问引用
}
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
return 0;
}
结合 std::bind 使用
std::ref 可以用来绑定引用以避免拷贝
#include <iostream>
#include <functional> // std::bind, std::ref
void print_and_increment(int& x) {
std::cout << "x: " << x << std::endl;
x++;
}
int main() {
int value = 42;
// 使用std::bind绑定引用
auto bound_func = std::bind(print_and_increment, std::ref(value));
bound_func(); // 调用时不会拷贝value
std::cout << "Value after increment: " << value << std::endl;
return 0;
}
传递引用给线程
在多线程编程中,std::ref 常用于向线程传递引用。
#include <iostream>
#include <thread>
#include <functional>
void increment(int& x) {
x++;
}
int main() {
int value = 0;
// 使用std::ref传递引用给线程
std::thread t(increment, std::ref(value));
t.join();
std::cout << "Value after thread increment: " << value << std::endl;
return 0;
}
std::ref 的返回值是 std::reference_wrapper 类型。std::reference_wrapper 提供了一些关键功能:
get():访问包装的引用。#include <iostream>
#include <functional>
int main() {
int x = 100;
std::reference_wrapper<int> ref = std::ref(x);
// 修改原变量
ref.get() = 200;
std::cout << "x: " << x << std::endl; // 输出: x: 200
return 0;
}
std::ref 仅支持左值引用:
如果传递的是临时对象(右值),编译会失败。
int x = 10;
std::ref(x); // 正确
std::ref(10); // 错误,10 是右值
需要调用 get() 明确访问引用:
在某些场景下(如容器操作)需要使用 .get() 来显式访问包装的引用。
避免悬空引用:
如果引用的原始对象已经销毁,std::ref 将指向无效内存,导致未定义行为。
内存模型(Memory Model) 定义了程序在多线程环境下如何共享和访问内存。它决定了不同线程间对变量的操作是否能够安全且一致地被观察到,并提供了相应的规则和工具来保证线程安全
std::mutex)。std::atomic 类型或合适的内存序(Memory Order)。C++ 内存模型定义了 内存序(Memory Order),它描述了线程之间对内存操作的顺序约束。主要包括:
std::memory_order_relaxed
memory_order_consume
std::memory_order_acquire
std::memory_order_release
std::memory_order_acq_rel
acquire 和 release 的语义。std::memory_order_seq_cst(默认)
std::atomic 类型来确保对共享变量的操作是线程安全的。#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> counter(0);
void increment() {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final counter value: " << counter << std::endl;
return 0;
}
C++ 提供了多种工具来实现线程同步:
std::mutex:用于保护临界区。
std::condition_variable:实现线程间的等待和通知。
内存屏障(Memory Fence)
使用 std::atomic_thread_fence 提供显式的内存屏障。
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<bool> ready(false);
int data = 0;
void producer() {
data = 42; // 写操作
std::atomic_thread_fence(std::memory_order_release);
ready.store(true, std::memory_order_relaxed);
}
void consumer() {
while (!ready.load(std::memory_order_relaxed)); // 自旋等待
std::atomic_thread_fence(std::memory_order_acquire);
std::cout << "Data: " << data << std::endl; // 读取操作
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
在多线程环境中,直接使用裸指针进行共享数据访问可能导致数据竞争。可以通过以下方法改善:
std::shared_ptr)。std::atomic<T*>)。用于启动异步任务并返回一个 std::future 对象,这个对象可以用于获取任务的结果。std::async 使得任务能够在后台并发执行,而主线程或其他线程可以继续执行其他工作
#include <iostream>
#include <future>
#include <thread>
int find_square(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return x * x;
}
int main() {
// 启动异步任务,计算 10 的平方
std::future<int> result = std::async(std::launch::async, find_square, 10);
// 可以在这里做其他工作
std::cout << "Doing other work while waiting for the result...\n";
// 获取异步任务的结果
std::cout << "The square of 10 is: " << result.get() << std::endl;
return 0;
}
#include <iostream>
#include <future>
#include <thread>
int find_square(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return x * x;
}
int main() {
// 启动延迟执行的任务
std::future<int> result = std::async(std::launch::deferred, find_square, 10);
// 可以在这里做其他工作
std::cout << "Doing other work while waiting for the result...\n";
// 获取异步任务的结果,只有此时任务才会执行
std::cout << "The square of 10 is: " << result.get() << std::endl;
return 0;
}
std::launch::deferred 时,异步任务并不会立即启动,直到你调用 result.get() 时,任务才会在当前线程中执行std::async 在这种模式下不会启动新的线程,而是将任务推迟到 get() 调用时执行std::function#include <functional>
std::function<返回类型(参数类型...)> func;
std::function 封装了可调用对象,确保在调用时参数和返回值符合定义的签名。std::function 内部使用了动态分配,可能引入额外的性能开销。std::function 对象是空的,调用时会抛出 std::bad_function_call 异常。存储普通函数
#include <iostream>
#include <functional>
void printMessage(const std::string& message) {
std::cout << message << std::endl;
}
int main() {
std::function<void(const std::string&)> func = printMessage;
func("Hello, std::function!"); // 调用存储的普通函数
return 0;
}
存储Lambdai表达式
#include <iostream>
#include <functional>
int main() {
std::function<int(int, int)> add = [](int a, int b) {
return a + b;
};
std::cout << "Sum: " << add(3, 5) << std::endl; // 输出:Sum: 8
return 0;
}
存储函数对象
#include <iostream>
#include <functional>
struct Multiplier {
int factor;
Multiplier(int f) : factor(f) {}
int operator()(int value) const {
return value * factor;
}
};
int main() {
std::function<int(int)> multiplyBy2 = Multiplier(2);
std::cout << multiplyBy2(10) << std::endl; // 输出:20
return 0;
}
存储成员函数
#include <iostream>
#include <functional>
class MyClass {
public:
void printMessage(const std::string& msg) const {
std::cout << "MyClass: " << msg << std::endl;
}
};
int main() {
MyClass obj;
std::function<void(const MyClass&, const std::string&)> func = &MyClass::printMessage;
func(obj, "Hello from member function!");
return 0;
}
动态切换可调用对象
#include <iostream>
#include <functional>
void func1() {
std::cout << "Function 1" << std::endl;
}
void func2() {
std::cout << "Function 2" << std::endl;
}
int main() {
std::function<void()> func;
func = func1;
func(); // 输出:Function 1
func = func2;
func(); // 输出:Function 2
return 0;
}
std::bind和std::placeholderstd::bind用于创建函数对象。它可以将一个函数(或可调用对象)的参数绑定为特定值或占位符,以生成一个新的可调用对象。
功能
#include <functional>
std::bind(Function, arg1, arg2, ..., argN)
Function:目标函数或可调用对象。
arg1, arg2, ..., argN:
std::placeholders::_1等形式表示,调用时动态传入。占位符格式为:std::placeholders::_N,N从1开始。
绑定普通函数的参数
#include <iostream>
#include <functional> // std::bind
using namespace std;
void greet(const string& name, int age) {
cout << "Hello, " << name << "! You are " << age << " years old." << endl;
}
int main() {
// 绑定 name 参数为 "Alice"
auto greetAlice = std::bind(greet, "Alice", std::placeholders::_1);
// 调用时只需提供 age 参数
greetAlice(25); // 输出:Hello, Alice! You are 25 years old.
return 0;
}
改变参数顺序
#include <iostream>
#include <functional>
using namespace std;
void subtract(int a, int b) {
cout << "Result: " << a - b << endl;
}
int main() {
// 交换参数的顺序
auto reversedSubtract = std::bind(subtract, std::placeholders::_2, std::placeholders::_1);
reversedSubtract(10, 20); // 输出:Result: 10
return 0;
}
绑定成员函数
#include <iostream>
#include <functional>
using namespace std;
class Calculator {
public:
void add(int a, int b) const {
cout << "Sum: " << a + b << endl;
}
};
int main() {
Calculator calc;
// 绑定成员函数,传入对象实例
auto addFunc = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2);
addFunc(10, 20); // 输出:Sum: 30
return 0;
}
结合std::bind和std::function
#include <iostream>
#include <functional>
using namespace std;
int multiply(int a, int b) {
return a * b;
}
int main() {
// 使用 std::bind
auto doubleValue = std::bind(multiply, std::placeholders::_1, 2);
// 存储到 std::function
std::function<int(int)> func = doubleValue;
cout << "5 * 2 = " << func(5) << endl; // 输出:5 * 2 = 10
return 0;
}
[!note]
Lambda可以替代std::bind
auto greetAlice = [](int age) { greet("Alice", age); };
上一篇:如何写出算法题?-算法思维
下一篇:指针与数组