c++基础提高

0. 泛型编程

  • c++编程范式之一

  • 强类型程序设计

    • 参与运算的所有对象的类型在编译时就已确定,并且编译程序会进行严格地类型检查
    • 解决强类型程序设计的严格性和灵活性的冲突

      宏函数重载函数模板

  • 模板

    实现代码复用,又称为代码生成器

    • 实现原理:

      • 将类型参数化
      • 模板参数推导
    • 编译器对于模板的推导,需要进行 显示实例化 或者 隐式实例化 才能正常进行

    • 函数模板

      • 若已有实例化的模板函数,则编译器不会进行推导

      • 函数模板之间可以进行重载

      • 函数模板参数列表 template<typename T, …>

      • 实例化

        • 隐式实例化
        • 显式实例化

          需在"<>"中指定,不能为空

  • 模板参数类型

    • 类型参数

      关键字class/typename进行修饰

    • 非类型参数/常量表达式

      与宏常量、const常量的区别:

      • 宏常量、const常量在定义之后就已经确定了
      • 而非类型参数可以在实例化时进行显式指定
    • 模板的实例化

      • 完全实例化

        只是对于 通用模板实例化 的一种 特定类型 的实例化,仍然是一个模板

        • 完全实例化之前必须要有通用模板实例化
      • 部分实例化/偏实例化

    • 类模板成员函数的实例化

      • 默认情况下,一个类模板的成员函数只有当程序中使用其时才进行实例化

      • 对于类模板中的类型成员以及普通成员的区分

        • 通过作用域限定符::访问到的成员是普通成员
        • 若要访问类型成员,需要关键字typename声明
      • 对于类模板的静态成员对象,除了所必要的初始化以外,还要注意只有在使用了该对象成员后,其初始化才会进行

    • 可变模板参数

      template<class Type>
      void printAll(Type t) {
          cout << t << endl;
      }
      template<class Type, class...Args>
      void printAll(Type t, Args... args) {
      #if 1
          cout << t << endl;
          printAll(args...);
      #else
          int arr[] = { (printAll(args),0)... };
      #endif
      }
      
      • 模板参数包

        template<class...Args>

      • 函数参数包

        ReturnType FuncName(Args... args)

      • 打包

        … 位于参数包之前

      • 解包

        … 位于参数包之后

      • 求取可变参数的个数

        sizeof...


1. 移动语义

提出的目的:

  • 为了解决临时对象不必要的资源浪费
  • 为了充分利用临时对象申请的资源而进行的资源转移
  • 右值引用

    当 const引用 作为函数形参时,无法识别出绑定的是左值还是右值,因此c++11引入 右值引用进行转移操作

    • 转移操作的执行时机: 当传递的是一个右值时
    • 右值引用只能绑定到右值,不能绑定到左值
    • 为了使得左值也可以进行绑定,需要进行 std::move() 函数 进行类型转换
  • 在移动操作之后,移动源对象必须保持有效、可析构的状态,但对其值不能做任何假设

  • 具有移动语义的函数要优于 具有复制控制语义的函数

  • 编译器不会默认合成具有移动语义的函数(移动构造函数、移动赋值运算符函数)

  • 定义了具有移动语义函数的类,如果不定义复制控制函数,则默认是删除的

  • 只有当一个类没有定义自己的拷贝构造函数时,如果类的所有非静态数据成员都可移动时,编译器才会为其合成一个移动构造函数

    • 编译器可以移动 内置类型
    • 对于一个成员对象所对应的类定义了移动构造函数,则该数据成员也可移动
  • 对于移动操作,如果不抛出异常,则将应其声明为 noexcept ;否则,编译器会认为其有可能抛出异常,从而执行一些额外的操作

    • 对于vector自动扩容而言,当执行push_back操作时,会申请一个新的堆空间,并将原堆空间中的内容拷贝至新的堆空间
    • 若定义了一个不抛出异常的移动操作,为了使得vector自动扩容时使用该移动操作,则需将该移动操作声明为noexcept的,否则,编译器会认为其有可能抛出异常,因此会调用复制控制函数
  • 当类中定义了移动构造函数以及拷贝构造函数之后,拷贝构造函数发生的时机发生了变化:

    • 在函数调用返回一个类对象时
      • 若返回的是一个 即将被销毁的对象,则调用 移动构造函数
      • 若返回的是一个 持久化的对象,则调用 拷贝构造函数

