在經(jīng)過一段時間的編寫,我們的數(shù)據(jù)結(jié)構(gòu)庫已經(jīng)有了一定的規(guī)模。那么我們之前編寫的代碼就沒有一點 bug 嗎?之前每個代碼都是經(jīng)過測試了的,因此闊能是沒有 bug 的,但是我們還是來對它進行分析,看看究竟是否一點 bug 都沒有呢。
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站建設(shè)、成都網(wǎng)站制作與策劃設(shè)計,遷西網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:遷西等地區(qū)。遷西做網(wǎng)站價格咨詢:028-86922220測試 1 :創(chuàng)建異常對象時的空指針問題,如下
main.cpp 如下
#include#include "Exception.h" using namespace std; using namespace DTLib; int main() { try { NullPointerException npe; cout << "throw" << endl; throw npe; } catch(const Exception& e) { cout << "catch" << endl; } return 0; }
我們來看看編譯結(jié)果
我們看到編譯運行時正常結(jié)束的,并沒有出錯。那么這是否就說明我們之前所編寫的代碼一點 bug 也沒有呢?我們來看看之前的 Exception 類族是怎樣實現(xiàn)的,它調(diào)用的是 strdup 函數(shù)實現(xiàn)的。我們在網(wǎng)上搜下 strdup 函數(shù)是怎樣實現(xiàn)的,它的源碼如下
我們看到它并沒有做指針 s 為空的判斷,因此我們直接使用這個函數(shù)還是有問題的。我們要在 message 賦值這塊使用下三目運算符來進行判斷,代碼如下
void Exception::init(const char* message, const char* file, int line) { m_message = (message ? strdup(message) : NULL); if( file != NULL ) { char s1[16] = {0}; itoa(line, s1, 10); m_location = static_cast(malloc(strlen(file) + strlen(s1) + 2)); m_location = strcpy(m_location, file); m_location = strcat(m_location, ":"); m_location = strcat(m_location, s1); } else { m_location = NULL; } }
這樣的話,代碼就足夠安全了。
測試 2 :LinkList 中的數(shù)據(jù)元素刪除
main.cpp 如下
#include#include "LinkList.h" using namespace std; using namespace DTLib; class Test : public Object { int m_id; public: Test(int id = 0) { m_id = id; } ~Test() { if( m_id == 1 ) { throw m_id; } } }; int main() { LinkList list; Test t0(0), t1(1), t2(2); try { list.insert(t0); list.insert(t1); list.insert(t2); list.remove(1); } catch(int e) { cout << e << endl; cout << list.length() << endl; } return 0; }
我們在 QT 中編譯運行的時候直接崩潰了,因為 Test 類在析構(gòu)時拋出異常了。在 VS 上運行時可以得到一丁點的結(jié)果,打印出的結(jié)果是鏈表的長度為 3。在銷毀 t1 對象之后,結(jié)果還是為 3,這個結(jié)果就有點問題了。我們來看看 LinkList 的 remove 操作是怎樣實現(xiàn)的,它是在 destroy 之后才進行 m_length-- 的。我們應(yīng)該在 destroy 之前進行 m_length--才行。那么 remove 操作有問題,clear 操作肯定也有問題了。我們應(yīng)該在 destroy 之前進行 m_length-- 的操作,而不是在最后將它賦值為 0。
測試 3 :LinkList 中遍歷操作與刪除操作的混合使用
main.cpp 如下
#include#include "LinkList.h" using namespace std; using namespace DTLib; int main() { LinkList list; for(int i=0; i<5; i++) { list.insert(i); } for(list.move(0); !list.end(); list.next()) { if( list.current() == 3 ) { list.remove(list.find(list.current())); cout << list.current() << endl; } } for(int i=0; i 我們來編譯運行,看看結(jié)果
結(jié)果是正確的,那么上面在查找數(shù)值為 3 的元素,查找成功之后刪除。那么刪除完之后他的 current 值為多少呢?我們看到結(jié)果是一個隨機數(shù)。那么問題來了,我們在刪除之后,理論上看到的數(shù)據(jù)元素應(yīng)該是它的下一個數(shù)字 4 啊。這是就需要修改 remove 函數(shù)了。我們在 remove 函數(shù)中添加一個判斷,如果它是指向即將要被刪除的元素,那么就將它指向 toDel->next。具體改動如下
bool remove(int i) { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { Node* current = position(i); Node* toDel = current->next; if( m_current == toDel ) { m_current = toDel->next; } current->next = toDel->next; m_length--; destroy(toDel); } return ret; }我們再來看看編譯結(jié)果
那么此時這個 bug 就已經(jīng)解決了。
測試 4 :StaticLinkList 中數(shù)據(jù)元素刪除時的效率問題
main.cpp 如下
#include#include "StaticLinkList.h" using namespace std; using namespace DTLib; int main() { StaticLinkList list; for(int i=0; i<5; i++) { list.insert(i); } list.remove(3); for(int i=0; i 我們來看看編譯結(jié)果
結(jié)果自然是沒錯的,我們再來仔細看看 StaticLinkList 類的 destroy 函數(shù)。在歸還空間之后,我們應(yīng)該直接跳出循環(huán),這樣效率會高一點。
測試 5 :StaticLinkList 是否需要提供析構(gòu)函數(shù)?
main.cpp 如下
#include#include "StaticLinkList.h" using namespace std; using namespace DTLib; int main() { StaticLinkList list; for(int i=0; i<5; i++) { list.insert(i); } for(int i=0; i 我們在 LinkList 類中是提供了析構(gòu)函數(shù)的,并且這個析構(gòu)函數(shù)是虛函數(shù)。我們根據(jù)之前的知識,雖然父類 LinkList 有析構(gòu)函數(shù)的實現(xiàn),但是這個析構(gòu)函數(shù)不會發(fā)生多態(tài)的,所以說在 LinkList 中調(diào)用的 clear 函數(shù)是 LinkList 中的 clear 函數(shù),在 StaticLinkList 中的 clear 函數(shù)調(diào)用的也是 LinkList 中的 clear 函數(shù)。但是在 clear 函數(shù)中調(diào)用的是 destroy 函數(shù),所以兩個類調(diào)用的 destroy 函數(shù)都是 LinkList 類中的 destroy 函數(shù)。我們在 LinkList 和 StaticLinkList 中的 create 和 destroy 函數(shù)中都打上斷點,然后進行斷點調(diào)試,看看結(jié)果
我們看到在進行 create 的時候,因為類對象是 StaticLinkList 類型的,所以它會去按照 StaticLinkList 的方式來進行 create,再來看看 destroy 時,進行的是哪個類函數(shù)的實現(xiàn)
我們看到在進行 destroy 時進行的是 LinkList 方式,這時便出現(xiàn)問題了。此時 delete 操作有可能不是進行的是堆空間上的操作,那么程序沒直接崩潰時因為這個程序太短小了。我們要將它改為實現(xiàn)的是 StaticLinkList 方式的 destroy 函數(shù)。那么該如何操作呢?我們就需要在 StaticLinkList 類中加上它的析構(gòu)函數(shù)了,如下
~StaticLinkList() { this->clear(); }我們再來斷點調(diào)試下,看看它調(diào)用的 destroy 函數(shù)究竟是哪個版本的實現(xiàn)。
我們看到這下調(diào)用的是 StaticLinkList 類方式的實現(xiàn)了。
測試 6 :DTLib 庫是否有必要增加多維數(shù)組類呢? 多維數(shù)組的本質(zhì)其實就是數(shù)組的數(shù)組!
main.cpp 如下
#include#include "StaticLinkList.h" #include "DynamicArray.h" using namespace std; using namespace DTLib; int main() { DynamicArray< DynamicArray > d; d.resize(3); for(int i=0; i 我們來看看編譯運行結(jié)果
我們再將上面的第 16 行的 d[i].resize(3); 改為 d[i].resize(i + 1);
程序照常打印出了多維數(shù)組,因此我們的庫是沒必要進行多維數(shù)組類的開發(fā)的。因為多維數(shù)組的本質(zhì)就是數(shù)組的數(shù)組!通過今天的 bug 的調(diào)試,我們所寫的庫也是有一定的 bug 的,凡是軟件編寫的產(chǎn)品基本上都會出 bug,所以需要我們不停的迭代升級,去解決問題。
本文題目:典型問題分析(十五)-創(chuàng)新互聯(lián)
URL鏈接:http://weahome.cn/article/jpggg.html