書接上文,三句名言鎮樓。
三句名言鎮樓
-
任何人都能寫出機器能看懂的代碼,只有優秀的程序員才能寫出人看懂的代碼
-
兩種寫程序的方式:把代碼寫的非常複雜,以至於"看不出明顯錯誤"。把代碼寫的非常簡單,以至於"明顯看不出錯誤"。
-
把正確的代碼改快速,要比把快速的代碼改正確,容易得太多。
處理錯誤方案
-
錯誤碼:檢查函數執行返回值或者
errno
碼,依此爲下一步行動依據。
缺點:- 將業務代碼和錯誤處理代碼混在一起。
- 錯誤碼可以被忽略,可能會引發更多錯誤
-
異常
- 通過
throw
拋出異常,在catch
中統一處理異常,分離業務代碼和錯誤處理代碼。 - 不能被忽略
- 適用於無法使用錯誤碼的場合
- 通過
有一種包裹整個函數體的異常寫法:
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
針對merge
、sort
、unique
等算法,提供了各容器的特化版本,針對數據結構特點進行鍼對性的優化。例如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語言編寫,兼容性強。cpr
是libcurl
的c++11
封裝版本,常用於客戶端程序。ZMQ
: 網絡通訊庫,支持多種通訊模式,可把消息隊列直接嵌入應用程序。