C++进阶编程 --- 1.模板

本文详细介绍了C++中的函数模板和类模板,包括它们的概念、语法、使用场景、区别、调用规则以及局限性,如自动类型推导、模板重载和友元等。同时提及了模板在类继承中的应用和分文件编写的解决方案。
摘要由CSDN通过智能技术生成

第一章:

1.模板

1.1 模板概念

模板就是建立通用的模具,提高复用性

特点

  • 模板不可直接使用,它只是一个框架

  • 模板的通用并不是万能的

1.2 函数模板

作用:建立一个通用的函数,让其函数返回值类型和形参类型可不具体设定,用一个虚拟的类型来表示。

C++另一种编程思想称为泛型编程,运用的主要技术就是模板。

C++中提供了两种模板机制:函数模板和类模板。

1.2.1 函数模板语法

语法

template<typename T>
函数声明或定义
  • template:声明创建模板

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

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

两种方式使用函数模板

  • 自动类型推导

  • 显示指定类型

#include <iostream>
using namespace std;

template<typename T>
void Swap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 10;
    int b = 20;

    //Swap(a, b);    //自动类型推导
    Swap<int>(a, b); //显示指定类型
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.2.2 函数模板注意事项
  • 自动类型推导,需推导出一致的数据类型T,才可使用

  • 模板需确定T的数据类型,才可使用

#include <iostream>
using namespace std;

template<typename T>
void Swap(T& a, T& b)
{
    T temp = a;
    a = b;
    b = temp;
}

void test01()
{
    int a = 10;
    int b = 20;
    char c = 'A';
    Swap(a, b);  //正确
    //Swap(a, c);  //报错,自动类型推导,需推导出一致的数据类型T,才可使用
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
}

template <typename T>
void func()
{
    cout << "func 函数的调用" << endl;
}

void test02()
{
    func<int>(); //正确 
    //func();    //报错,模板需确定T的数据类型,才可使用
}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
1.2.3 函数模板案例
  • 使用函数模板封装一个排序的函数,可对不同数据类型数组进行排序

  • 排序规则从大到小,排序算法为选择排序

  • 分别利用char数据和int数组进行测试

    #include <iostream>
    using namespace std;
    
    template<class T>
    void MySwap(T& a, T& b) //交换函数
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    template <class T>
    void MySort(T arr[], int len) //排序函数
    {
        for (int i = 0; i < len; i++)
        {
            int max = i; //默认最大值的下标
            for (int j = i + 1; j < len; j++)
            {
                if (arr[max] < arr[j])
                {
                    max = j;
                }
            }
            if (max != i)
            {
                MySwap(arr[max], arr[i]); 
            }
        }
    }
    
    template <class T>
    void printArray(T arr[], int len)   //打印数组函数
    {
        for (int i = 0; i < len; i++)
        {
            cout << arr[i] << " ";
        }
        cout << endl;
    }
    
    void test01()
    {
        //测试char类型数组
        char chArray[10] = "dbcaefg";
        int chLength = sizeof(chArray) / sizeof(char); //计算数组长度
        MySort(chArray, chLength);
        printArray(chArray, chLength);
    
        //测试int类型数组
        int intArray[] = { 6,2,4,3,9,8,1,5,7 }; 
        int intLength = sizeof(intArray) / sizeof(int); 计算数组长度
        MySort(intArray, intLength);
        printArray(intArray, intLength);
    }
    
    int main()
    {
        test01();
        system("pause");
        return 0;
    }
    
1.2.4 普通函数与函数模板的区别
  • 普通函数调用时可发生自动类型转换(隐式类型转换)

  • 函模板调用时,如果用自动类型转换,将不会发生隐式类型转换

  • 如果用显示指定类型的方式,可发生隐式类型转换

#include <iostream>
using namespace std;

int myAdd_1(int a, int b)
{
    return a + b;
}

template <class T>
int myAdd_2(T a, T b)
{
    return a + b;
}

