zoukankan      html  css  js  c++  java
  • C扩展 C++回顾到入门

    引言

      C扩展也称C++, 是一个复(za)杂(ji)优(ken)秀(die)的语言. 本文通过开发中常用C++方式
    来了解和回顾C++这么语言. C++看了较多的书但还是觉得什么都不会. 只能说自己还付出太少,哎.

    在引言部分我们先感受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
    ", log);
                exit(EXIT_FAILURE);
            }
    
            _wf = fopen(wf, "ab");
            if (NULL == _wf) {
                fclose(_log);
                fprintf(stderr, "fopen is error : %s
    ", 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头文件时候表示当前代码是开源的, 头文件和实现都在一起.

    并且不使用全局变量和全局函数.

    还有这段代码

        // 设置LogSimple为友员, 可以访问当前私有属性
        friend class LogSimple;
    
    ......
    
        
        // 只能在当前类和继承类中使用的单例对象, 这个只是声明
        static LogInit _li;

    是构建上层语言的 类的构造器. "只会在第一次使用这个类的时候构建这个对象". C++中通过技巧能够完成一切, 是一个强调技巧,强混乱约束的语言.

    测试代码如下 main.cpp

    #include "LogSimple.hpp"
    
    /*
     * 主函数, 测试简单的日志系统
     * 快速熟悉C++类的使用方法.
     */
    int main(void) {
    
        LogSimple *log;
        LogDebug debug;
        LogFatal fatal;
    
        // 简单测试
        log = &debug;
        log->Log("debug 日志测试!
    ");
    
        log = &fatal;
        log->Log("fatal 日志测试
    ");
    
        // 测试完毕
        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('
    ');
    
        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
    ", *iptr);
    
        // 使用 数组类型
        AutoPtr<abx> abs(10);
        printf("abs[6].c = %s
    ", 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
    ", *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
    ", 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("
    释放的数据如下:");
        while (!qds.empty()) {
            c = qds.front();
            printf("%f ", c);
    
            qds.pop();
        }
        putchar('
    ');
    
        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>
    ", strs[i-1], strs[i]);
            }
        }
    
        // 这里开始查找处理
        map<string, string>::iterator it = kvs.find("are");
        if (it != kvs.end())
            printf("找见了 %s => %s
    ", it->first.c_str(), it->second.c_str());
        else
            printf("没有找见 are => NULL
    ");
    
        // 全局输出
        puts("当前的数据内容如下:");
        for (it = kvs.begin(); it != kvs.end(); ++it) {
            printf("%s => %s
    ", it->first.c_str(), it->second.c_str());
        }
    
        system("pause");
        return 0;
    }

    运行结果

    到这里基本上C++ 语言中常用的语法规则, 基本都回顾熟悉完毕了. 后面随着开发, 慢慢了解突破. 最快的熟悉手段还是大量看专业书籍和敲代码. 

    后记

      错误是难免, 这里纯属回顾C++基础语法. 有问题随时交流, 接受任何C++高玩的批评. 拜~~

  • 相关阅读:
    POJ 3259 Wormholes【BellmanFord】
    POJ 2960 SNim【SG函数的应用】
    ZOJ 3578 Matrixdp水题
    HDU 2897 邂逅明下【bash博弈】
    BellmanFord 算法及其优化【转】
    【转】几个Java的网络爬虫
    thinkphp 反字符 去标签 自动加点 去换行 截取字符串 冰糖
    php 二维数组转 json文本 (jquery datagrid 数据格式) 冰糖
    PHP 汉字转拼音(首拼音,所有拼音) 冰糖
    设为首页与加入收藏 兼容firefox 冰糖
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5430930.html
Copyright © 2011-2022 走看看