C++提高编程部分总结

ZJ Lv100

前言

这里是对于想要深入了解C++编程的同学的关于提高编程部分的总结,请在了解了核心部分和基础部分后再看本部分总结,不然有些地方很难懂

1 模板

1.1 模板概念

概念:建立通用的模具,大大提高复用性

特点:

  • 模板不可以直接使用,它只是一个框架
  • 模板的通用并不是万能的

运用思想:泛型编程

模板机制:

  • 函数模板
  • 类模板

作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

语法:

1
2
3
4
5
6
7
8
template<typename T>
template<class T>
//函数声明或定义
template --- //声明创建模板

typename --- //表面其后面的符号是一种数据类型,可以用class代替

T --- //通用的数据类型,名称可以替换,通常为大写字母

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //利用模板提供通用的交换函数
    template<typename T>
    void mySwap(T& a, T& b)
    {
    T temp = a;
    a = b;
    b = temp;
    }
    void printArray(T arr[], int len) {

    for (int i = 0; i < len; i++) {
    cout << arr[i] << " ";
    }
    cout << endl;
    }
    void test()
    {
    //1、自动类型推导
    int a=1;
    char b='b';
    //这样不同的类型就不能用模板来实现
    mySwap(a, b);
    printArray(charArr, num);
    printArray(intArr, num);
    }
  • 模板必须要确定出T的数据类型,才可以使用(但T为什么根据情况来,没有特殊要求可以任取)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //利用模板提供通用的交换函数
    template<typename T>
    void func()
    {
    cout<<'hhh'<<endl;
    }

    void test()
    {
    //2、显示指定类型
    func();//错误
    func<int>();//正确
    }

局限性:

  • 模板的通用性并不是万能的
  • 简单赋值操作不能用于数组
  • 普通数据类型不能用于类数据类型

拓展性(局限性的解决):(用具体化的方式实现特殊情景)

  • 具体化(template<>

  • 其实也是另一种重载形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class Person
    {
    public:
    Person(string name, int age)
    {
    this->m_Name = name;
    this->m_Age = age;
    }
    string m_Name;
    int m_Age;
    };
    //具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
    //具体化优先于常规模板
    template<> bool myCompare(Person &p1, Person &p2)
    {
    if ( p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
    {
    return true;
    }
    else
    {
    return false;
    }
    }

1.2 函数模板

函数模板作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

语法:

1
2
3
4
5
6
7
template<typename T>
//函数声明或定义
template --- //声明创建模板

typename --- //表面其后面的符号是一种数据类型,可以用class代替

T --- //通用的数据类型,名称可以替换,通常为大写字母

1.2.1 函数模板使用方法

  • 自动类型推导(IDE自己判断)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //利用模板提供通用的交换函数
    template<typename T>
    void mySwap(T& a, T& b)
    {
    T temp = a;
    a = b;
    b = temp;
    }

    void test()
    {
    //1、自动类型推导
    mySwap(a, b);
    }

  • 显示指定类型(<类型>——自己传入类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//利用模板提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}

void test()
{
//2、显示指定类型
mySwap<int>(a, b);
}

1.2.2 普通函数与函数模板

一般不同时出现,除非故意这么写,或不小心多写了

区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

当传入为不同的类型参数时,模板函数不能推到,而普通函数可以(int与char,char会转为ascii码)

调用规则:

  • 如果函数模板和普通函数都可以实现,优先调用普通函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //优先调用普通函数
    void myPrint(int a, int b)
    {
    cout << "调用的普通函数" << endl;
    }

    template<typename T>
    void myPrint(T a, T b)
    {
    cout << "调用的模板" << endl;
    }
  • 可以通过空模板参数列表来强制调用函数模板

    1
    2
    //空模板参数列表
    myPrint<>(a, b); //调用函数模板
  • 函数模板也可以发生重载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //函数模板重载
    template<typename T>
    void myPrint(T a, T b)
    {
    cout << "调用的模板" << endl;
    }
    template<typename T>
    void myPrint(T a, T b, T c)
    {
    cout << "调用重载的模板" << endl;
    }
  • 如果函数模板可以产生更好的匹配,优先调用函数模板

    1
    2
    3
    4
    //考虑更好的匹配机制,优先调用函数模板
    char c1 = 'a';
    char c2 = 'b';
    myPrint(c1, c2); //调用函数模板

1.3 类模板

类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。

语法:

1
2
3
4
5
6
7
template<class T>
//函数声明或定义
template --- //声明创建模板

typename --- //表面其后面的符号是一种数据类型,可以用class代替

T --- //通用的数据类型,名称可以替换,通常为大写字母

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//类模板
template<class NameType, class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};

Person<string, int>P1("孙悟空", 999);

1.3.2 成员函数

1.3.2.1 创建时机

类模板中的成员函数在调用时才创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};

class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};

template<class T>
class MyClass
{
public:
T obj;

//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成

void fun1() { obj.showPerson1(); }
void fun2() { obj.showPerson2(); }
//无法直接调用这两个函数,需要在主函数中给定obj的类型,才会选择行的调用

};

MyClass<Person1> m;//能调用fun1(),不能调用fun2()
1.3.2.2 类外实现

类模板中成员函数在类内声明,在类外实现定义

注意事项:类模板中成员函数类外实现时,需要加上模板参数列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();