void test01()
{
    int a = 10;
    int b =  20;
    char c = 'a';

    cout << myAdd_1(a, c) << endl; //107,普通函数调用时可发生自动类型转换(隐式类型转换)

    //cout << myAdd_2(a, c) << endl; //报错,函模板调用时,如果用自动类型转换,将不会发生隐式类型转换

    cout << myAdd_2<int>(a, c) << endl; //107,如果用显示指定类型的方式,可发生隐式类型转换
}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.2.5 普通函数与函数模板的调用规则
  • 函数模板和普通函数都可实现的情况下,优先调用普通函数

  • 函数模板可以产生更好的匹配的情况下,优先调用函数模板

  • 可通过空模板参数列表来强制调用函数模板

  • 函数模板也可发生重载

    #include <iostream>
    using namespace std;
    
    int myAdd(int a, int b)
    {
        cout << "普通函数调用" << endl;
        return a + b;
    }
    
    template <class T>
    int myAdd(T a, T b)
    {
        cout << "函数模板调用" << endl;
        return a + b;
    }
    
    template <class T>
    int myAdd(T a, T b, T c)
    {
        cout << "函数模板重载调用" << endl;
        return a + b + c;
    }
    
    void test01()
    {
        int a = 10;
        int b = 20;
        char ch1 = 'a';
        char ch2 = 'b';
    
        myAdd(a, b);     //普通函数调用
    
        myAdd<>(a, b);   //函数模板调用
    
        myAdd(a, b, 30); //函数模板重载调用
    
        myAdd(ch1, ch2); //函数模板调用 
    }
    
    int main()
    {
        test01();
        system("pause");
        return 0;
    }
    
1.2.6 模板的局限性
  • 模板的通用性不是万能的
template<class T>
void assignment(T a, T b)
{
    a = b
}

如果传入的a和b是数组,就没法实现。

template<class T>
void compare(T a, T b)
{
    if(a > b)
    {
       ...
    }
}

如果传入的是自定义数据类型,也无法实现。

为了解决这种问题,提供了模板的重载,为特定的类型提供具体化的模板

#include <iostream>
using namespace std;

class Student
{
public:
    Student(string name, int age)
    {
        this->m_name = name;
        this->m_age = age;
    }
    string m_name;
    int m_age;
};

