調(diào)試實戰(zhàn) | 缺少 const 導致的 bug
前言
最近,在項目里遇到一個很 “詭異” 的問題。明明把后面需要使用的值存起來了,可是使用的時候,卻拿到了一堆垃圾數(shù)據(jù)。有可能是什么原因呢?一起來看看吧。
調(diào)查思路
這種問題排查起來比較簡單??梢砸来闻挪橐韵聨讉€地方:
- 保存代碼是否正確。
- 讀取代碼是否正確。
- 在保存后,讀取前是否有其它代碼修改了保存的值。
按照這個思路,很快定位到問題出在第一步,也就是保存的時候出了問題。
為了讓各位小伙伴兒也能實際感受這個問題,我特意模仿實際項目中的代碼寫了一份可以重現(xiàn)的代碼。
關(guān)鍵代碼
AdjustInfoByteConverter 里的代碼比較簡單,而且已經(jīng)驗證過沒問題,這就不貼了。感興趣的小伙伴兒可以下載示例工程查看完整代碼。
調(diào)用代碼如下:
#include?"stdafx.h"
#include?"DemoObject.h"
int?_tmain(int?argc,?_TCHAR*?argv[])
{
????CDemoObject?object;
????AdjustInfo?info;
????auto?bytes?=?AdjustInfoByteConverter::ToBytes(info);
????object.SetAdjustInfo(bytes);
????//?in?real?project,?object?will?be?serialized,?and?deserialized.
????//?following?code?may?run?on?another?thread.
????std::vector<byte>?restoreBytes;
????object.GetAdjustInfo(restoreBytes);
????auto?info1?=?AdjustInfoByteConverter::FromBytes(restoreBytes);
????return?0;
}
說明:
在實際排查問題的時候我就是這樣縮小排查范圍的,調(diào)用完
SetAdjustInfo(),直接調(diào)用GetAdjustInfo(),查看兩次結(jié)果是否一致。這樣可以很快縮小需要排查的范圍。在寫完
AdjustInfoByteConverter::ToBytes和AdjustInfoByteConverter::FromBytes的實現(xiàn)后,也用了類似的辦法做了驗證,所以在實際項目中直接排除了這兩個函數(shù)的嫌疑。
CDemoObject 類的實現(xiàn)如下,很規(guī)矩。
#pragma?once
#include?"ObjectProperty.h"
#include?"AdjustInfo.h"
#include?"AdjustInfoByteConverter.h"
class?CDemoObject
{
private:
????ExtendPropertySet?extendProperty;
public:
????void?SetAdjustInfo(const?std::vector<byte>&?bytes)
????{
????????PropertyValue?value(bytes);
????????extendProperty.SetSubParam("AdjustInfo",?value);
????}
????int?GetAdjustInfo(std::vector<byte>&?bytes)?const
????{
????????PropertyValue?value;
????????extendProperty.GetSubParam("AdjustInfo",?value);
????????bytes?=?value.m_value.m_valueByte;
????????return?(int)bytes.size();
????}
};
有瑕疵的代碼如下,但并不是所有情況下都有問題,你能找出這個問題嗎?
#pragma?once
#include?<vector>
#include?<map>
class?PropertyValue
{
public:
????enum?{?VALUENULL,?INT,?DOUBLE,?Bool,?String,?Block?}?m_type;
????PropertyValue()?:?m_type(VALUENULL)?{}
????struct
????{
????????std::vector<byte>?m_valueByte;
????}?m_value;
????template<class?T>
????PropertyValue(T?value)
????{
????????m_type?=?Block;
????????unsigned?__int32?nSize?=?sizeof(value);
????????byte?*data?=?(byte*)&value;
????????for?(unsigned?__int32?i?=?0;?i?<?nSize;?++i)
????????{
????????????m_value.m_valueByte.push_back((byte)(*(data?+?i)));
????????}
????}
????PropertyValue(std::vector<byte>&?value)
????{
????????m_type?=?Block;
????????m_value.m_valueByte?=?value;
????}
};
class?ExtendPropertySet
{
public:
????void?SetSubParam(const?std::string&?name,?PropertyValue?param)
????{
????????m_SubParamMap[name]?=?param;
????}
????bool?GetSubParam(const?std::string&?name,?PropertyValue&?param)?const
????{
????????auto?it?=?m_SubParamMap.find(name);
????????if?(it?!=?m_SubParamMap.end())
????????{
????????????param?=?it->second;
????????????return?true;
????????}
????????return?false;
????}
private:
????std::map<std::string,?PropertyValue>??m_SubParamMap;
};
根本原因
這個問題的根本原因在于:調(diào)用了錯誤的 PropertyValue 構(gòu)造函數(shù)。
預期被調(diào)用的函數(shù)是 PropertyValue(std::vector<byte>& value),而實際調(diào)用的函數(shù)卻是 template<class T> PropertyValue(T value)。
因為 CDemoObject 類的 void SetAdjustInfo(const std::vector<byte>& bytes) ?函數(shù)的參數(shù)是 const 的。在編譯 PropertyValue value(bytes); 這行代碼的時候,需要找到一個最優(yōu)的構(gòu)造函數(shù),最終找到的是 template 版本的。不能把一個 const 對象丟給一個參數(shù)是非 const 的函數(shù)!
解決方案
這個問題解決起來很簡單,有兩種改法:
- 去掉
const對象的const屬性。 - 改動底層代碼,把非
const版本改成const版本的函數(shù)。
實際項目中采用的第一種改法,因為沒有權(quán)限改動底層接口,但這種改法治標不治本。
下載鏈接
百度云盤鏈接: 鏈接: https://pan.baidu.com/s/1a2p9YWPLtlOe6dM_j_s80w 提取碼: j96h
CSDN:https://download.csdn.net/download/xiaoyanilw/14965002
總結(jié)
- 盡量使用引用傳遞類對象,而且如果不想在函數(shù)內(nèi)部修改這個對象的話,務必加上
const。 - 不能把一個
const對象丟給一個非const參數(shù)的函數(shù)。 - 排查問題的時候,盡可能的縮小范圍。
