C++模板

模板是建立通用的膜具,大大提供复用性;

特点: 模板不能直接使用,它只是一个框架;模板通用不是万能的;

函数模板

C++泛型编程主要利用的技术就是模板。

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

函数模板的作用:建立一个通用的函数,其函数返回值类型和参数可以不具体指定,用一个虚拟的类型来代表;

语法: template <typename T> or template<class T> ;template表示声明一个模板,typename和class 表示后面的数T是一个通用的数据类型. T可以换成其它字符代替,不过默认用T(默认大写字母)

template<typename T>
void myswap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
int main()
{
    int a = 10;
    int b = 20;
    //两种方式调用函数模板
    //自动类型推导
    myswap(a,b); // 编译器自动推导出传进去的a b类型, 使用自动类型推导 必须推导出一致的数据类型
   
    // 显示指定类型
   myswap<int> (a,b); // 调用函数模板的时候写上数据类型,必须指定T的数据类型,不然不能调用

}

自动类型推导必须推导出一致的数据类型才可以调用函数模板;

调用函数模板必须指定T的数据类型,否则无法调用

普通函数和函数模板的区别:

1、普通函数调用时可以发生自动类型转换(隐式类型转换)

2、自动类型推导调用函数模板时,不能发生自动类型转换(隐式类型转换)

3、如果使用显示指定类型,可以发生隐式类型转换

template <typename T>
T myadd(T a, T b)
{
return a + b;
}
int a = 10;
char b= 'c';
myadd(a,b); // 使用自动类型推导时调用函数模板不可以发生隐式类型转换
myadd<int>(a,b); //使用显示指定类型时可以发生自动类型转换

建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用数据类型

调用规则:

1、当普通函数和函数模板都能实现时,优先调用普通函数

2、可以通过空模板参数列表来强行调用函数模板

3、函数模板可以发生重载

4、当函数模板可以产生更好匹配,优先调用函数模板

int test(int a , int b)
{
      return a + b;
}
template<typename T> 
T test(T a , T b)
{
     return a + b;
}
 
template <typename T>  //函数模板重载
T test(T a, T b , T c)
{
    return a + b + c;
}

int main()
{
  int a = 10 ;
int b = 20;
test(a,b); // 当普通函数和函数模板都能实现时,优先调用普通函数
test<>(a,b); // 使用空模板参数列表 < > 可以强制调用函数模板
 
char c = 'c';
char d = 'd';
test(c,d); // c d 都是字符类型, 此时调用函数模板更方便,不用发生类型转换,优先调用函数模板
return 0;
}

如果提供了函数模板就不要提供普通函数,否则容易出现二义性

对于自定义的数据类型,函数模板需要做具体化的实现

template <typename T>
bool mycompare(T &a, T &b); //需要进行声明才可以这样使用
template<>bool mycompare(Person &a, Person & b)  // template<> 指明是函数模板
{
       if(a.name = b.name && a.age == b.age)
{
            return true;
}
else
return false;
}

总结:利用具体化模板,可以解决自定义类型的通用化,学习模板并不是为了使用模板,而是在STL能够运用系统的提供的模板

类模板

类模板和函数模板的区别:

类模板没有自动类型推导的使用方式;类模板在模板参数列表中可以有默认参数;

template<class T =int>  //将T默认设置为int 类型
void test(T a, )
{
   return a;
}

成员函数的创建时机:

普通类的成员函数在一开始时就创建,类模板中的成员函数在调用时才创建

对象做函数参数:

1、指定传入的类型 -- 直接显示对象的数据类型(最常用)

2、参数模板化 -- 将对象的参数变成模板进行传递

3、整个类模板化,将这个对象类型模板化传递

void test1(Person<string,int> &p)  //指定传入的数据类型
{
      p.name = "lisi";
}

//将参数模板化 
template<class T1,class T2>
void test02(Person<T1,T2> &P)
{
   p.name = "wwu";
}

//整个类模板化
template<class T>
void test03(T &p)
{
     p.name = "liu";
}

利用typeid(通用数据类型).name() 来查看此刻的通用数据类型

类模板与继承:

1、当子类继承的父类是一个类模板时,子类在声明的时候必须指定父类中T的数据类型,如果不指定,将无法给子类分配内存

2、如果想灵活指定父类中T的数据类型,则需将子类也进行模板化

template <class T>
class Person
{
   public:
   T name;

class Son : public Person  //必须指定父类中的通用数据类型T,否则编译器无法给子类分配内存
{
};
class son : public Person <int>

//如果向让子类灵活指定父类中T的数据类型,需要将子类也模板化
template <class T1, class T2>
class son1 : public Person <T2>
{

};

成员函数类外实现:

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

template<class T1,class T2>
class Person
{
   public:
   void showPerson();
   T1 name ;
   T2 age;
};

//类外实现需要加上模板声明 和指定作用域(需要加上参数列表)
template<class T1, class T2>
void Person<T1,T2> :: showPerson()
{
    ;
}

分文件编写:

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

解决:直接包含.cpp源文件; 或者将声明和实现写到一个文件中,并更名后缀名为:.hpp, hpp是约定的名称,并不是强制

总结:主流的解决方式是第二种,将成员函数声明和实现写到一起,把后缀名改为.hpp

类模板和友元:

1、全局函数类内实现 -- 直接在类内声明友元即可

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

template<class T1, class T2>
class Person ; // 声明类模板的存在
template <class T1,class T2>
void printf(Person<T1,T2> P)  //全局函数类外实现, 需要声明是模板内的全局函数,
{
  cout << p.m_Name <<  "  " << p.m_age << endl;
}
template<class T1, class T2>
class Person
{
friend void printf(Person<T1,T2>p)
{
   cout << p.m_Name << "  "  << p.m_age << endl;  // 全局函数类内实现
}
 
//全局函数类外实现,需要在类内声明 ,且在类外实现
  friend void fun<>(Person<T1,T2>&p);   //需要加上类模板空参数列表
   public : 
    T1 m_Name;
    T2 m_age;
};

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值