template <class T>
bool myCompare(T &a, T &b)
{
    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

template<> bool myCompare(Student & p1, Student& p2) //使用具体化Student版本实现代码,具体化优先调用
{
    if (p1.m_age == p2.m_age && p1.m_name == p2.m_name)
    {
        return true;
    }
    else
    {
        return false;
    }
}
void test01()
{
    Student s1("小明", 20);
    Student s2("小红", 18);

    bool result = myCompare(s1, s2);
    if (result)
    {
        cout << "s1 = s2" << endl;
    }
    else
    {
        cout << "s1 != s2" << endl;
    }
}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.3 类模板

作用:建立一个通用类,类中成员数据类型可不具体设定,用一个虚拟的类型表示。

1.3.1 类模板语法

语法

template<class T>
类
  • template:声明创建模板

  • typename:表其后面的符号是一种数据类型,可用typename代替

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

#include <iostream>
using namespace std;
#include <string>

template <class T1, class T2>
class Student
{
public:
    Student(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    void showStuInfo()
    {
        cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
    }

    T1 m_name;
    T2 m_age;
};

void test01()
{
    Student<string, int> s1("小明", 18);
    s1.showStuInfo();
}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.3.2 类模板与函数模板的区别
  • 类模板没有自动类型推导的使用方式

  • 类模板在模板参数列表中可以有默认参数

#include <iostream>
using namespace std;

template<class T1, class T2 = int>
class Student
{
public:
    Student(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    void showStuInfo()
    {
        cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
    }

    T1 m_name;
    T2 m_age;
};

void test01()
{
    //Student s1("小明", 18); //报错,类模板没有自动类型推导
    Student<string> s1("小明", 18);
    s1.showStuInfo();

}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.3.3 类模板中成员函数创建时机
  • 普通类中的成员函数一开始就可创建

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

#include <iostream>
using namespace std;

class Student1
{
public:
    void printStudent1()
    {
        cout << "Student1" << endl;
    }
};


class Student2
{
public:
    void printStudent2()
    {
        cout << "Student2" << endl;
    }
};

template<class T>
class Class
{
public:
    T student;

    void func1()
    {
        student.printStudent1();
    }

    void func2()
    {
        student.printStudent2();
    }

};

void test01()
{
    Class<Student1>s1;
    s1.func1();
    //s1.func2(); //报错,函数调用才会去创建成员函数
}

int main()
{
    test01();
    system("pause");
    return 0;
}
1.3.4 类模板对象做函数参数

三种传入方式:

  • 指定传入的类型 — 直接显示对象的数据类型

  • 参数模板化 — 将该对象中的参数变为模板进行传递

  • 整个类模板化 — 将该对象类型模板化进行传递

#include <iostream>
using namespace std;

template<class T1, class T2>
class Student
{
public:
    Student(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    void showStuInfo()
    {
        cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
    }

    T1 m_name;
    T2 m_age;
};

void printStuInfo(Student<string, int>&s) //传入指定类型
{
    s.showStuInfo();
}

void test01()
{
    Student<string, int>s("小明", 18);
    printStuInfo(s);
}

template<class T1, class T2>
void printStuInfo2(Student<T1, T2>& s) //参数模板化
{
    s.showStuInfo();
    cout << "T1的类型:" << typeid(T1).name() << endl; //T1的类型:class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
    cout << "T2的类型:" << typeid(T2).name() << endl; //T2的类型:int 
}

void test02()
{
    Student<string, int>s("小红", 19);
    printStuInfo2(s);
}

template<class T>
void printStuInfo3(T& s) //整个类模板化
{
    s.showStuInfo();
    cout << "T的类型:" << typeid(T).name() << endl; //T的类型:class Student<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>
}

void test03()
{
    Student<string, int>s("小王", 20);
    printStuInfo3(s);
}
int main()
{
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}
1.3.5 类模板与继承
  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定父类中T的类型

  • 不知道类型的话,编译器无法给子类分配内存空间

  • 想灵活指定父类中T的类型,子类需也变成类模板

#include <iostream>
using namespace std;

template<class T>
class Base
{
public:
    T m_age;
};

//class Son :public Base //报错,子类在声明的时候,要指定父类中T的类型
class Son :public Base<int>
{

};

void test01()
{
    Son s1;
}

template<class T1, class T2>
class Son2 :public Base<T2> //想灵活指定父类中T的类型,子类需也变成类模板
{
public:
    void showType()
    {
        cout << "T1的类型:" << typeid(T1).name() << endl; //T1的类型:int
        cout << "T2的类型:" << typeid(T2).name() << endl; //T2的类型:char
    }
    T1 m_a;
};

void test02()
{
    Son2<int, char> s2;
    s2.showType();
}

int main()
{
    test01();
    test02();
    system("pause");
    return 0;
}
1.3.6 类模板成员函数类外实现
#include <iostream>
using namespace std;

template<class T1, class T2>
class Student
{
public:
	Student(T1 name, T2 age);

	void printStuInfo();

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test01()
{
	Student<string, int> s("小明", 18);
	s.printStuInfo();
}

int main()
{
	test01();
	system("pause");
	return 0;
}
1.3.7 类模板分文件编写

类模板中成员函数创建时机在调用阶段,导致分文件编写时链接不到

解决方法:

  1. 直接包含.cpp源文件

  2. 将声明和实现写在同一文件中,更改后缀名.hpp,hpp约定名称,不强制

test.cpp:

#include <iostream>
using namespace std;

//#include "Student.cpp" //解决方法1 - 包含源文件

#include "Student.hpp" //解决方法2 - .h和.cpp内容写一起,改后缀.hpp文件

void test01()
{
	Student<string, int>s("小明", 18);
	s.printStuInfo();
}

int main()
{
	test01();
	system("pause");
	return 0;
}

Student.cpp:

#include "Student.h"

template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

Student.h:

#pragma once
#include <iostream>
using namespace std;

template<class T1, class T2>
class Student
{
public:
	Student(T1 name, T2 age);

	void printStuInfo();

	T1 m_Name;
	T2 m_Age;
};

Student.hpp:

#pragma once
#include <iostream>
using namespace std;

template<class T1, class T2>
class Student
{
public:
	Student(T1 name, T2 age);

	void printStuInfo();

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Student<T1, T2>::Student(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Student<T1, T2>::printStuInfo()
{
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
1.3.8 类模板与友元
  • 全局函数类内实现 - 在类内声明友元即可

  • 全局函数类外实现 - 需提前让编译器知道全局函数的存在

#include <iostream>
using namespace std;

//提前让编译器知道Studet类存在
template<class T1, class T2> 
class Student;

//类外实现
template<class T1, class T2>
void printStuInfo(Student<T1, T2>s)
{
	cout << "姓名:" << s.m_Name << " 年龄:" << s.m_Age << endl;
}

template<class T1,class T2>
class Student
{
public: 
	Student(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	//全局函数 - 类内实现
	friend void printStuInfo(Student<T1, T2>s)
	{
		cout << "姓名:" << s.m_Name << " 年龄:" << s.m_Age << endl;
	}

	//全局函数 - 类外实现
	//提前让编译器知道函数的存在
	//加空模板参数列表
	friend void printStuInfo<>(Student<T1, T2>s);

	T1 m_Name;
	T2 m_Age;
};

void test01()
{
	Student<string, int>s("小明", 18);
	printStuInfo(s);

	Student<string, int>s2("小红", 19);
	printStuInfo(s2);
}

int main()
{
	test01();
	system("pause");
	return 0;
}

下一章:C++进阶编程 — 2.初始STL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值