前言
c++文章连载:
1.C++基础
1.C++基础
2.C++新增和有变化的关键字
3.C++的内存管理
2.面向对象
1.C++的封装和访问权限
2.C++继承和多态特性
3.C++的运算符重载
4.C++静态类和静态成员
5.C++的友元函数和友元类
3.模板编程和STL
1.C++模板编程入门
2.STL的容器类和迭代器
3.STL的泛型算法
4.模板特化与类型萃取
5.STL的其他容器讲解
6.智能指针与STL查漏补缺
4.杂项
1.c++各种流操作
2.依赖,关联,聚合,组合,继承
3.一些技巧性的代码设计
1.概述
1、泛型在C++、java、C#等多种语言中都有
2、模板是一种编译时的多态
(1)函数模板定义时typename和class效果是一样的,视个人习惯而使用
2.函数模板和类模板
(1)类名前要加template<typename T>
,函数定义前也要加,而且函数的作用域符前面需要加<T>
,template<typename T> people<T>::people(int a)
使用类库的人需要指定模板的类型,所以使用需要带上类型:People p1(4);
(2)单模板参数的类模板使用,代码实践
3.模板友元函数
3.1、友元函数参数中不带模板的情况
(1)友元函数声明在class内,定义实现写在class外
(2)友元函数参数中类的直接给出具体类型,譬如
(3)这种友元函数实际是削弱了模板参数在使用
3.2、友元函数参数中带模板参数方法1
(1)友元函数声明和定义都写在class内部
(2)虽然写在class内,但仍然是友元,而不是member function
1.因为不能通过对象.友元函数的方式来执行友元函数,所以说明友元函数不是对象的成员。
2.友元函数不能通过this->成员来调用类中的成员,只有通过传参,比如传一个对象形参:
friend void print(const people& pn);
发现以下代码效果一样:
friend void print(const person<T>& pn)
{
cout << pn.age << endl;
}
friend void print(const person& pn)
{
cout << pn.age << endl;
}
(3)友元可以适配类的各种模板参数
3.3、友元函数参数中带模板参数方法2
(1)友元函数声明在class内,定义在class外
(2)声明时函数名加后缀,而定义时不用加
(3)需要class和friend function的2个前置声明
(4)调用friend function时可加<实参类型>后缀,也可以不加,但是加就必须加对了
4.模板运算符重载函数
#include <iostream>
#include <string>
using namespace std;
// 定义模板类
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T a):age(a){};
// 运算符重载 + - c = a + b;
People<T> operator+(People<T> &other);
// 运算符重载+= a += b; 等价于 a = a + b;
People<T> operator+=(People<T> &other);
void print(void);
};
template <typename T> People<T> People<T>::operator+(People<T> &other)
{
People<T> tmp;
tmp.age = this->age + other.age;
return tmp;
}
template <typename T> People<T> People<T>::operator+=(People<T> &other)
{
// this->age = this->age + other.age;
this->age += other.age;
return *this;
}
template <typename T> void People<T>::print(void)
{
cout << "age = " << this->age << endl;
}
int main(void)
{
People<string> a("aston");
People<string> b("zhu");
People<string> c("");
// c = a + b;
a += b;
a.print();
/*
People<int> a(4);
People<int> b(6);
People<int> c(0);
c = a + b;
c.print();
*/
return 0;
}
5.模板友元运算符重载实现
5.1、+作为友元运算符重载
(1)友元函数在class内实现,ok,但是因为是友元实现的,所以参数要有2个
(2)友元函数在class外实现,不ok
(3)友元函数的第三种实现,ok(见代码)
5.2、+=作为友元运算符重载
(1)友元函数在class内实现,ok,但必须带2个参数,带1个不行
(2)友元函数在class外实现,ok
(3)友元函数的第三种实现,ok
#include <iostream>
#include <string>
using namespace std;
//template <typename T> class People;
//template <typename T> People<T>& operator+=(People<T> &a, People<T> &b);
// 定义模板类
template <typename T> class People
{
private:
T age;
public:
People(){};
People(T a):age(a){};
template <typename B> friend People<B>& operator+=(People<B> &a, People<B> &b);
/*
friend People<T>& operator+=(People<T> &a, People<T> &b)
{
//a.age = a.age + b.age;
a.age += b.age;
return a;
}
*/
// template <typename U> friend People<U> operator+(People<U> &a, People<U> &b);
/*
friend People<T> operator+(People<T> &a, People<T> &b)
{
People<T> tmp;
tmp.age = a.age + b.age;
return tmp;
}
*/
void print(void);
};
template <typename T> People<T>& operator+=(People<T> &a, People<T> &b)
{
a.age += b.age;
return a;
}
/*
template <typename T> People<T> operator+(People<T> &a, People<T> &b)
{
People<T> tmp;
tmp.age = a.age + b.age;
return tmp;
}
*/
template <typename T> void People<T>::print(void)
{
cout << "age = " << this->age << endl;
}
int main(void)
{
#if 0
People<string> a("aston");
People<string> b("zhu");
People<string> c("");
// c = a + b;
a += b;
a.print();
#endif
#if 1
People<int> a(4);
People<int> b(6);
People<int> c(0);
a += b;
a.print();
#endif
return 0;
}
6.模板类的继承
类模板与模板类概念:模板类是类模板实例化后的一个产物
#include <iostream>
#include <string>
using namespace std;
// 定义模板类,作为父类
template <typename U1, typename U2> class People
{
public:
U1 x1; // x1是double
U2 x2; // x2是int
People(){};
People(U1 a, U2 b):x1(a),x2(b){};
};
// Man类里T1对应int,T2对应double
// 继承时,父类就是People<double,int>,因为这一步是认符号,不认顺序
// 然后再去到People<T1, T2>类里去,所以是People<double,int>对应了People<T1, T2>
// 于是,在People类内部,T1对应double,T2对应int,这时候是认顺序,不认符号
template <typename T1, typename T2> class Man:public People<T2, T1>
{
public:
T1 y1; // T1 int
T2 y2; // T2 double
Man(){};
Man(T1 a, T2 b):y1(a),y2(b){};
// 4个参数,顺序:按照y1 y2 x1 x2
Man(T1 a1, T2 a2, T1 b1, T2 b2):People<T2,T1>(b1, b2),y1(a1),y2(a2){};
};
int main(void)
{
Man<int,double> m1(4, 5.5, 6, 7.7); // y1=a, y2=5.5 x1=6, x2=7.7
cout << "x1 = " << m1.x1 << ", x2 = " << m1.x2 << endl;
cout << "y1 = " << m1.y1 << ", y2 = " << m1.y2 << endl;
return 0;
}
7.非类型模板参数和模板类型推导
7.1、非类型模版参数
template<typename T, int VAL>
T addValue(const T& x)
{
return x + VAL;
}
template<typename T, T VAL>
T addValue(const T& x)
{
return x+VAL;
}
参考 https://blog.csdn.net/lanchunhui/article/details/49634077
浮点型(包括float,double)、类、字符串不可以作为非类型模板参数
7.2、类型推导的隐式类型转换
(1)在决定模板参数类型前,编译器执行隐式类型转换,有时候实际类型和看起来会不同
(2)编译器用值类型实例化函数模板,而不是用相应的引用类型
(3)编译器用指针类型实例化函数模板,而不是相应的数组类型
(4)去除const修饰,绝不会用const类型实例化函数模板,总是用相应的非const类型
(5)对于指针来说,指针和 const 指针是不同的类型。
7.3、模板和库
(1)模板无法单独编译,也就不能通过lib连接静态库的形式隐藏实现
(2)模板通常会把声明和定义写在头文件里,所以c++的模板总是开源的
int a = 5;
int &b = a;
People<b>
int a[5];
void func(a); // 数组做实参,实际实参不是数组而是指针
People<a>
const int a = 5;
People<a>
const int *p1;
int *p2;
说明typename的一种用法,这种用法里typename不等同于class
https://blog.csdn.net/dick_china/article/details/4522253