泛型编程 Generic Programming

  1. 所谓泛型编程,就是使用template(模板)为主要工具来进行编写程序。c++标准库里面有大量的模板编程,最大特点就是将数据与算法分离,算法不需要依赖数据类型。 (思考之前的ncnn源码是使用的GP编程还是OOP编程(面向对象编程 Object Oriented Programming

模板 templates

  • 模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版
  • 模板是一种参数化的编程,它再实例化使用的时候,必须优先指明模板函数的类型这个先决条件。
  • 类模板是指我们的定义的类,模板类是指对象,指的是我们实例化以后的对象。
  • 声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类
  • 声明一个模板,模板参数可以是一个,也可以是多个
// 函数模板定义 
template <typename 形参名>
返回类型 函数名(参数) {
    函数体 
};

typename与class可以进行替换

image.png
思考问题:

  • float b =100 行不行
    • 不行,template只定义了一个类型T,t1和t2是必须一个类型的
  • float c = liu_max(a, b) 可以吗?c是什么类型
    • 可以的
    • 从模板函数中输出的是int类型,经过float强制类型转化,变为float类型
  • 模板的作用域
    • 试想一下我如果在下面在写一个函数还要写template吗 ?
    • 必须要 ```cpp // 类模板定义

template class XXX{….}


- `template` :关键字
- `typename T` : 是模板的参数
- `XXX`   : 模板名字

![image.png](https://cdn.nlark.com/yuque/0/2021/png/353587/1615529931273-0d604270-c409-4c87-8558-80325ac9e038.png#align=left&display=inline&height=383&margin=%5Bobject%20Object%5D&name=image.png&originHeight=383&originWidth=480&size=103930&status=done&style=none&width=480)
<a name="68rdf"></a>
### 特化
如果我们打算给模板函数(类)的某个特定类型写一个函数,就需要用到模板的特化。  
```cpp
template<>    // 这个就是特化意思是下面是一个模板函数 
float max<float> (float a ,float b ) {  return a>b ? a :b}
template<> 
float  max<float,T2>  (float a ,T2 b ) {return a> b ? a :b}

只有类模板有偏特化,不能部分特例化函数模版,不过可以通过函数重载来实现类似功能。

image.png

class  xxx{
    public:
        xxx();
        int get_score();
        int score;
   private:
        int age;
    char[100]  mother_name;
    protected:
        保护的成员
}

image.png

  • 类的抽象
  • 成员变量
  • 构造函数
  • explict
  • singleton单例模式
  • 析构函数
  • 复合类
  • 类与类之间的关系
  • 前向引用声明
  • 特殊的类 struct enum

    抽象

    面向对象编程的基本特点: 抽象 封装 继承 多态

  • 抽象: 对同一类对象的共同属性和行为进行概括,形成类

  • 封装: 封装就是将实例抽象得到的数据和行为封装成一个类。
  • 继承: 大象 猫 都是哺乳动物
  • 多态: 美短和加菲猫生活习惯不一样

    cat:
    {
       Int sex,  string pinzhong , int width ,int height,int weight  
    
        Eat_food() ; Sa_jiao();
    }
    

    成员变量

    class  xxx{
      public:
          公有
         private:
          私有
      protected:
            保护的成员
    }
    
  • public :一个类的public成员变量、成员函数,可以通过类的成员函数、类的实例变量进行访问

  • private :一个类的private成员变量、成员函数,无法通过类的实例变量进行访问。但是可以通过类的友元函数、友元类进行访问。
  • protected : 一个类的protected成员变量、成员函数,无法通过类的实例变量进行访问。但是可以通过类的友元函数、友元类进行访问。
  • privateprotected :两者的访问权限一致,但是细节是protected只能在派生类中访问, private只能在友元中访问

    构造函数

    构造函数是一种特殊的成员函数,在每个类创建的时候都会被隐式的创建,名称与类名完全一致,它不会返回任何数据类型,void也不行,构造函数通常是用来初始化一些成员变量。
    image.png
    思考:

  • cat::cat() {}; cat::cat(int age=0) {};

    • 不行,
  • void cat::cat() {....}
    • 不行,不能加void
  • 不写构造函数行不行?
    • 可以,系统会默人给你生成一个无参构造 cat::cat() = default;

如何高效:

cat::cat(int a, int b, int c){
        _a = a;
        _b = b ;
        _c  = c;
}

cat::cat(int a, int b, int c): _a(a), _b(b), _c(c){}  // 快的多

最好写一个空的构造函数 (为了通用性更好)(不知道你调用的哪个构造函数了,自己debug一下)

构造函数explict

explict 这个关键字它用于类的单参数构造前面,就是说如果没有去做一个构造函数的时候会隐式的帮我做一个构造函数,但是当我写了explict去构造了一个有参构造函数,意思就是不让你进行隐式的构造了必须按着这个标准的构造方式来,在有多个构造函数的时候可能就没有什么作用

singleton格式(单例模式)的构造函数:

单例模式是众多设计模式中的一种,单例模式比较常见的一种,面试和工作中也会经常接触到。它主要应用于日志类、配置类、管理器、共享资源类等方面。
特点:

  • 构造函数私有
  • 保证一个类只创建一个实例
  • 需要提供该实例的全局访问点
  • 高效

    class Config{
      public:
          static Config& instance(){
              static Config s_instance;
              return s_instance;
          }
          int parse_config(std::string const& conf_path);
    
          int int_conf(std::string const&key,int defautlt_value){
              auto iter = int_conf_.find(key);
              if(iter!=int_conf_.end()){
                  return iter->second;
              }
              return defautlt_value;
          }
    
          float float_conf(std::string const&key,float default_value){
              auto iter =float_conf_.find(key);
              if(iter!= float_conf_.end()){
                  return  iter->second;
              }
              return default_value;
          }
    
          vector<int> str_conf(std::string const&key,vector<int> default_value){
              auto iter = str_conf_.find(key);
              if(iter!= str_conf_.end()){
                  return  Split(iter->second,",");
              }
              return default_value;
          }
          const char* char_conf(std::string const&key ){
              auto iter = str_conf_.find(key);
              if(iter!= str_conf_.end()){
                  return iter->second.data();
              }
          }
          std::vector<int> Split(const std::string& src, const std::string& separator);
      private:
          Config(){}
          std::unordered_map<std::string,int>     int_conf_;
          std::unordered_map<std::string,float>   float_conf_;
          std::unordered_map<std::string,std::string>  str_conf_;
    };
    

    首次instance完了以后就初始化了这个类,这个类已经存在了全局变量中,instance这个位置就是一个全局的访问点

析构函数

     类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。 <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/353587/1615533388592-1e2169bf-e8bb-49ec-8d1d-f423cab63d55.png#align=left&display=inline&height=108&margin=%5Bobject%20Object%5D&name=image.png&originHeight=108&originWidth=395&size=12595&status=done&style=none&width=395)

类与类之间的关系

  • 继承 Inheritance :A是B的子类
  • 复合 Composition : A has B , B has C
  • 委托 Delegation :A中有B的指针

    前向引用声明

    两个类互相声明需要对方 这该如何声明 不是万能的,不一定解决问题
    image.png

    特殊类

  • 结构体 : 一种特殊的类 ,一般来说里面没有任何操作,只有数据的存放相关操作

  • 联合体 :(这个没必要 用的不多)
  • 枚举类 : 要懂
    struct 结构名 { 
      类型 变量名; 
      类型 变量名; 
      ... 
    } 结构变量;
    
    image.pngimage.png
    思考:
    image.png
    这样初始化行不行?不行
    结构体初始化:
  1. 顺序初始化: box a= {1,2,3,4}; 必须一一对应
  2. 指定初始化 box b; b.x = 10;
  3. 结构体中有默认构造函数 那么默认的构造方式就是构造函数中的构造方式
  • 结构体中的可以有函数吗? 如果有那和类不是一样了

可以有,class中默认的成员访问权限是private的,而struct中则是public的。

image.png
枚举类型的定义:枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。
枚举类型的定义格式为:
enum <类型名> {枚举表}
enum color_set1 {RED, BLUE, WHITE, BLACK};
默认是0开始的整形,主要作用也是在某些变量使用时候比较方便参考