羅劍鋒的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: 網絡通訊庫,支持多種通訊模式,可把消息隊列直接嵌入應用程序。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章