2. 资源管理

在c语言中,资源回收是靠程序员来确保的,并不能保证资源100%被回收

2.1 RAII技术

Resource Acquisition Is Initialization

  • 本质特征

    利用栈对象的生命周期来管理资源

  • 特性

    • 栈对象创建时分配资源
    • 栈对象销毁时回收资源
    • 还定义了一些 用于访问资源的方法
    • 一般情况下,不允许复制

2.2 智能指针

  • auto_ptr

    • c++0x版本,c++17中弃用

      auto_ptr(auto_ptr&); 该函数底层实现的是移动语义

    • release

    • reset

    • get

  • unique_ptr

    • 表达对象语义,即不能复制、赋值

      表达对象语义的方法:

      • 复制控制函数 私有化
      • 复制控制函数 删除
      • 继承一个对象语义的类
        unique_ptr底层实现采用该方法
    • 提供了移动构造函数

    • 独享资源的所有权

  • shared_ptr

    • 表达值语义

    • 共享资源所有权

      实现:引用计数

    • 若一个堆类对象被托管给 shared_ptr 智能指针,那么在该类中为了使用其对应的shared_ptr,需要继承enable_shared_from_this<>模板类,使用其共有方法shared_from_this()来获取对应的shared_ptr

  • weak_ptr

    • 弱引用智能指针

    • 引入目的

      • 解决 shared_ptr 的循环引用问题

        class Parent;
        class Child {
        public:
        	Child() : _pParent(nullptr) {}
            std::shared_ptr<Parent> _pParent;
        };
        class Parent {
        public:
        	Parent() : _pChild(nullptr) {}
        	std::shared_ptr<Child> _pChild;
        };
        shared_ptr<Child> pChild(new Child());
        shared_ptr<Parent> pParent(new Parent());
        pChild->_pParent = pParent;
        pParent->_pChild = pChild;
        

        循环引用1
        当栈对象pChild,pParent销毁之后,
        循环引用2

    • 当进行复制时,引用计数不会自增

    • 不能直接访问托管的资源,需要通过 lock() 方法提升为shared_ptr

      • 若提升成功lock函数返回对应的shared_ptr,失败返回nullptr
    • 可以检查托管资源是否有效 expried()

2.3 删除器

  • 默认情况下,智能指针只能回收 指向堆空间的指针,因为在资源回收时使用delete

    • 对于指向堆空间的指针,使用deletedelete[]
    • 对于文件指针,使用fclose()
  • 通过使用删除器,可以指定资源回收的方式

    //默认删除器模板,使用delete进行回收资源
    template<class Type>
    struct DefaultDelete {
        void operator()(Type * pType) {
            if(pType) {
                delete pType;
                pType = nullptr;
            }
        }
    };
    
    //默认删除器模板,使用delete[]进行回收资源
    template<class Type>
    struct DefaultDeleteArray {
        void operator()(Type * pType) {
            if(pType) {
                delete[] pType;
                pType = nullptr;
            }
        }
    };
    
    //完全特列化,针对FILE类型,使用fclose进行回收资源
    template<>
    struct DefaultDelete<FILE> {
        void operator()(FILE * pType) {
            if(pType) {
                fclose(pType);
                pType = nullptr;
            }
        }
    };
    
    //RAII的模板实现,表达对象语义
    template<class Type, class Deleter = DefaultDelete<Type>>
    class RAII {
    public:
        RAII(Type * pType)
        : _pType(pType)
        {
    
        }
        
        RAII(const RAII &) = delete;
        RAII & operator=(const RAII &) = delete;
    
        RAII(RAII && rhs) {
            if(this != rhs) {
                _pType = release();
                _deleter = rhs._deleter;
            }
        }
    
        ~RAII() {
            _deleter(_pType);
        }
        
        Type * get() const {
            return _pType;
        }
        
        void reset(Type * pType) {
            if(_pType != pType) {
                _deleter(_pType);
                _pType = pType;
            }
        }
    
        Type * operator->() {
            return _pType;
        }
    
        Type& operator*() {
            return *_pType;
        }
    
    private:
        Type * release() {
            Type * pTmp = _pType;
            _pType = nullptr;
            return pTmp;
        }
    
    private:
        Type * _pType;
        Deleter _deleter;
    };
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值