之前沒有接觸過 c++,不過聽說 c++ 的指針很坑,直到最近在用 QT / C++ 寫一個 Linux Deepin 系統上檢測網絡流量和網速的小程序時,發現 c++ 的指針用起來真的特別蛋疼。
不過好在花了幾個小時最終還是明白了指針的用法。
有一段代碼的原型大概是這樣的:
QList<NetFlowObject> netflowobj_list; /** 從 list 列表中找出網卡名為 ifname 的 NetFlowObject 對象 **/ bool getNfoFromList(QString ifname, NetFlowObject &nfo);
其中 NetFlowObject 是自己寫的一個類,QList 是 Qt 提供的一個鏈表。 getNfoFromList 函數返回 boolean 型結果,如果找到相同名稱的網卡,返回 true,並將 nfo 設為 QList 中找到的 NetFlowObject 對象。否則返回 false。
那么最開始的想法是通過遍歷 QList 找到 NetFlowObject 對象。
bool NetInfo::getNfoFromList(QString ifname,NetFlowObject &nfo) { //-------- A①
foreach(NetFlowObject o, netflowobj_list) { //-------- A②
if(o.getIfName() == ifname) { //-------- A③
nfo = o; return true;
}
}
return false;
}
void NetInfo::someFunction() { // 如果找到相同的 nfo 對象,修改它的數據 NetFlowObject nfo1; bool finded = getNfoFromList(ifname, nfo1); //-------- B① if(finded) { nfo1.updateRecvBytes(if_recv_bytes.toInt()); nfo1.updateTransBytes(if_trans_bytes.toInt()); } }
嗯,上面的這段代碼很顯然沒有辦法修改 QList 鏈表中的對象的屬性。首先,函數是傳值的,也就是說 A① 處函數的參數 nfo 是不會影響 someFunction 里 B① 處的 nfo1 對象的。nfo1 對象的屬性改變同樣也不會影響 nfo 對象。
通過函數的參數傳遞的只是 nfo1 對象的一個副本,兩個對象之間不會影響。
其次,A②處的 foreach 這個便捷的循環也是提供 QList 對象的一個副本,這樣的話,更加沒有辦法修改找到的 NetFlowObject 對象了。
好吧,這個錯誤是 c++ 最常見也是最愚蠢的錯誤,那么想要得到 QList 中的某個 NetFlowObject 對象的引用,函數傳遞的就不能是 NetFlowObject 對象了,那么就改成 NetFlowObject * 也就是指針吧。另外,將循環改為 for 計數循環。
代碼如下
bool NetInfo::getNfoFromList(QString ifname,NetFlowObject *nfo) { for(int i = 0; i < netflowobj_list.count(); i++) { NetFlowObject o = netflowobj_list[i]; // PrintUtil::print(o.getIfName() + " === " + QString::number(o.getLatestRecvBytes())); if(o.getIfName() == ifname) { *nfo = netflowobj_list[i]; // 將指針所指向的 NetFlowObject 對象修改為 o -------C① return true; } } return false; }
相應的,
bool finded = getNfoFromList(ifname, &nfo1);
但是這樣做,發現修改 nfo1 對象仍然沒有效果, QList 中 NetFlowObject 對象的屬性依然沒有改變。恩沒有錯,上面代碼 C① 處又是傳遞的副本。
這里要說一下 QList 對象的 at(i) 函數和 QList[i] 數組形式得到的對象是不同的。函數原型為 const T &QList.at(int i) const,也就是不能通過 at() 的返回值對 nfo 對象進行修改,
而數組形式的函數原型是 T &QList::operator[] (int i),也就是用起來和平常的數組沒什么太大區別。
既然一層引用不能達到效果,那么,函數傳遞的時候,就傳一個 NetFlowObject * 對象的指針,也就是作為 NetFlowObject 對象的指針的引用。(這個指針的指針說起來太繞口了,第二個指針改稱引用,不然腦子就漿糊了),貼出最終的代碼:
bool NetInfo::getNfoFromList(QString ifname,NetFlowObject **nfo) { for(int i = 0; i < netflowobj_list.count(); i++) { NetFlowObject o = netflowobj_list[i]; // PrintUtil::print(o.getIfName() + " === " + QString::number(o.getLatestRecvBytes())); if(o.getIfName() == ifname) { *nfo = &netflowobj_list[i]; // 將指針所指向的 NetFlowObject 指針修改為 QList 中第 i 個對象的引用 return true; } } return false; } void NetInfo::someFunction() { NetFlowObject *nfo; bool finded = getNfoFromList(ifname, &nfo); if(finded) { nfo->updateRecvBytes(if_recv_bytes.toInt()); nfo->updateTransBytes(if_trans_bytes.toInt()); } }
通過兩層引用,最終達到了修改 QList 鏈表中對象的目的。
感覺還有一種解決方法,就是將函數原型 getNfoFromList 修改成 NetFlowObject * getNfroFromList(QString ifname);
找到相應的對象后直接返回該對象的引用,這樣就不用通過函數參數傳遞兩層引用了。
-------------------------- 思考 -------------------------
引用指針繞的頭都暈了,而且這個錯誤理論上應該屬於邏輯錯誤(編譯時不會報錯,運行時不會發生異常),但是特喵的就是結果不對,debug 起來也很蛋疼,通過長時間的分析和總結才能得到正確的結果。