參考前文傳送門:
C++解析(26):函數模板與類模板
C++解析(20):智能指針與類型轉換函數
C++解析(28):異常處理html
數據結構課程的特色:ios
數據結構課程並不關注數據元素的具體類型!算法
如何爲數據結構的學習選擇合適的語言?
支持泛型編程的語言最適合數據結構課程的學習?編程
泛型編程的概念——不考慮具體數據類型的編程方式
數據結構
C++中的函數模板:架構
函數模板的語法規則:ide
函數模板的使用:函數
C++中的類模板:學習
類模板的應用:測試
(使用QtCreator建立Template項目)
示例——使用函數模板和類模板:
#include <iostream> using namespace std; template <typename T> void Swap(T& a, T& b) { T t = a; a = b; b = t; } template <typename T> class Op { public: T process(T v) { return v * v; } }; int main() { int a = 2; int b = 1; Swap(a, b); cout << "a = " << a << ", " << "b = " << b << endl; double c = 0.01; double d = 0.02; Swap<double>(d, c); cout << "c = " << c << ", " << "d = " << d << endl; Op<int> opInt; Op<double> opDouble; cout << "5 * 5 = " << opInt.process(5) << endl; cout << "0.3 * 0.3 = " << opDouble.process(0.3) << endl; return 0; }
運行結果爲:
a = 1, b = 2 c = 0.02, d = 0.01 5 * 5 = 25 0.3 * 0.3 = 0.09
內存泄漏(臭名昭著的Bug):
當代C++軟件平臺中的智能指針:
智能指針的設計方案:
(使用QtCreator建立StLib項目)
示例——實現智能指針類模板SmartPointer.h:
#ifndef SMARTPOINTER_H #define SMARTPOINTER_H namespace StLib { template <typename T> class SmartPointer { protected: T* m_pointer; public: SmartPointer(T* p = NULL) { m_pointer = p; } SmartPointer(const SmartPointer<T>& obj) { m_pointer = obj.m_pointer; const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; } SmartPointer<T>& operator= (const SmartPointer<T>& obj) { if( this != &obj ) { delete m_pointer; m_pointer = obj.m_pointer; const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; } return *this; } T* operator-> () { return m_pointer; } T& operator* () { return *m_pointer; } bool isNull() { return (m_pointer == NULL); } T* get() { return m_pointer; } ~SmartPointer() { delete m_pointer; } }; } #endif // SMARTPOINTER_H
#include <iostream> #include "SmartPointer.h" using namespace std; using namespace StLib; class Test { public: Test() { cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; } }; int main() { SmartPointer<Test> sp = new Test(); SmartPointer<Test> nsp; nsp = sp; cout << sp.isNull() << endl; cout << nsp.isNull() << endl; return 0; }
運行結果爲:
Test() 1 0 ~Test()
智能指針的使用軍規——只能用來指向堆空間中的對象或者變量
C++內置了異常處理的語法元素try ... catch ...:
C++經過throw語句拋出異常信息:
throw拋出的異常必須被catch處理:
未被處理的異常會順着函數調用棧向上傳播,直到被處理爲止,不然程序將中止執行。
同一個try語句能夠跟上多個catch語句:
異常處理的匹配規則:
(使用QtCreator建立Exception項目)
示例——異常類型匹配:
#include <iostream> using namespace std; double divide(double a, double b) { const double delta = 0.000000000000001; double ret = 0; if( !((-delta < b) && (b < delta)) ) { ret = a / b; } else { throw 0; // 產生除 0 異常 } return ret; } void Demo1() { try { throw 'c'; } catch(int i) { cout << "catch(int i)" << endl; } catch(double d) { cout << "catch(double d)" << endl; } catch(char c) { cout << "catch(char c)" << endl; } } void Demo2() { throw 0.0001; // "HelloWorld"; // const char* } int main() { cout << "main() begin" << endl; try { double c = divide(1, 1); cout << "c = " << c << endl; } catch(...) { cout << "Divided by zero..." << endl; } Demo1(); try { Demo2(); } catch(char* c) { cout << "catch(char* c)" << endl; } catch(const char* cc) { cout << "catch(const char* cc)" << endl; } catch(...) { cout << "catch(...)" << endl; } cout << "main() end" << endl; return 0; }
運行結果爲:
main() begin c = 1 catch(char c) catch(...) main() end
C++中的異常處理:
(根據賦值兼容性原則:子類的異常對象能夠被父類的catch語句塊抓住。)
現代C++庫中必然包含充要的異常類族
異常類是數據結構類所依賴的「基礎設施」!
異常類功能定義:
異常類中的接口定義:
(在StLib中實現異常類族)
示例1——建立異常類族Exception類:
實現Exception.h
#ifndef EXCEPTION_H #define EXCEPTION_H using namespace std; namespace StLib { #define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__)) class Exception { protected: char* m_message; char* m_location; void init(const char* message, const char* file, int line); public: Exception(const char* message); Exception(const char* file, int line); Exception(const char* message, const char* file, int line); Exception(const Exception& e); Exception& operator= (const Exception& e); virtual const char* message() const; virtual const char* location() const; virtual ~Exception(); }; } #endif // EXCEPTION_H
實現Exception.cpp
#include "Exception.h" #include <cstring> #include <cstdlib> namespace StLib { void Exception::init(const char* message, const char* file, int line) { /* message指向的字符串有可能在棧上,有可能在堆空間,還有可能在全局數據區 * strdup()將字符串複製一份到堆空間中 * file:發生異常的文件名 * line:發生異常的行號 * m_location的長度加2,一個給":",一個給"\0" */ m_message = strdup(message); if( file != NULL ) { 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 = NULL; } } Exception::Exception(const char *message) { init(message, NULL, 0); } Exception::Exception(const char* file, int line) { init(NULL, file, line); } Exception::Exception(const char* message, const char* file, int line) { init(message, file, line); } 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() { free(m_message); free(m_location); } }
main.cpp測試
#include <iostream> #include "Exception.h" using namespace std; using namespace StLib; int main() { try { THROW_EXCEPTION(Exception, "test"); // ==>throw Exception("test", __FILE__, __LINE__); } catch(const Exception& e) { cout << "catch(const Exception& e)" << endl; cout << e.message() << endl; cout << e.location() << endl; } return 0; }
運行結果爲:
catch(const Exception& e) test ..\StLib\main.cpp:11
(在StLib中實現異常類族)
示例2——實現ArithmeticException.h:
#ifndef EXCEPTION_H #define EXCEPTION_H using namespace std; namespace StLib { #define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__)) class Exception { protected: char* m_message; char* m_location; void init(const char* message, const char* file, int line); public: Exception(const char* message); Exception(const char* file, int line); Exception(const char* message, const char* file, int line); Exception(const Exception& e); Exception& operator= (const Exception& e); virtual const char* message() const; virtual const char* location() const; virtual ~Exception() = 0; }; class ArithmeticException : public Exception { public: ArithmeticException() : Exception(0) { } ArithmeticException(const char* message) : Exception(message) { } ArithmeticException(const char* file, int line) : Exception(file, line) { } ArithmeticException(const char* message, const char* file, int line) : Exception(message, file, line) { } ArithmeticException(const ArithmeticException& e) : Exception(e) { } ArithmeticException& operator= (const ArithmeticException& e) { Exception::operator=(e); return *this; } }; } #endif // EXCEPTION_H
main.cpp測試
#include <iostream> #include "Exception.h" using namespace std; using namespace StLib; int main() { try { THROW_EXCEPTION(ArithmeticException, "test"); } catch(const ArithmeticException& e) { cout << "catch(const ArithmeticException& e)" << endl; cout << e.message() << endl; cout << e.location() << endl; } catch(const Exception& e) { cout << "catch(const Exception& e)" << endl; cout << e.message() << endl; cout << e.location() << endl; } return 0; }
運行結果爲:
catch(const ArithmeticException& e) test ..\StLib\main.cpp:11
(剩下的四個子類的實現相似於ArithmeticException類。)
設計原則:
在可複用代碼庫設計時,儘可能使用面向對象技術進行架構,儘可能使用異常處理機制分離正常邏輯和異常邏輯。