C擴展 C++回顧到入門

引言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++高玩的批評. 拜~~

相關文章
相關標籤/搜索