strptime使用問題和對象的深淺拷貝問題

一、      strptime函數的使用問題

【問題描述】
用strptime函數,對時間字段進行處理,字段格式爲“%Y年%m月%d日 %H時%M分”。通過程序對該字段進行修改,然後驗證字段值,發現修改後的值不對,而且多次修改,字段值會不斷變化。
【問題重現】
編寫一個簡化版的測試代碼,如下:

void strptime_test(const char* format, const char* strTime)
{
struct tm tmTmp;
// 用MEMSET宏定義,控制是否清空tmTmp
#ifdef MEMSET
    memset(&tmTmp, 0, sizeof(tmTmp));
#endif
    if(NULL == strptime(strTime, format, &tmTmp)){
        fprintf(stderr, "strptime error: %s\n", strTime);
        return;
    }
    time_t nTime = timegm(&tmTmp);
    printf("strptime(%s) is %d\n", strTime, nTime);
}
int main(int argc, char** argv)
{
    …..//參數檢查
    strptime_test(argv[1], argv[2]);
}

用MEMSET宏控制strptime前是否初始化tmTmp,分別編譯帶宏的和不帶宏的兩個版本:
g++ strptime_test.cpp -o strptime_test_nomemset
g++ -DMEMSET strptime_test.cpp -o strptime_test
執行結果如下:

bash$ ./strptime_test_nomemset "%Y-%m-%d %H:%M" "1970-1-1 0:0"
strptime(1970-1-1 0:0) is 1199291128
bash $ ./strptime_test_nomemset "%Y-%m-%d %H:%M" "1970-1-1 0:0"
strptime(1970-1-1 0:0) is -1734398952
bash $ ./strptime_test_nomemset "%Y-%m-%d %H:%M:%S" "1970-1-1 0:0:0"
strptime(1970-1-1 0:0:0) is 0
bash $ ./strptime_test_nomemset "%Y-%m-%d %H:%M:%S" "1970-1-1 0:0:0"
strptime(1970-1-1 0:0:0) is 0

沒有memset初始化tmTmp的版本,不帶秒的結果是隨機的,帶秒的結果是正常的

bash$ ./strptime_test "%Y-%m-%d %H:%M" "1970-1-1 0:0"
strptime(1970-1-1 0:0) is 0
bash $ ./strptime_test "%Y-%m-%d %H:%M" "1970-1-1 0:0"
strptime(1970-1-1 0:0) is 0
bash $ ./strptime_test "%Y-%m-%d %H:%M:%S" "1970-1-1 0:0:0"
strptime(1970-1-1 0:0:0) is 0
bash $ ./strptime_test "%Y-%m-%d %H:%M:%S" "1970-1-1 0:0:0"
strptime(1970-1-1 0:0:0) is 0

使用memset初始化tmTmp的版本,不帶秒和帶秒的結果都是正常的
【原因分析】
通過簡化的測試代碼定位問題後,確認是在調用strptime函數時,不會把年月日時分秒等所有的字段清空,如果有一個字段沒賦值,則值是隨機的。因爲在isearch裏的時間沒有秒,因此使用strptime時,需要memset初始化。
在strptime的man手冊裏有這樣一句:
In principle, this function does not initialize tm but only stores the values specified.  This means  that  tm  should  be  initialized  before  the  call.
【解決辦法】
由於輸入的時間是沒有秒的,因此,在調用strptime前,調用memset初始化變量,避免秒爲隨機值,gmtexpire時間會變的問題解決了。

二、      對象的深拷貝和淺拷貝問題

【問題描述】
在處理先delete後add doc,並且是多條add時,程序會core掉。
【原因分析】
這個添加多個doc的場景,代碼實現簡化後如下:

Document* pDoc = new Document;
vector< Document> docVec;
pDoc->setValue(…);
docVec.push_back(*pDoc);
pDoc->setValue(…);
docVec.push_back(*pDoc);

而CDocument類的實現如下:

class CDocument
{
    ……//其它函數忽略
    /* 析構函數 */
    ~CDocument()
    {
        for(n32_t i = 0; (size_t)i < m_fields.size(); i++)
        {
            if(m_fields[i]) delete m_fields[i];
        }
    }
private:
    std::vector<CField*> m_fields;
};

 

Document類中有一個指針的vector成員“std::vector<CField*> m_fields;”,而沒有實現拷貝構造函數。在docVec.push_back(*pDoc)時,只是淺拷貝,只拷貝了CField的指針,這樣一份CField會有多個Document的指針指向。
在對象析構時,又因爲析構函數中,只調用了delete,沒有將指針賦成NULL,在Document析構的時候,會被釋放多次,導致程序core掉。
淺拷貝和深拷貝的區別如下圖,左爲淺拷貝,右爲深拷貝。

【解決辦法】
1)  在CDocument析構函數中,delete指針後,將指針賦成NULL,可解決core的問題。但這樣docvec中是放了多份Document的淺拷貝,且裏面的CField都是一樣的。
2)  在CDocument中添加拷貝構造函數,但需要改動CDocument類,未採用
3)  在調用處,實現CDocument類的拷貝,目前使用此方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章