strdup 源代碼:
char * __strdup(const char *s) { size_t len = strlen(s) +1; void *new = malloc(len); if (new == NULL) return NULL; return (char *)memecpy(new,s,len); }
問題:strdup 未對空指針作處理。
m_message = strdup(message);
==>node
m_message = (message != nullptr) ? strdup(message) : nullptr;
[修復] 文件:Exception.cppios
#include "Exception.h" #include <cstring> #include <cstdlib> namespace DTLib { Exception::Exception(const char *message) : Exception(message, nullptr, 0) { } Exception::Exception(const char *file, int line) : Exception(nullptr, file, line) { } Exception::Exception(const char *message, const char *file, int line) { m_message = (message != nullptr) ? strdup(message) : nullptr; // 修復!! if (file != nullptr) { char sl[16] = {0}; itoa(line, sl, 10); m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2)); m_location = strcpy(m_location, file); m_location = strcat(m_location, ":"); m_location = strcat(m_location, sl); } else { m_location = nullptr; } } Exception::Exception(const Exception &e) { m_message = strdup(e.m_message); m_location = strdup(e.m_location); } Exception &Exception::operator= (const Exception &e) { if (this != &e) { free(m_message); free(m_location); m_message = strdup(e.m_message); m_location = strdup(e.m_location); } return *this; } const char *Exception::message() const { return m_message; } const char *Exception::location() const { return m_location; } Exception::~Exception() { delete m_message; delete m_location; } }
LinkList<Test> list; Test t0(0), t1(1), t2(2); // t1 在析構時拋出異常 try { } catch(...) { cout << list.length() << endl; // ??? }
輸出:[Qt]
說明:Qt中析構函數拋出的異常,沒法被外部捕獲
terminate called after throwing an instance of 'int'
輸出:[vs]
1 3
問題:單鏈表狀態出現問題,指望結果爲 2。
定位:remove 函數未考慮異常安全。
bool remove(int i) { // ... destroy(toDel); --m_length; // ... }
==>segmentfault
bool remove(int i) { // ... --m_length; destroy(toDel); // ... }
[修復]文件:LinkList.h數組
#ifndef LINKLIST_H #define LINKLIST_H #include "List.h" #include "Exception.h" namespace DTLib { template <typename T> class LinkList : public List<T> { public: LinkList() { m_header.next = nullptr; m_length = 0; m_step = 0; m_current = nullptr; } bool insert(const T &e) override // O(n) { return insert(m_length, e); } bool insert(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i <= m_length)); if (ret) { Node *node = create(); if (node != nullptr) { Node *current = position(i); node->value = e; node->next = current->next; current->next = node; ++m_length; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ..."); } } return ret; } bool remove(int i) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { Node *current = position(i); Node *toDel = current->next; current->next = toDel->next; --m_length; destroy(toDel); } return ret; } bool set(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { position(i)->next->value = e; } return ret; } T get(int i) const // O(n) { T ret; if (!get(i, ret)) { THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); } return ret; } bool get(int i, T &e) const override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { e = position(i)->next->value; } return ret; } int find(const T &e) override // O(n) { int ret = -1; int i = 0; Node *node = m_header.next; while (node) { if (node->value == e) { ret = i; break; } else { node = node->next; ++i; } } return ret; } int length() const // O(1) { return m_length; } void clear() // O(n) { while (m_header.next) { Node *toDel = m_header.next; m_header.next = toDel->next; --m_length; destroy(toDel); } } bool move(int i, int step = 1) // O(n) { bool ret = ((0 <= i) && (i < m_length) && (step > 0)); if (ret) { m_current = position(i)->next; m_step = step; } return ret; } bool end() // O(1) { return (m_current == nullptr); } T current() // O(1) { if (!end()) { return m_current->value; } else { THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ..."); } } bool next() // O(n) { int i = 0; while ((i < m_step) && !end()) { m_current = m_current->next; ++i; } return (i == m_step); } ~LinkList() // O(n) { clear(); } protected: struct Node : public Object { T value; Node *next; }; mutable struct : public Object { char reserved[sizeof (T)]; Node *next; }m_header; int m_length; int m_step; Node *m_current; Node *position(int i) const // O(n) { Node *ret = reinterpret_cast<Node*>(&m_header); for (int p=0; p<i; ++p) { ret = ret->next; } return ret; } virtual Node *create() // N(1) { return new Node(); } virtual void destroy(Node *pn) // N(1) { delete pn; } }; } #endif // LINKLIST_H
LinkList<int> 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->value() << endl; ??? } }
輸出:[隨機值]
18022592
緣由:current 指向的堆空間被釋放。
bool remove(int i) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { Node *current = position(i); Node *toDel = current->next; current->next = toDel->next; --m_length; destroy(toDel); } return ret; }
==>安全
bool remove(int i) override // O(n) { 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; }
[修復]LinkList.hide
#ifndef LINKLIST_H #define LINKLIST_H #include "List.h" #include "Exception.h" namespace DTLib { template <typename T> class LinkList : public List<T> { public: LinkList() { m_header.next = nullptr; m_length = 0; m_step = 0; m_current = nullptr; } bool insert(const T &e) override // O(n) { return insert(m_length, e); } bool insert(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i <= m_length)); if (ret) { Node *node = create(); if (node != nullptr) { Node *current = position(i); node->value = e; node->next = current->next; current->next = node; ++m_length; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ..."); } } return ret; } bool remove(int i) override // O(n) { 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; } bool set(int i, const T &e) override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { position(i)->next->value = e; } return ret; } T get(int i) const // O(n) { T ret; if (!get(i, ret)) { THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); } return ret; } bool get(int i, T &e) const override // O(n) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { e = position(i)->next->value; } return ret; } int find(const T &e) override // O(n) { int ret = -1; int i = 0; Node *node = m_header.next; while (node) { if (node->value == e) { ret = i; break; } else { node = node->next; ++i; } } return ret; } int length() const // O(1) { return m_length; } void clear() // O(n) { while (m_header.next) { Node *toDel = m_header.next; m_header.next = toDel->next; --m_length; destroy(toDel); } } bool move(int i, int step = 1) // O(n) { bool ret = ((0 <= i) && (i < m_length) && (step > 0)); if (ret) { m_current = position(i)->next; m_step = step; } return ret; } bool end() // O(1) { return (m_current == nullptr); } T current() // O(1) { if (!end()) { return m_current->value; } else { THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ..."); } } bool next() // O(n) { int i = 0; while ((i < m_step) && !end()) { m_current = m_current->next; ++i; } return (i == m_step); } ~LinkList() // O(n) { clear(); } protected: struct Node : public Object { T value; Node *next; }; mutable struct : public Object { char reserved[sizeof (T)]; Node *next; }m_header; int m_length; int m_step; Node *m_current; Node *position(int i) const // O(n) { Node *ret = reinterpret_cast<Node*>(&m_header); for (int p=0; p<i; ++p) { ret = ret->next; } return ret; } virtual Node *create() // N(1) { return new Node(); } virtual void destroy(Node *pn) // N(1) { delete pn; } }; } #endif // LINKLIST_H
ISSUE_4 StaticLinkList 是否須要提供析構函數?函數
int main() { StaticLinkList<int, 10> sll; for (int i=0; i<10; ++i) { sll.insert(i); } return 0; // 如何析構??? }
背景知識:
在構造函數和析構函數中,不會發生多態行爲,只會調用自身版本的成員函數(不管直接仍是間接調用)測試
知識點傳送門this
析構過程分析:
==> StaticLinkList 析構函數執行
==> LinkList 析構函數執行
==> LinkList<T>::clear() 函數執行
==> LinkList<T>::destroy() 函數執行
==> delete 非堆空間指針,行爲未定義spa
解決方案:爲StaicLinkList 提供析構函數
~StaticLinkList() // O(n) { this->clear(); }
析構過程分析:
==> StaticLinkList 析構函數執行
==> StaticLinkList<T>::clear() 函數執行
==> StaticLinkList<T>::destroy() 函數執行
[修復] 文件:StaticLinkList.h
#ifndef STATICLINKLIST_H #define STATICLINKLIST_H #include "LinkList.h" #include <iostream> using namespace std; namespace DTLib { template <typename T, int N> class StaticLinkList : public LinkList<T> { public: StaticLinkList() // O(n) { for (int i=0; i<N; ++i) { m_used[i] = 0; } } int capacity() // O(1) { return N; } ~StaticLinkList() // O(n) { this->clear(); } protected: //typedef typename LinkList<T>::Node Node; using Node = typename LinkList<T>::Node; struct SNode : public Node { void *operator new (unsigned int size, void *loc) { (void)size; return loc; } }; unsigned char m_space[N * sizeof(SNode)]; char m_used[N]; Node *create() override // O(n) { SNode *ret = nullptr; for (int i=0; i<N; ++i) { if (m_used[i] == 0) { ret = reinterpret_cast<SNode*>(m_space) + i; ret = new(ret)SNode; m_used[i] = 1; break; } } return ret; } void destroy(Node *pn) override // O(n) { SNode *space = reinterpret_cast<SNode*>(m_space); SNode *psn = dynamic_cast<SNode*>(pn); for (int i=0; i<N; ++i) { if (psn == (space + i)) { m_used[i] = 0; pn->~Node(); break; } } } }; } #endif // STATICLINKLIST_H
No. 多維數組的本質:數組的數組!
main.cpp
#include <iostream> #include "DynamicArray.h" using namespace std; using namespace DTLib; int main() { DynamicArray< DynamicArray<int> > d; d.resize(3); for (int i=0; i<d.length(); ++i) { d[i].resize(i + 1); } for (int i=0; i<d.length(); ++i) { for (int j=0; j<d[i].length(); ++j) { d[i][j] = i + j; } } for (int i=0; i<d.length(); ++i) { for (int j=0; j<d[i].length(); ++j) { cout << d[i][j] << " "; } cout << endl; } return 0; }
輸出:
0 1 2 2 3 4
是軟件就有bug,所以須要不停的迭代升級,解決問題。
庫是一種特殊的軟件產品,也會存在各類bug,也須要迭代升級,解決問題。
只要代碼發生改動,就須要測試。
以上內容整理於狄泰軟件學院系列課程,請你們保護原創!