本文主要是介绍C++总结2,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
标准库
字符串
- string 其实并不是一个“真正的类型”,而是模板类 basic_string 的特化形式:
- using string = std::basic_string<char>; // string其实是一个类型别名
- basic_string用来支持不同字符类型。
- 字符编码和国际化的问题实在是太复杂了, C++ 一直没有提供处理编码的配套工具。
- string 是一个功能比较齐全的字符串类,提供了字符操作的所有方法;也提供了类似容器的操作。
- 但我们还是应该将string作为字符串类来看待。
后缀"s"
using namespace std::literals::string_literals; //必须打开名字空间
auto str = "std string"s; // 后缀s,表示是标准字符串,直接类型推导
原始字符串-不用考虑转义
auto str = R"(nier:automata)"; // 原始字符串:nier:automata
字符串转换
stoi()、stol()、stoll() 等把字符串转换成整数。
stof()、stod() 等把字符串转换成浮点数。
to_string() 把整数、浮点数转换成字符串。
字符串视图类
- C++17有string_view(字符串的视图),内部只保存一个指针和长度,成本很低。
正则-文本处理
regex:表示一个正则表达式,是 basic_regex 的特化形式;
smatch:表示正则表达式的匹配结果,是 match_results 的特化形式。
regex_match():完全匹配一个字符串;
regex_search():在字符串里查找一个正则匹配;
regex_replace():正则查找再做替换。
容器
- 所有容器保存元素采用的是“值”(value)语义,也就是说,容器里存储的是元素的拷贝、副本,而不是引用。
- 有序容器(如map/set),内部使用红黑树结构。存放的自定义类型,必须要重载运算符“<”。
- 无序容器(如unordered set/unordered map),内部使用hash表。存放自定义类型,需要可以计算hash,还需要重载运算符==。其中hash算法需要指定,并在创建容器时进行设置。
unordered_set<Point, decltype(hasher)> s(10, hasher); //haser为定义的hash算法。
容器算法
- 所有的算法本质上都是 for 或者 while,通过循环遍历来逐个处理容器里的元素。
- 尽量使用标准库中的算法,效率最优。
迭代器
- 容器一般都会提供 begin()、end() 成员函数。
- 迭代器不一定支持“++”“--”操作符,要用函数来操作,常用的有这么几个:
- distance(),计算两个迭代器之间的距离;
- advance(),前进或者后退 N 步;
- next()/prev(),计算迭代器前后的某个位置。
foreach使用
for(const auto& x : v) { // range for循环
cout << x << ","; }
多线程
仅调用一次
static std::once_flag flag; // 全局的初始化标志
auto f = []() // 在线程里运行的lambda表达式
{
std::call_once(flag, // 仅一次调用,注意要传flag [](){ // 匿名lambda,初始化函数,只会执行一次
cout << "only once" << endl; } // 匿名lambda结束
); // 在线程里运行的lambda表达式结束
};
线程局部存储
thread_local int n = 0; // 线程局部存储变量
auto f = [&](int x) // 在线程里运行的lambda表达式,捕获引用
{
n += x; // 使用线程局部变量,互不影响
cout << n; // 输出,验证结果
};
thread t1(f, 10); // 启动两个线程,运行函数f thread t2(f, 20);
原子变量
- 多线程中的互斥量(Mutex)成本太高,对于小数据,应该采用“原子化”这个更好的方案。
- using atomic_bool = std::atomic<bool>; // 原子化的bool
- using atomic_int = std::atomic<int>; // 原子化的int
- using atomic_long = std::atomic<long>; // 原子化的long
- 原子变量禁用了拷贝构造函数,所以在初始化的时候不能用“=”的赋值形式,只能用圆括号或者花括号。
线程使用
thread t1(f); // 启动两个线程,运行函数f
thread t2(f);
t1.join(); // 等待线程结束
t2.join();
异步-async()
- 启动一个异步任务,相当于开了一个线程,但内部通常会有优化,比直接使用线程更好。
- async() 会返回一个 future 变量。
auto task = [](auto x) // 在线程里运行的lambda表达式
{
this_thread::sleep_for( x * 1ms); // 线程睡眠
cout << "sleep for " << x << endl; return x;
};
auto f = std::async(task, 10); // 启动一个异步任务
f.wait(); // 等待任务完成
assert(f.valid()); // 确实已经完成了任务
cout << f.get() << endl; // 获取任务的执行结果
技能相关
序列化
JSON
- 库:JSON for Modern,只需要引入一个头文件。
MessagePack
- 轻量级的数据交换格式,是二进制,比 JSON 更小巧,处理起来更快。
- 库:msgpack-c
网络通信
libcurl
- 纯 C 语言开发,兼容性、可移植性非常好。
- libcurl 收发 HTTP 数据的基本步骤有 4 个:
- 使用 curl_easy_init() 创建一个句柄,类型是 CURL*。但我们完全没有必要关心句柄的类型,直接用 auto 推导就行。
- 使用 curl_easy_setopt() 设置请求的各种参数,比如请求方法、URL、header/body 数据、超时、回调函数等。这是最关键的操作。
- 使用 curl_easy_perform() 发送数据,返回的数据会由回调函数处理。
- 使用 curl_easy_cleanup() 清理句柄相关的资源,结束会话。
cpr
- 对 libcurl 的一个 C++11 封装,使用了很多现代 C++ 的高级特性。
ZMQ
- 同时支持客户端和服务器端编程,是一个高级的异步并发框架。
设计模式(列举常用模式)
五大原则
单一职责原则
开闭原则
- 对扩展开放,对修改关闭,是否可以不改变源码就能够增加新功能。
里氏替换原则
- 子类必须能够完全替代父类,就是说子类不能改变、违反父类定义的行为。
接口隔离原则
- 尽量简化给外界调用的接口,使用适配器来转换接口,使用装饰模式来增加接口,使用外观来简化复杂系统的接口。
依赖反转原则
- 上层要避免依赖下层的实现细节,下层要反过来依赖上层的抽象定义,如模板方法模式可以算是比较明显的依赖反转的例子,父类定义主要的操作步骤,子类必须遵照这些步骤去实现具体的功能。
创建型模式-对 new 的封装
结构型模式-以灵活、可拆卸、可装配的方式组合出新的对象。
适配器模式
- 不需要修改源码,就能够把一个对象转换成可以在本系统中使用的形式。
外观模式
代理模式
- 它和适配器有点像,与其差异:适配是为了适配插入系统,而代理是要控制对象。
- 限制、屏蔽、隐藏、增强或者优化一个类,就可以使用代理。
行为模式-描述了对象之间动态的消息传递的方式
职责链
- 把多个对象串成一个“链条”,让链条里的每个对象都有机会去处理请求。
- 一系列的try-catch 块就构成了处理异常的职责链
策略
- 封装了不同的算法,可以在运行的时候灵活地互相替换。
- 策略和装饰模式和状态模式相比,策略模式的的特点是不会改变类的外部表现和内部状态,只是动态替换一个很小的算法功能模块。
这篇关于C++总结2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!