public:
T1 m_Name;
T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

1.3.3 类模板对象做函数参数

传入方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//类模板
template<class NameType, class AgeType = int>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
  1. 指定传入的类型 — 直接显示对象的数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //1、指定传入的类型
    void printPerson1(Person<string, int> &p)
    {
    p.showPerson();
    }
    void test01()
    {
    Person <string, int >p("孙悟空", 100);
    printPerson1(p);
    }
  2. 参数模板化 — 将对象中的参数变为模板进行传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //2、参数模板化
    template <class T1, class T2>
    void printPerson2(Person<T1, T2>&p)
    {
    p.showPerson();
    cout << "T1的类型为: " << typeid(T1).name() << endl;
    cout << "T2的类型为: " << typeid(T2).name() << endl;
    }
    void test02()
    {
    Person <string, int >p("猪八戒", 90);
    printPerson2(p);
    }

  3. 整个类模板化 — 将这个对象类型 模板化进行传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //3、整个类模板化
    template<class T>
    void printPerson3(T & p)
    {
    cout << "T的类型为: " << typeid(T).name() << endl;
    p.showPerson();

    }
    void test03()
    {
    Person <string, int >p("唐僧", 30);
    printPerson3(p);
    }

1.3.4 模板中导出系统给模板定义的数据类型

关键词:typeid

语法:typeid(T).name()

1.3.5 类模板与继承

注意事项:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<class T>
    class Base
    {
    T m;
    };

    //class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
    class Son :public Base<int> //必须指定一个类型
    {
    };
  • 如果不指定,编译器无法给子类分配内存

  • 如果想灵活指定出父类中T的类型,子类也需变为类模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //类模板继承类模板 ,可以用T2指定父类中的T类型
    template<class T1, class T2>
    class Son2 :public Base<T2>
    {
    public:
    Son2()
    {
    cout << typeid(T1).name() << endl;
    cout << typeid(T2).name() << endl;
    }
    };

    void test02()
    {
    Son2<int, char> child1;
    }

1.3.6 类模板与函数模板区别

类模板与函数模板区别主要有两点:

  • 类模板没有自动类型推导的使用方式(需要自己在定义类对象的时候加上<>)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template<class NameType, class AgeType = int> 
    class Person
    {
    public:
    Person(NameType name, AgeType age)
    {
    this->mName = name;
    this->mAge = age;
    }
    void showPerson()
    {
    cout << "name: " << this->mName << " age: " << this->mAge << endl;
    }
    public:
    NameType mName;
    AgeType mAge;
    };

    Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
    Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
  • 类模板在模板参数列表中可以有默认参数

    1
    2
    3
    template<class NameType, class AgeType = int>

    Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数

1.3.7 类模板分文件编写

可用于类外实现的情况

方法:

  • 直接包含.cpp源文件
  • 将声明.h和实现.cpp写到同一个文件中,并更改后缀名为.hpp.hpp是约定的名称,并不是强制(主流)

提示:

  • 在.h源文件中,#pragma once防止头文件重复包含

image-20241028022728934

1.3.8 类模板与友元

实现方法:

  • 全局函数类内实现 - 直接在类内声明友元即可(建议用此)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    template<class T1, class T2>
    class Person
    {
    //1、全局函数配合友元 类内实现
    friend void printPerson(Person<T1, T2> & p)
    {
    cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
    }
    public:

    Person(T1 name, T2 age)
    {
    this->m_Name = name;
    this->m_Age = age;
    }

    private:
    T1 m_Name;
    T2 m_Age;
    };

    void test01()
    {
    Person <string, int >p("Tom", 20);
    printPerson(p);
    }
  • 全局函数类外实现 - 需要提前让编译器知道全局函数的存在(将在类外实现的函数提前写,还要提前写明类与模板)

    1. 全局函数提前

      1
      2
      3
      4
      template<class T1,class T2>
      printPerson2(Person<T1,T2>p){
      cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
      }
    2. 类模板提前

      1
      template<class T1, class T2>
    3. 类声明提前

      1
      class Person;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    template<class T1, class T2>
    class Person;
    //函数模板的实现
    template<class T1,class T2>
    printPerson2(Person<T1,T2>p){
    cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
    }

    //以上为提前告诉编译器的东西

    template<class T1, class T2>
    class Person
    {
    //2、全局函数配合友元 类外实现
    //普通函数的声明
    friend void printPerson2(Person<T1, T2> & p);
    //函数模板的声明
    friend void printPerson2<>(Person<T1, T2> & p);
    public:

    Person(T1 name, T2 age)
    {
    this->m_Name = name;
    this->m_Age = age;
    }


    private:
    T1 m_Name;
    T2 m_Age;

    };

    void test02()
    {
    Person <string, int >p("Jerry", 30);
    printPerson2(p);
    }

具体提高部分引入

这里是黑马程序员的免费资料,本人从网盘中下载下来了,要学习完整C++基础资料的可以访问下面链接,链接为我的博客(搬运过来了),里面也有C++基础资料的网盘分享

C++提高编程
  • Title: C++提高编程部分总结
  • Author: ZJ
  • Created at : 2024-10-25 18:00:00
  • Updated at : 2025-01-17 01:36:25
  • Link: https://blog.overlordzj.cn/2024/10/25/C++/提高部分总结/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments