直播app開發中容易犯的小錯誤,有則改之無則加勉

雲豹科技程式設計師發表於2021-12-15

對於一個程式設計初學者來說,常犯錯是很正常的,就算是有了一定功底的人也會犯一些低階錯誤,總結一下直播app開發中容易犯錯的程式設計小坑,希望大家以後多多注意。

迴圈遍歷中刪除原先容器本身

用 for 發起任何形式的遍歷時,它的遍歷順序都是從最初就確定的,而在遍歷中刪除了原先遍歷容器本身,會導致直播app開發當前索引的變化,這樣會帶來兩個危害:一是會導致漏刪元素,二是會導致遍歷超過連結串列的長度。

這個小坑,尤其對於直播app開發初學者,很容易不知不覺就跳進去了。

比如下面這段 Python 程式碼:

# 每個詞中間是空格,用正則過濾掉英文和數字,最終輸出"每一天 你"import re
a = "I cherish 每一天 with 你 for 10000"list1 = a.split(" ")print(list1)res = re.findall(r'\d+|[a-zA-Z]+',a)for i in list1:
    if i in res:
        list1.remove(i)print(list1) #輸出是 ['cherish', '每一天', '你', '10000']

原先用正則匹配出來的四個要刪除的字元分別是 [‘I’, ‘cherish’, ‘with’, ‘for’, ‘10000’] ,在原先空格分隔索引之後分別對應[0 1 3 5 6] 可以驗證發現每次 for 迴圈遍歷刪除了原先列表裡的元素之後這個索引就變化了。

那麼,對於上面的程式碼,優雅的做法是用 join 重新拼接字串,或者以英文字元和數字作為分隔符,得到一個空字元和中文的列表,再用 join 來拼接。

迭代器失效問題

這個知識點對 C++ 程式碼的初學者來說,包括老選手,都是容易犯的錯誤。對 C++ STL 中迭代器的刪除需要慎重,稍有不慎,就會造成迭代器失效的問題。

迭代器的失效問題:對容器的操作影響了元素的存放位置,稱為迭代器失效。

失效情況:

  • 當直播app開發容器呼叫erase()方法後,當前位置到容器末尾元素的所有迭代器全部失效。
  • 當容器呼叫insert()方法後,當前位置到容器末尾元素的所有迭代器全部失效。
  • 如果容器擴容,在其他地方重新又開闢了一塊記憶體。原來容器底層的記憶體上所儲存的迭代器全都失效了。

直播app開發中迭代器失效的原因是:因為 vetor、deque 使用了連續分配的記憶體,erase 操作刪除一個元素導致後面所有的元素都會向前移動一個位置,這些元素的地址發生了變化,所以當前位置到容器末尾元素的所有迭代器全部失效。

分三種情況:

對於序列式容器(如 vector, deque)的迭代器失效示例如下:

// 在這裡想把大於2的元素都刪除
 for (auto it = q.begin(); it != q.end(); it++) {
  if (*it > 2)
   q.erase(it); // 這裡就會發生迭代器失效
 }

解決方法是利用 erase 方法可以返回下一個有效的 iterator,所以程式碼做如下修改即可:

// 在這裡想把大於 2 的元素都刪除for(auto iter=vec.begin();iter!=vec.end();){
    if(*iter>2) {
     iter=vec.erase(iter); // 這裡會返回指向下一個元素的迭代器,因此不需要再自加了
    }
    else {
     iter++;
    }}

對於連結串列式容器(如 list),刪除當前的 iterator,僅僅會使當前的 iterator 失效,這是因為 list 之類的容器,使用了連結串列來實現,插入、刪除一個結點不會對其他結點造成影響。

只要在 erase 時,遞增當前 iterator 即可,並且 erase 方法可以返回下一個有效的 iterator。

for (iter = dataList.begin(); iter != dataList.end();){
   (*it)->doSomething();
   if (shouldDelete(*iter))
      iter = dataList.erase(iter);  //erase刪除元素,返回下一個迭代器
   else
      ++iter;}

對於關聯容器(如 map, set,multimap,multiset),只要在 erase 時,遞增當前 iterator 即可。

這是因為 map 之類的容器,使用了紅黑樹來實現,插入、刪除一個結點不會對其他結點造成影響。

erase 迭代器只是被刪元素的迭代器失效,但是返回值為 void,所以要採用 erase(iter++) 的方式刪除迭代器,因為傳給 erase 的是 iter 的一個副本,iter++ 是下一個有效的迭代器。

for (iter = dataMap.begin; iter != dataMap.end();){
   (*it)->doSomething();
   if (shouldDelete(*iter))
      dataMap.erase(iter++);  //erase刪除元素,返回下一個迭代器
   else
      ++iter;}

資源關閉

這裡的資源包括直播app開發檔案、資料庫連線和 socket 連線等,我們以檔案操作為例,說明一下常見的資源關閉錯誤。

我們來以 Go 舉例,檔案操作的一個程式碼示例:

file, err := os.Open("file.go") if err != nil {
    fmt.Println("open file failed:", err)
    return}

可能你寫到這就開始專注直播app開發業務程式碼了,最後“忘記”了寫關閉檔案操作的程式碼。殊不知,在這裡埋下了一個入坑的隱患。

在 Linux 中,一切皆檔案,當開啟的檔案數過多時,就會觸發 "too many open files“ 的系統錯誤,從而讓整個系統陷入崩潰。

我們增加上關閉檔案操作的程式碼,如下所示:

file, err := os.Open("file.go")defer file.Close()if err != nil {
    fmt.Println("open file failed:", err)
    return}

Golang 提供了一個很好用的關鍵字 defer,defer 語句的含義是不管程式是否出現異常,均在函式退出時自動執行相關程式碼。

但值得注意的是上面的修改又引入了新問題,即如果檔案開啟錯誤,呼叫 file.Close 會導致程式丟擲異常(panic),所以正確的修改應該將 file.Close 放到錯誤檢查之後,如下:

file, err := os.Open("file.go")if err != nil {
    fmt.Println("open file failed:", err)
    return}defer file.Close()

變數的大小寫/初始化

變數的大小寫

在 C++,Python等語言中對於關鍵字的增加沒有 Golang那麼嚴格,相比之下,Golang 對關鍵字的增加非常吝嗇,其中沒有 private、protected 和 public 這樣的關鍵字。

要使某個符號對其他包(package)可見(即可以訪問),需要將該符號定義為以大寫字母開頭,這些符號包括介面,型別,函式和變數等。

另外對於直播app開發未使用的變數,在其他語言,可能會警告,但在 Go 語言裡,如果存在未使用的變數會導致編譯失敗。

變數的初始化

一個變數未初始化就開始使用(如果定義在全域性,變數會自動初始化,不在此列),在 Go 裡面,另一個常見錯誤是,區域性變數有可能遮蓋或隱藏全域性變數,因為通過 “:=” 方式初始化的區域性變數看不到全域性變數。

精度轉化

在對直播app開發中一些資料做處理的時候,也會遇到一些特殊 case 情況需要排查,最終你會發現精度轉化函式呼叫的不對也會掉坑裡了。

比如在 C++ 裡面,string to float and double Conversion 就提供了三個函式,那麼具體到實戰層面來說,每個函式的保留的位數也是不一樣的。

  • std::stof() - convert string to float
  • std::stod() - convert string to double

不同等級的精度轉化不一樣。

string s = "116.8"// 若呼叫 stof 轉化為 float 則結果是  116.80000305175781// 若呼叫 stod 轉化為 double 則結果是 116.8

如果轉換的數在後續需要進一步的數學運算,比如每個數乘以 1e6,可想而知導致的結果肯定是不一樣的,如果這裡沒注意,涉及廣告等業務的程式碼上線出了事故,那損失可就不是一筆小數目啊。

其它小坑

在直播app開發中忽略了“=”與“ ”的區別,在大部分語言的語法規則裡,“=”是賦值運算子,“”是關係運算子。

另外對於 Python 來說。

  • 誤以為 Python 是弱型別語言,其實是強型別語言;
  • 誤以為 True/False 是常量值,在 Pytho2.7是兩個內建(built-in)變數;
  • 對於兩個等值數字比如 a = 88, b =88, 誤以為 a is b 返回 True,其實返回 False;
  • 誤以為函式不是物件,其實函式也是物件,也可以像其他型別的物件一樣被賦值,傳遞,作為返回值。

本文轉載自網路,轉載僅為分享乾貨知識,如有侵權歡迎聯絡雲豹科技進行刪除處理
原文連結:https://mp.weixin.qq.com/s/A1RsLNclAf9yGIEALCUuzQ


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69996194/viewspace-2847941/,如需轉載,請註明出處,否則將追究法律責任。

相關文章