引言java
C擴展也稱C++, 是一個復(za)雜(ji)優(ken)秀(die)的語言. 本文經過開發中經常使用C++方式
來了解和回顧C++這麼語言. C++看了較多的書但仍是以爲什麼都不會. 只能說本身還付出太少,哎.ios
在引言部分咱們先感覺C++類的設計. 數組
有個以下需求, 設計一個簡單的日誌系統. 先看下面 LogSimple.hpp多線程
#ifndef _HPP_LOGSIMPLE #define _HPP_LOGSIMPLE #include <iostream> using namespace std; // 特殊技巧構建類構造器 class LogInit { // 設置LogSimple爲友員, 能夠訪問當前私有屬性 friend class LogSimple; // _log寫普通文件, _wf寫級別高的文件 static FILE* _log; static FILE* _wf; // 私有的構造器, 證實這個類是私有類 LogInit() { const char* log = "rpc.log"; const char* wf = "rpc.log.wf"; _log = fopen(log, "ab"); if (NULL == _log) { fprintf(stderr, "fopen is error : %s\n", log); exit(EXIT_FAILURE); } _wf = fopen(wf, "ab"); if (NULL == _wf) { fclose(_log); fprintf(stderr, "fopen is error : %s\n", wf); exit(EXIT_FAILURE); } } // 析構打開的句柄 ~LogInit() { fclose(_wf); fclose(_log); } }; // 定義靜態變量 FILE* LogInit::_log = NULL; FILE* LogInit::_wf = NULL; // 基礎的日誌系統 class LogSimple { protected: // 只能在當前類和繼承類中使用的單例對象, 這個只是聲明 static LogInit _li; protected: // 打印普通訊息 void LogWrite(string msg) { fprintf(LogSimple::_li._log, msg.c_str()); } // 打印等級高信息 void WfWrite(string msg) { fprintf(LogSimple::_li._wf, msg.c_str()); } public: virtual void Log(string msg) = 0; }; // 定義在 LogSimple 中聲明的靜態量 LogInit LogSimple::_li; // Debug 模式日誌 class LogDebug : public LogSimple { public: // 重寫Log輸出內容 void Log(string msg) { #if defined(_DEBUG) this->LogWrite(msg); #endif } }; // Debug 模式日誌 class LogFatal : public LogSimple { public: // 重寫Log輸出內容 void Log(string msg) { this->LogWrite(msg); this->WfWrite(msg); } }; #endif // !_HPP_LOGSIMPLE
這裏使用了 *.hpp 文件,也稱C++的充血模型. 當使用 hpp頭文件時候表示當前代碼是開源的, 頭文件和實現都在一塊兒.函數
而且不使用全局變量和全局函數.測試
還有這段代碼ui
// 設置LogSimple爲友員, 能夠訪問當前私有屬性 friend class LogSimple; ...... // 只能在當前類和繼承類中使用的單例對象, 這個只是聲明 static LogInit _li;
是構建上層語言的 類的構造器. "只會在第一次使用這個類的時候構建這個對象". C++中經過技巧可以完成一切, 是一個強調技巧,強混亂約束的語言.this
測試代碼以下 main.cppspa
#include "LogSimple.hpp" /* * 主函數, 測試簡單的日誌系統 * 快速熟悉C++類的使用方法. */ int main(void) { LogSimple *log; LogDebug debug; LogFatal fatal; // 簡單測試 log = &debug; log->Log("debug 日誌測試!\n"); log = &fatal; log->Log("fatal 日誌測試\n"); // 測試完畢 puts("測試完畢!"); system("pause"); return 0; }
運行結果線程
生成日誌文件圖
再扯一點, C++類中靜態變量, 分兩步構造,先在類中聲明, 再在外面定義分配實際空間. 好,這裏關於C++的類回顧完畢.
前言
前言部分回顧一下C++中模板用法.
開始先回顧瞭解函數模板, 看下面測試文件 main.cpp
#include <iostream> using namespace std; /* * 快速排序遞歸核心, 當前是從小到大排序 */ template <typename T> static void _sortquick(T a[], int si, int ei) { // 遞歸結束條件 if (si >= ei) return; int low = si, high = ei; T axle = a[low]; while (low < high) { // 找最右邊不合適點 while (low < high && a[high] > axle) --high; if (low >= high) break; a[low++] = a[high]; //找最左邊不合適點 while (low < high && a[low] < axle) ++low; if (low >= high) break; a[high--] = a[low]; } // 分界點找好了, 歸位 此時low == high a[low] = axle; //新一輪遞歸 _sortquick(a, si, low - 1); _sortquick(a, high + 1, ei); } // 包裝對外使用的快排接口 template<typename T> inline void sortquick(T a[], int len) { _sortquick(a, 0, len - 1); } /* * 這裏溫故函數模板,以快速排序爲例 */ int main(void) { // 開始測試, 模板函數 int a[] = {5, 6, 1, 2, 4, 5, 1, 4, 14, 1, 17 }; // 開始調用測試 是 sortquick<int> 自動推導 sortquick(a, sizeof(a) / sizeof(*a)); puts("排序後數據爲:"); for (int i = 0; i < sizeof(a) / sizeof(*a); ++i) printf("%d ", a[i]); putchar('\n'); system("pause"); return 0; }
經過 template<typename T> 構建一個模板的快排函數. 測試結果以下
再來回顧一下 模板類用法 咱們構建一個 簡單的 智能指針類 AutoPtr.hpp
#ifndef _HPP_AUTOPTR #define _HPP_AUTOPTR #include <cstring> #include <cstdlib> /** *簡單的智能指針,支持建立基本類型 基本類型數組 *支持智能管理對象類型,對象數組類型 *不容許賦值構造,複製構造,不容許new建立 */ template<typename T> class AutoPtr { T *_ptr; unsigned _len; AutoPtr<T>(const AutoPtr<T> &autoPtr); AutoPtr<T> &operator=(const AutoPtr<T> &autoPtr); void *operator new(unsigned s); public: AutoPtr(unsigned len = 1U) { this->_len = len; this->_ptr = !len ? NULL : (T*)calloc(len, sizeof(T)); } ~AutoPtr(void) { for (unsigned u = this->_len; u > 0U; --u) this->_ptr[u - 1].~T();//delete的本質 free(this->_ptr); } inline T& operator*(void) const { return *this->_ptr; } inline T* operator->(void) const { return this->_ptr; } inline T& operator[](unsigned idx) const { return this->_ptr[idx]; } inline T* operator+(unsigned idx) const { return this->_ptr + idx; } //獲取智能託管資源的長度,在數組中有用 inline unsigned size(void) { return this->_len; } }; #endif // !_HPP_AUTOPTR
測試代碼以下 main.cpp
#include <iostream> #include "AutoPtr.hpp" using namespace std; struct abx { int a; float b; char *c; }; /* * 這裏將處理 泛型類的使用講解 * 泛型仍是在開發中少用.這裏只是初級熟悉篇. */ int main(void) { // 先使用基礎的用法 AutoPtr<int> iptr; *iptr = 666; printf("*iptr = %d\n", *iptr); // 使用 數組類型 AutoPtr<abx> abs(10); printf("abs[6].c = %s\n", abs[6].c); system("pause"); return 0; }
演示結果
經過上面兩個例子, 練習一下基本熟悉泛型語法簡易用法了.高級的用法, 那還得春夏秋冬......
正文
這裏簡單講解STL中開發中用到的容器類.使用一些簡單例子,方便上手使用.
先看list 鏈表使用案子
一樣經過代碼開始 main.cpp, 經過list處理隨機業務.
#include <iostream> #include <cassert> #include <ctime> #include <list> using namespace std; /* * 主函數 - 熟悉STL list 用法 * 業務需求以下: * 有一堆這樣數據 * 標識 權重 * 1 100 * 2 200 * 3 100 * ... ... * 須要隨機出一個數據. */ class RandGoods { list<int> idxs; //存全部索引的 list<int> weights; //存全部權重的 int sum; //計算總的權重和 public: RandGoods(void) { this->sum = 0; // 初始化隨機種子 srand((unsigned)time(NULL)); } /* * 添加數據 */ void Add(int idx, int weidth) { // 簡單檢測一下參數 assert(idx>=0 && weidth > 0); this->idxs.push_front(idx); this->weights.push_front(weidth); this->sum += weidth; } // 獲得一個隨機數據 int Get(void) { int ns = 0; int rd = rand() % sum; int len = this->weights.size(); list<int>::iterator it = this->idxs.begin(); list<int>::iterator wt = this->weights.begin(); while (wt != this->weights.end()) { ns += *wt; if (ns > rd) return *it; ++it; ++wt; } return -1; } // 輸出全部數據 void Print(void) { list<int>::iterator it = this->idxs.begin(); list<int>::iterator wt = this->weights.begin(); puts("當前測試數據以下:"); while (wt != this->weights.end()) { printf("%3d %3d\n", *it, *wt); ++it; ++wt; } } }; /* * 溫故 list用法, C++ STL 沒有上層語言封裝的好用 */ int main(void) { // 隨機對象 RandGoods rg; int len = rand() % 20 + 5; // 返回是 [5, 24] //添加數據 for (int i = 0; i < len; ++i) { int weight = rand() % 200 + 1; rg.Add(i, weight); } // 這裏測試 獲得數據 rg.Print(); // 獲得一個數據 int idx = rg.Get(); printf("獲得隨機物品索引:%d\n", idx); system("pause"); return 0; }
對於STL 庫有不少功能, 這裏就是最簡單的使用方式. 工做中須要用到高級的用法, 能夠及時查. 關鍵是有思路.
演示結果
C++ 的list 沒有 java和C#的List好用. 差距太大. 或者說STL相比上層語言提供的容器, 顯得不那麼天然. 估計是C++是開創者,
後面的語言知道坑在那, 簡化創新了. 也能夠用vector可變數組代替list. 若是在C中直接用語法層提供的可變數組 int max = 10; int a[max];
在棧上聲明可變數組就能夠了.
再看queue 隊列使用方式
關於stl 容器用法都是比較基礎例子, 重點能用, 高級的須要看專門介紹的書籍. 關於隊列底層庫中經常使用. 和多線程一塊兒配合.
流程很繞, 這裏簡單寫個容易的例子以下main.cpp
#include <iostream> #include <queue> using namespace std; /* * 這裏使用 queue隊列, 簡單使用瞭解 * 最簡單的生產後, 直接消耗 */ int main(void) { queue<double> qds; int i, len = rand() % 20 + 5; double c; int a, b; puts("生產的數據以下:"); // 先生產 隊列是尾巴插, 頭出來 for (i = 0; i < len; ++i) { a = rand(); b = rand(); if (a >= b) c = a + 1.0 * b / a; else c = (double)-b - 1.0 * a / b; // 隊列中添加數據 printf("%f ", c); qds.push(c); } puts("\n釋放的數據以下:"); while (!qds.empty()) { c = qds.front(); printf("%f ", c); qds.pop(); } putchar('\n'); system("pause"); return 0; }
運行截圖以下
注意的是C++隊列是尾查頭出.
後看map 鍵值對使用例子
先看 main.cpp
#include <iostream> #include <map> #include <string> using namespace std; /* * 這裏是使用 map. 簡單的熟悉map的使用方法 */ int main(void) { map<string, string> kvs; const char* strs[] = { "Sweet", "are", "the", "uses", "of", "adversity", "Knowledge", "is", "one", "thing", "but", "faith", "is", "another" }; // 先添加數據 int i; pair<map<string, string>::iterator, bool> pit; for (i = 1; i < sizeof(strs) / sizeof(*strs); ++i) { pit = kvs.insert(pair<string, string>(strs[i - 1], strs[i])); if (!pit.second) { printf("插入失敗<%s,%s>\n", strs[i-1], strs[i]); } } // 這裏開始查找處理 map<string, string>::iterator it = kvs.find("are"); if (it != kvs.end()) printf("找見了 %s => %s\n", it->first.c_str(), it->second.c_str()); else printf("沒有找見 are => NULL\n"); // 全局輸出 puts("當前的數據內容以下:"); for (it = kvs.begin(); it != kvs.end(); ++it) { printf("%s => %s\n", it->first.c_str(), it->second.c_str()); } system("pause"); return 0; }
運行結果
到這裏基本上C++ 語言中經常使用的語法規則, 基本都回顧熟悉完畢了. 後面隨着開發, 慢慢了解突破. 最快的熟悉手段仍是大量看專業書籍和敲代碼.
後記
錯誤是不免, 這裏純屬回顧C++基礎語法. 有問題隨時交流, 接受任何C++高玩的批評. 拜~~