罗剑锋的C++实战笔记-学习笔记(2)

书接上文,三句名言镇楼。

三句名言镇楼

  • 任何人都能写出机器能看懂的代码,只有优秀的程序员才能写出人看懂的代码

  • 两种写程序的方式:把代码写的非常复杂,以至于"看不出明显错误"。把代码写的非常简单,以至于"明显看不出错误"。

  • 把正确的代码改快速,要比把快速的代码改正确,容易得太多。

处理错误方案

  • 错误码:检查函数执行返回值或者 errno 码,依此为下一步行动依据。
    缺点:

    1. 将业务代码和错误处理代码混在一起。
    2. 错误码可以被忽略,可能会引发更多错误
  • 异常

    1. 通过 throw 抛出异常,在 catch 中统一处理异常,分离业务代码和错误处理代码。
    2. 不能被忽略
    3. 适用于无法使用错误码的场合

有一种包裹整个函数体的异常写法:


void some_fun()
try
{

}
catch(...)
{

}

上述写法可以捕获some_fun执行过程中所有可能的异常,而且少一级缩进,处理逻辑更清晰。

一般认为,普通构造、拷贝构造、转移构造、析构函数应尽量声明为noexcept,告知编译器据此进行优化。析构函数保证绝对不会抛异常。

Lambda

Lambda函数内部的捕获变量,需要在函数体声明前方声明,才能正确捕获。

	auto f2 = [&]()
	{
		x_ += 10; // 在函数体之前,是看不到x_变量的,编译出错。
	};
	int x_ = 33;   
	

一句话,按需捕获,最小化对外部的影响。每个 lambda 表达式的类型都是唯一的,可使用 std::function 类来存储。

字符串和容器的区别

字符串是文本,内部的字符之间是强关系,顺序不能随便调换,否则就失去了本意,通常视为一个整体来处理。

容器是容纳某种类型数据的集合,内部元素之间没有任何关系,对容器来说,可以随意增删改,以单个元素为操作单位。

使用R来表示原始字符串,比如


auto str1 = R"(\r\n\t)";    // 表示 str1 为 \r\n\t 这6个字符。

STL中的算法

相比于手写for循环,STL中的内置算法是更高层次的抽象和封装。 关注做了什么,而不关心是怎么做的。

比如说,统计一个序列中大于2的元素个数。


#include <algorithm>
auto n = std::count_if(begin(v), end(v), [](auto& v){
    return v > 2;
});

算法通过迭代器间接访问容器内部元素,分离了数据和对数据的操作,使得算法适用范围更广,更灵活。为了兼顾效率,在通用算法之上,STL针对mergesortunique等算法,提供了各容器的特化版本,针对数据结构特点进行针对性的优化。例如list容器有sort成员函数。

打印数组中的元素,可使用如下套路:


auto print = [](const auto& x) {
	cout << x << ",";
};

std::sort(begin(arr), end(arr));        // 先排序
for_each(cbegin(arr), cend(arr), print);   //  再输出

for_each将元素遍历和对每个元素的操作分开,含义明确,达到更好的封装。

排序算法

  • 要求稳定,使用 stable_sort
  • 选出前几名(TopN),并要求有序,使用 partial_sort
  • 选出前几名(BestN),不要求有序,使用 nth_element。 该算法适用于中位数、百分位数
  • 分成两组,使用 partition
  • 求最大、最小值,使用 minmax_element

多线程开发实践

  • 仅调用一次:std::call_once函数,在多线程调用时,保证可调用对象只会调用一次。
  • 线程局部存储: thread_local关键字。
  • 原子变量:原子变量禁用拷贝构造函数,不能使用=赋值,只能用圆括号或花括号。
  • 将线程往上抽象一层,运行一个异步任务,可使用 std::async
#include <future>
#include <thread>
using std::this_thread::sleep_for;
#include <chrono>
void test_async()
{
	auto task = [](auto x) {
		cout << "begin sleep for" << x << endl;
		sleep_for(std::chrono::milliseconds(x * 1000));
		cout << "end sleep for" << x << endl;
		return x;
	};

	auto f = std::async(task, 50); // 启动一个异步任务
	f.wait();      // 等待任务完成
	assert(f.valid());     // 确实已经完成任务
	cout << f.get() << endl;     // 获得任务执行结果(只能调用一次)
}


应用层常用库

数据交换格式

  • json: 纯文本,容易阅读,编辑,适用性最广。可使用nlohmann/json.hpp来解析
  • MessagePack : 二进制格式,小巧高效,但只对基本类型和标准容器进行序列化/反序列化。
  • ProtoBuffer: Google出品的工业级数据格式,注重安全和性能。

网络通讯

  • libcurl: 著名开源curl项目的底层核心库,C语言编写,兼容性强。cprlibcurlc++11封装版本,常用于客户端程序。
  • ZMQ: 网络通讯库,支持多种通讯模式,可把消息队列直接嵌入应用程序。
posted @ 2022-11-28 11:52  浩天之家  阅读(122)  评论(0编辑  收藏  举报