C/C++教程

C++总结2

本文主要是介绍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 个:
  1. 使用 curl_easy_init() 创建一个句柄,类型是 CURL*。但我们完全没有必要关心句柄的类型,直接用 auto 推导就行。
  2. 使用 curl_easy_setopt() 设置请求的各种参数,比如请求方法、URL、header/body 数据、超时、回调函数等。这是最关键的操作。
  3. 使用 curl_easy_perform() 发送数据,返回的数据会由回调函数处理。 
  4. 使用 curl_easy_cleanup() 清理句柄相关的资源,结束会话。

cpr

  • 对 libcurl 的一个 C++11 封装,使用了很多现代 C++ 的高级特性。

ZMQ

  • 同时支持客户端和服务器端编程,是一个高级的异步并发框架。

设计模式(列举常用模式)

五大原则

单一职责原则

  • 不要做多余的事,功能明确单一。

开闭原则

  • 对扩展开放,对修改关闭,是否可以不改变源码就能够增加新功能。

里氏替换原则

  • 子类必须能够完全替代父类,就是说子类不能改变、违反父类定义的行为。

接口隔离原则

  • 尽量简化给外界调用的接口,使用适配器来转换接口,使用装饰模式来增加接口,使用外观来简化复杂系统的接口。

依赖反转原则

  • 上层要避免依赖下层的实现细节,下层要反过来依赖上层的抽象定义,如模板方法模式可以算是比较明显的依赖反转的例子,父类定义主要的操作步骤,子类必须遵照这些步骤去实现具体的功能。

创建型模式-对 new 的封装

  • 单例
  • 工厂

结构型模式-以灵活、可拆卸、可装配的方式组合出新的对象。

适配器模式

  • 不需要修改源码,就能够把一个对象转换成可以在本系统中使用的形式。

外观模式

  • 封装了一组对象,然后对外表现成一个对象。

代理模式

  • 它和适配器有点像,与其差异:适配是为了适配插入系统,而代理是要控制对象。
  • 限制、屏蔽、隐藏、增强或者优化一个类,就可以使用代理。

行为模式-描述了对象之间动态的消息传递的方式

职责链

  • 把多个对象串成一个“链条”,让链条里的每个对象都有机会去处理请求。
  • 一系列的try-catch 块就构成了处理异常的职责链

策略

  • 封装了不同的算法,可以在运行的时候灵活地互相替换。
  • 策略和装饰模式和状态模式相比,策略模式的的特点是不会改变类的外部表现和内部状态,只是动态替换一个很小的算法功能模块。
这篇关于C++总结2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!