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

  • 相关阅读:
    第一个shell脚本(bash脚本)
    通过Nginx反向代理配置/.well-known/pki-validation/fileauth.txt步骤实例
    Windows注册表中修改UAC(用户账号控制)及批处理脚本
    ssh免密码认证
    ansible快速文档
    devops基础06--maven pom.xml
    java开发基础
    devops基础05--nexus
    devops基础04--maven
    centos7下安装postgresql13
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5430930.html
Copyright © 2011-2022 走看看