美文网首页
现代C++改变了什么

现代C++改变了什么

作者: 老瓦在霸都 | 来源:发表于2020-10-14 14:01 被阅读0次

    C 是一门古老的语言, C++在为C 引入了面向对象和泛型,也引入了许多的复杂性,例如多重继承,模板的特化,等等。同时 C++ 本身除了 STL 标准库,缺少高质量的并发和网络软件包,每个C++程序员或多或少都造过大大小小的轮子。

    例如我就写过很多 string 处理的函数, 比如 Trim, LowerCase, UpperCase, 等等,还有基于引用计数的智能指针,包装 pthread 函数的线程类,林林总总,各种轮子都要自己和团队手工打造。有时有点成就感,可是显然也拖慢了开发速度,自己的轮子虽然够用,可是并非考虑周全,异常保护不够,经常会挖坑,也让别人踩坑。

    所以近年来,逐渐在项目中引入 boost 库来提高开发效率,也从 C++ 98 向 C++11, 14, 17, 乃至 C++ 20 过渡。

    在 C++ 11、14、17、20 中我们可以看到很多 boost library 的影子, 例如 boost::shared_ptr, boost::thread , boost::random, 等等。

    C++ 11 新特性

    改进的对象构造

    • 继承基类的构造函数
    • 默认的成员值
    • 委托构造函数
    • overwrite 关键字
    • final 关键字
    • 统一的初始化 {}

    其他语言增强

    • 基于范围的 for 循环
    • long long int 类型 (64位)
    • lambda 函数
    • 移动语义和右值引用
    • 强类型枚举: enum 增加了类型检查
    • 智能指针
    • 原始字符串字面量
    • 静态断言
    • 可变参数模板
    • 改善了右尖括号的处理

    新的关键字

    • auto 自动类型推导
    • constexpr: 表达式常量
    • decltype: 声明类型
    • nullptr: 空指针
    • thread_local: 线程局部存储

    对 C++ 库的增强

    • stl 容器的列表初始化
    • 随机库
    • 正则表达式库
    • 无序容器(hash)
    • 附加的算法
    • 元组模板
    • 其他的新类型: chrono(时间测量), ratio(有理数分数), complex(复数)

    作为一个 Java/C++ 都时常用到的老程序员,委托构造函数,智能指针,lambda 等许多东西都不新鲜,唯独需要重点提到的是右值。

    C++ 本来就已经很复杂了,为什么又要弄出来右值这个东西来烧脑呢? 原因还是在于对于压榨出高性能的需求,就象Linux 系统命令 cpmv, 原先的值拷贝好比 cp, 现在的右值用于移动语义好比 mv

    什么是右值,因为左值通常出现在赋值运算符的左边(也可能在右边),而右值一般出现在赋值运算符的右边。

    一句话,有名字的是左值,没名字的右值,就因为右值没有名字,它们都是一个临时的变量,对于它们的复制可以简化为移动,反正临时的无名变量没人会用到。

    最常用的例子是在函数中返回一个对象, C++11 之前如果对象比较大,最好用指针,而C++11之后适用移动语义,下面这样写就挺好。

    Command createCommand() {
      Command command;
      command.setName("doSomeThing");
      return command;
    }
    

    例子代码

    现代C++类的默认成员函数不仅仅只有构造,析构,拷贝构造函数和赋值运算符,还要加上移动构造函数和移动赋值运算符。例如:

    class Command
    {
    public:
        Command();
        Command(std::string name);
    
        Command(const Command& other); //拷贝构造函数
        Command& operator=(const Command& other);//赋值运算符
    
        Command(Command&& other); //移动构造函数
        Command& operator=(Command&& other); //移动赋值运算符
    
        virtual ~Command();
    
        void setName(const std::string& name);
        void setParameter(const std::string& name, const std::string& value);
        void setData(const uint8_t* pData, size_t length);
    
        friend std::ostream& operator<<(std::ostream&, const Command& obj);
    
    private:
        std::string m_name = "";
        std::map<std::string, std::string> m_parameters;
        size_t m_length = 0;
        uint8_t* m_data = nullptr;
    };
    

    具体实现为

    Command::Command():Command("") {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". construct: " << m_name << "@" <<this;    
    }
    
    Command::Command(string name):m_name(name) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". construct: " << m_name << "@" <<this;    
    }
    
    Command::Command(const Command& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". copy construct: " << m_name << "@" <<this;
        m_name = other.m_name;
        for (const auto& kv : other.m_parameters) {
            BOOST_LOG_TRIVIAL(trace) << kv.first << " has value " << kv.second;
            m_parameters.insert(kv);
        }
        std::copy(other.m_data, other.m_data + m_length, m_data);
    }
    
    Command& Command::operator=(const Command& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". operator =: " << other.m_name;
        if (this != &other)
        {
            this->m_name = other.m_name;
            for (const auto& kv : other.m_parameters) {
                std::cout << kv.first << " has value " << kv.second << std::endl;
                m_parameters.insert(kv);
            }
            // Free the existing resource.
            if(m_data) {
                delete[] m_data;
            }
            m_length = other.m_length;
            m_data = new uint8_t[m_length];
            std::copy(other.m_data, other.m_data + m_length, m_data);
        }
        return *this;
    }
    
    Command::Command(Command&& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< " move copy construct: " << other.m_name << "@" <<this;
        m_name = std::move(other.m_name);
        m_parameters = std::move(other.m_parameters);
    
        // Copy the data pointer and its length from the source object.
        m_data = other.m_data;
        m_length = other.m_length;
    
        // Release the data pointer from the source object so that
        // the destructor does not free the memory multiple times.
        other.m_data = nullptr;
        other.m_length = 0;
    
    }
    
    Command& Command::operator=(Command&& other) 
    {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< " move operator ==" << other.m_name;
       if (this != &other)
       {
          m_name = std::move(other.m_name);
          m_parameters = std::move(other.m_parameters);
          // Free the existing resource.
          delete[] m_data;
    
          // Copy the data pointer and its length from the
          // source object.
          m_data = other.m_data;
          m_length = other.m_length;
    
          // Release the data pointer from the source object so that
          // the destructor does not free the memory multiple times.
          other.m_data = nullptr;
          other.m_length = 0;
       }
       return *this;
    }
    
    Command::~Command() {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". destruct: " << m_name ;
    }
    
    
    void Command::setName(const string& name) {
        m_name = name;
    }
    void Command::setParameter(const string& name, const string& value) {
        m_parameters[name] = value;
    }
    
    void Command::setData(const uint8_t* pData, size_t length) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". setData: " << m_name;
        if(nullptr != m_data) {
            delete[] m_data;
        }
        
        m_length = length;
        m_data = new uint8_t[length];
        std::copy(pData, pData + length, m_data);
    }
    
    ostream& operator<<(ostream& os, const Command& obj)
    {
        os << obj.m_name;
        os << ": ";
        //for (map<string, string>::iterator it = obj.m_parameters.begin(); it != obj.m_parameters.end(); ++it)
        for (const auto& kv : obj.m_parameters)
        {
            os << kv.first << "=" << kv.second << endl;
        }
        for(size_t i=0; i< obj.m_length; ++i) {
            os << *(obj.m_data + i);
        }
        return os;
    }
    
    
    #ifndef RUN_EXAMPLE_H_
    #define RUN_EXAMPLE_H_
    
    #include <stdio.h>
    #include <stdint.h>
    
    #include <string>
    #include <map>
    #include <iostream>
    #include <memory>
    
    #include <unordered_map>
    #include <boost/log/trivial.hpp>
    #include <boost/program_options.hpp>
    #include <boost/core/noncopyable.hpp>
    #include <boost/assert.hpp>
    
    //old function pointer
    typedef int (*exam_func_ptr)(int argc, char** argv);
    //new function object
    typedef std::function<int(int, char**)> exam_func_t;
    
    
    template<typename T, typename... Ts>
    std::unique_ptr<T> my_make_unique(Ts&&... params) {
        return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
    }
    
    
    class Command
    {
    public:
        Command();
        Command(std::string name);
    
        Command(const Command& other);
        Command& operator=(const Command& other);
    
        Command(Command&& other);
        Command& operator=(Command&& other);
    
        virutal ~Command();
    
        void setName(const std::string& name);
        void setParameter(const std::string& name, const std::string& value);
        void setData(const uint8_t* pData, size_t length);
    
        friend std::ostream& operator<<(std::ostream&, const Command& obj);
    
    private:
        std::string m_name = "";
        std::map<std::string, std::string> m_parameters;
        size_t m_length = 0;
        uint8_t* m_data = nullptr;
    };
    
    class ExampleRunner: boost::noncopyable {
    public:
        ExampleRunner();
        //no need by noncopyable
        //ExampleRunner(const ExampleRunner& rhs);
        //ExampleRunner& operator=(const ExampleRunner& rhs);
    
        virtual ~ExampleRunner();
    
            void init();
    
        size_t size() const;
    
            void register_example(const std::string& name, const exam_func_t &exam);
    
            int execute_example(const std::string& name, int argc, char** argv) const;
    private:
        volatile uint32_t m_example_count = 0;
        std::unordered_map<std::string, exam_func_t> m_func_examples;        
    };
    
    
    #endif
    
    #include "run_example.h"
    
    using namespace std;
    namespace po = boost::program_options;
    
    extern int function_demo(int argc, char** argv);
    extern int lambda_demo(int argc, char* argv[]);
    extern int rvalue_demo(int argc, char* argv[]);
    extern int smart_ptr_demo(int argc, char** argv);
    
    const int CASE_COUNT =4;
    
    const char* usage = R"name(please specify example name:
        function_demo
        or lambda_demo
        or rvalue_demo
        or smart_ptr_demo
    )name";
    
    
    //C++11: delegate constructor
    Command::Command():Command("") {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". construct: " << m_name << "@" <<this;    
    }
    
    Command::Command(string name):m_name(name) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". construct: " << m_name << "@" <<this;    
    }
    
    Command::Command(const Command& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". copy construct: " << m_name << "@" <<this;
        m_name = other.m_name;
        for (const auto& kv : other.m_parameters) {
            BOOST_LOG_TRIVIAL(trace) << kv.first << " has value " << kv.second;
            m_parameters.insert(kv);
        }
        std::copy(other.m_data, other.m_data + m_length, m_data);
    }
    
    Command& Command::operator=(const Command& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". operator =: " << other.m_name;
        if (this != &other)
        {
            this->m_name = other.m_name;
            for (const auto& kv : other.m_parameters) {
                std::cout << kv.first << " has value " << kv.second << std::endl;
                m_parameters.insert(kv);
            }
            // Free the existing resource.
            if(m_data) {
                delete[] m_data;
            }
            m_length = other.m_length;
            m_data = new uint8_t[m_length];
            std::copy(other.m_data, other.m_data + m_length, m_data);
        }
        return *this;
    }
    
    Command::Command(Command&& other) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< " move copy construct: " << other.m_name << "@" <<this;
        m_name = std::move(other.m_name);
        m_parameters = std::move(other.m_parameters);
    
        // Copy the data pointer and its length from the source object.
        m_data = other.m_data;
        m_length = other.m_length;
    
        // Release the data pointer from the source object so that
        // the destructor does not free the memory multiple times.
        other.m_data = nullptr;
        other.m_length = 0;
    
    }
    
    Command& Command::operator=(Command&& other) 
    {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< " move operator ==" << other.m_name;
       if (this != &other)
       {
          m_name = std::move(other.m_name);
          m_parameters = std::move(other.m_parameters);
          // Free the existing resource.
          delete[] m_data;
    
          // Copy the data pointer and its length from the
          // source object.
          m_data = other.m_data;
          m_length = other.m_length;
    
          // Release the data pointer from the source object so that
          // the destructor does not free the memory multiple times.
          other.m_data = nullptr;
          other.m_length = 0;
       }
       return *this;
    }
    
    Command::~Command() {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". destruct: " << m_name ;
    }
    
    
    void Command::setName(const string& name) {
        m_name = name;
    }
    void Command::setParameter(const string& name, const string& value) {
        m_parameters[name] = value;
    }
    
    void Command::setData(const uint8_t* pData, size_t length) {
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__<< ". setData: " << m_name;
        if(nullptr != m_data) {
            delete[] m_data;
        }
        
        m_length = length;
        m_data = new uint8_t[length];
        std::copy(pData, pData + length, m_data);
    }
    
    ostream& operator<<(ostream& os, const Command& obj)
    {
        os << obj.m_name;
        os << ": ";
        //for (map<string, string>::iterator it = obj.m_parameters.begin(); it != obj.m_parameters.end(); ++it)
        for (const auto& kv : obj.m_parameters)
        {
            os << kv.first << "=" << kv.second << endl;
        }
        for(size_t i=0; i< obj.m_length; ++i) {
            os << *(obj.m_data + i);
        }
        return os;
    }
    
    
    ExampleRunner::ExampleRunner(): m_example_count(0),m_func_examples() {
        BOOST_LOG_TRIVIAL(trace)<<"* ExampleRunner construct: " ;    
    }
    
    ExampleRunner::~ExampleRunner() {
        BOOST_LOG_TRIVIAL(trace)<<"* ExampleRunner destruct: ";
    }
    
    void ExampleRunner::init() {
        register_example("function_demo", function_demo);
        register_example("smart_ptr_demo", smart_ptr_demo);
        register_example("lambda_demo", lambda_demo);
        register_example("rvalue_demo", rvalue_demo);
    }
    
    void ExampleRunner::register_example(const string& name, const exam_func_t &exam)
    {
        m_example_count++;
        m_func_examples[name] = exam;
    }
    
    int ExampleRunner::execute_example(const string& name, int argc, char** argv) const
    {
        auto it = m_func_examples.find(name);
        if(it != m_func_examples.end()) {
            BOOST_LOG_TRIVIAL(trace) << "execute "<< it->first;
            exam_func_t func = it->second;
            return func(argc, argv);
        }
        BOOST_LOG_TRIVIAL(trace) << "not registered "<< name;
        return -1;
    }
    
    
    size_t ExampleRunner::size() const {
        return m_func_examples.size();
    }
    
    int main(int argc, char** argv)
    {
        unique_ptr<ExampleRunner> runner = my_make_unique<ExampleRunner>();
        runner->init();
        BOOST_ASSERT_MSG(runner->size()==CASE_COUNT, "example count should be 2");
        //c++11 R"raw string"
        po::options_description desc("Allowed options:");
        
        desc.add_options()
            ("help,h", "produce help message")
            ("name,n", po::value<string>(), usage)
        ;
    
        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);    
    
        if (vm.count("help")) {
            BOOST_LOG_TRIVIAL(trace) << desc << "\n";
            return 1;
        }
    
        if (vm.count("name")) {
            BOOST_LOG_TRIVIAL(trace) << "* example name is "<< vm["name"].as<string>() << ".";
            runner->execute_example(vm["name"].as<string>(), argc, argv);
        } else {
            BOOST_LOG_TRIVIAL(trace) << "example name was not set.";
            BOOST_LOG_TRIVIAL(trace) << desc ;
        }
    
        return 0;
    }
    
    

    关于智能指针和移动拷贝的例子

    #include "run_example.h"
    
    using namespace std;
    
    
    int rvalue_demo(int argc, char* argv[])
    {
        //move constructor
        Command cmd1(Command("c1"));
        //copy constructor
        Command cmd2 = cmd1;
    
        shared_ptr<Command> sharedPtr = make_shared<Command>("update");
        sharedPtr->setParameter("user", "alice");
        uint8_t nBytes[3]   = { 0x00,0x01,0x02 };
        sharedPtr->setData(nBytes, 3);
        
        shared_ptr<Command> sharedPtr2 = sharedPtr;
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << ". sharedPtr=" << sharedPtr.get() << ", "<< sharedPtr.use_count() <<", command="<< *sharedPtr;
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << ". sharedPtr2=" << sharedPtr2.get() << ", "<< sharedPtr2.use_count() <<", command="<< *sharedPtr2;
    
        return 0;
    }
    
    int smart_ptr_demo(int argc, char* argv[])
    {
    
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << " -- unique_ptr --";
        //unique_ptr<Command> ptr = unique_ptr<Command>(new Command());
        unique_ptr<Command> uniquePtr = my_make_unique<Command>();
        uniquePtr->setName("create");
        uniquePtr->setParameter("user", "walter");
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << ". unique pointer=" << uniquePtr.get() <<", command="<< *uniquePtr;
    
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << ". -- shared_ptr --";
    
        shared_ptr<Command> sharedPtr = make_shared<Command>();
        sharedPtr->setName("retrieve");
        sharedPtr->setParameter("user", "walter");
        BOOST_LOG_TRIVIAL(trace) << "line " <<__LINE__ << ". shared pointer=" << sharedPtr.get() << ", "<< sharedPtr.use_count() <<", command="<< *sharedPtr;
    
    
        return 0;
    }
    
    

    运行结果

    ./bin/run_example -n rvalue_demo
    [2020-10-07 13:59:40.303527] [0x0000000107d9edc0] [trace]   * ExampleRunner construct:
    [2020-10-07 13:59:40.304168] [0x0000000107d9edc0] [trace]   * example name is rvalue_demo.
    [2020-10-07 13:59:40.304179] [0x0000000107d9edc0] [trace]   execute rvalue_demo
    [2020-10-07 13:59:40.304198] [0x0000000107d9edc0] [trace]   line 27. construct: c1@0x7ffeed644df0
    [2020-10-07 13:59:40.304204] [0x0000000107d9edc0] [trace]   line 61 move copy construct: c1@0x7ffeed644db0
    [2020-10-07 13:59:40.304210] [0x0000000107d9edc0] [trace]   line 100. destruct:
    [2020-10-07 13:59:40.304234] [0x0000000107d9edc0] [trace]   line 31. copy construct: @0x7ffeed644df0
    [2020-10-07 13:59:40.304255] [0x0000000107d9edc0] [trace]   line 27. construct: update@0x7faa5c407408
    [2020-10-07 13:59:40.304264] [0x0000000107d9edc0] [trace]   line 112. setData: update
    [2020-10-07 13:59:40.304281] [0x0000000107d9edc0] [trace]   line 19. sharedPtr=0x7faa5c407408, 2, command=update: user=alice
    
    [2020-10-07 13:59:40.304289] [0x0000000107d9edc0] [trace]   line 20. sharedPtr2=0x7faa5c407408, 2, command=update: user=alice
    
    [2020-10-07 13:59:40.304295] [0x0000000107d9edc0] [trace]   line 100. destruct: update
    [2020-10-07 13:59:40.304315] [0x0000000107d9edc0] [trace]   line 100. destruct: c1
    [2020-10-07 13:59:40.304319] [0x0000000107d9edc0] [trace]   line 100. destruct: c1
    [2020-10-07 13:59:40.304327] [0x0000000107d9edc0] [trace]   * ExampleRunner destruct:
    

    参考资料

    相关文章

      网友评论

          本文标题:现代C++改变了什么

          本文链接:https://www.haomeiwen.com/subject/dxetyktx.html