生成一个类模板
template<class _Ty>
class MM
{
public:
protected:
}
//template <> //MM类中没有用到未知类型,不能直接写未知类型的模板类
//class Data
//{
//};
template <class _Ty>
class MM
{
public:
MM() {}
MM(string name):name(name) {}
void print(); //类中声明在类外实现
protected:
string name;
};
//void MM::print() //报错
//{
// cout << "类模板" << endl;
//}
//类模板中的函数在类外实现,都需要类名限定
template <class _Ty>
void MM<_Ty>::print() //所有用到类名的地方:都需要用类名<类型>的方式使用
{
cout << "类模板" << endl;
}
int main()
{
/*用类型+对象名创建对象的方式报错
MM mm; 报错:不能直接创建一个对象,必须要传一个类型,就算没有用到 也要传类型*/
//只要用template修饰就是一个模板类,必须采用显式调用
MM<int> mm1;
MM<string> mm2;
MM<double> mm3;
}
类的继承中 - - - 模板类被继承
用到类型的地方:
-
在类外实现函数
-
在子类继承父类中用到类名
-
在继承中的构造函数
template <class _Ty>
class MM
{
public:
MM() {}
MM(string name):name(name) {}
void print();
protected:
string name;
};
template <class _Ty>
void MM<_Ty>::print()
{
cout << "类模板" << endl;
}
//class Girl :public MM
//{
//};
template<class _Ty>
class Girl :public MM<_Ty>
{
public:
Girl(string name) :MM<_Ty>(name) /*注意:这里用的是类型!!!虽然是调用父类的构造函数
但是解析为类型也需要加<>的方式使用*/
{
}
/*报错: [非法的成员初始化:MM类不是基类或成员]
解决方案:因为用到了类名 需要采用<>的方式使用*/
protected:
};
int main()
{
Girl<int> girl("Loveyou"); //显示调用
girl.print();
return 0;
}
/*输出*/
类模板
综合案例 - - - 两个未知类型的情况
template <class _Ty1, class _Ty2>
class Data
{
public:
Data(_Ty1 one, _Ty2 two) :one(one), two(two) {} //使用初始化参数列表,this指针
void print(); //在类外实现
protected:
_Ty1 one;
_Ty2 two;
};
template <class _Ty1, class _Ty2> //类名限定 用到类名加<>使用
void Data<_Ty1, _Ty2>::print()
{
cout << one <<"\t"<< two << endl;//成员函数在类外实现,访问自己的成员
}
int main()
{
//这个类可以构造出不同的类型 只能显式调用
Data<string, int> mmInfo("小芳", 19); //MM的信息
mmInfo.print();
Data<int, int> data(12, 11); //日期
data.print();
}
/*输出*/
小芳 19
12 11
自定义类型当做模板参数
-
基本自定义类型(没有用到模板)
-
自定义类型也是一个模板(用到模板)
-
模板传入自定义类型,关键点就在于运算符重载
函数模板传入基本数据类型 & 自定义类型
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
protected:
string name;
int age;
};
template <class _Ty>
void print(_Ty one)
{
cout << one << endl;
}
int main()
{
//函数模板传入基本数据类型
print(12); //隐式调用
print<string>("string"); //显示调用
//函数模板传入自定义数据类型
print(MM("mm", 19)); //构建一个无名对象(自定义类型数据)传入报错!!!
} //传入一个对象进来,对象是不能直接打印的,需要运算符重载
/*输出*/
12
string
//函数模板传入自定义数据类型报错
//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)
函数模板传自定义类型
错误:
error C2679:二进制 "<<" :没有找到接受 "_Ty" 类型的右操作数的运算符(或没有可接受的转换)
原因:
传入一个对象进来,one 是一个MM类对象,对象不能直接打印,因为我们传的是自定义类型,要为这个数据类型写一个重载 ---> 运算符重载
打印MM的数据
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
friend ostream& operator<<(ostream& out, MM& mm) //为这个数据类型写一个重载
{
out << mm.name << " " << mm.age;
return out;
}
protected:
string name;
int age;
};
template <class _Ty>
void print(_Ty one)
{
cout << one << endl;
}
int main()
{
//函数模板传入自定义数据类型
print(MM("mm", 19));
}
/*输出*/
mm 19
返回两人中年龄最大的
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
friend ostream& operator<<(ostream& out, MM& mm)
{
out << mm.name << " " << mm.age;
return out;
}
bool operator>(MM& mm) const //用类的成员函数|友元函数的方式重载都可以
{
return this->age > mm.age; //返回年龄最大的
}
protected:
string name;
int age;
};
template <class _Ty>
void print(_Ty one)
{
//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)
cout << one << endl;
}
template <class _Ty>
_Ty Max(_Ty a, _Ty b)
{
//error C2676: 二进制“>”: “_Ty”不定义该运算符或到预定义运算符可接收的类型的转换
return a > b ? a : b; //因为是自定义类型,没办法直接比较
}
int main()
{
//函数模板传入自定义类型
MM xiaoF("小芳", 18);
MM xiaoL("小丽", 28);
//显式调用 传入MM类型
MM result = Max<MM>(xiaoF, xiaoL);
//MM result = Max(xiaoF, xiaoL); //函数模板可以隐式调用,传入自定义类型即可
cout << result << endl; //直接输出 或 print(result);
}
/*输出*/
小丽 28
类模板传自定义类型 - - - 写一个链表管理模板类型的数据
class MM
{
public:
MM(string name, int age) :name(name), age(age) {}
friend ostream& operator<<(ostream& out, const MM& mm) //改为const类型的引用
{
out << mm.name << " " << mm.age;
return out;
}
bool operator>(MM& mm) const
{
return this->age > mm.age;
}
protected:
string name;
int age;
};
template <class _Ty> //节点
class Node
{
public:
Node(_Ty data, Node<_Ty>* next) :data(data), next(next) {}
/*pmove = pmove->data error C2248:“Node<_Ty>::data”无法访问protected成员(在“Node<_Ty>”
类中声明) 需要提供公有接口*/
_Ty getData()
{
return data;
}
Node<_Ty>* getNext()//访问指针 返回值类型是指针类型
{
return next;
}
protected:
_Ty data;
Node<_Ty>* next; //类模板中用到了类名,要用<>的方式,Node是一个类型,必须要用<>的方式
//普通写法:Node* next;
};
template <class _Ty> //需要template <class _Ty>,因为用到了Node类型,Node里面是未知量
class List
{
public:
List()
{
headNode = nullptr; //把指针指为空即可
}
void insertList(_Ty data) //数据插入 头插法
{
headNode = new Node<_Ty>(data, headNode); //类模板必须要用显式写法
}
void printList() //打印
{
Node<_Ty>* pmove = headNode;
while (pmove != nullptr)
{
//error C2679: 二进制“<<”: 没有找到接受“_Ty”类型的右操作数的运算符(或没有可接受的转换)
cout << pmove->getData() << endl; //getData()是MM类型
pmove = pmove->getNext();
}
cout << endl;
}
protected:
Node<_Ty>* headNode;
};
void testList()
{
List<int> list;
//链表存整数
list.insertList(1);
list.insertList(2);
list.insertList(3);
list.printList();
//链表存自定义类型---MM类是一个普通类
List<MM> mmList;
mmList.insertList(MM("小芳", 18));
mmList.insertList(MM("小丽", 28));
mmList.insertList(MM("小美", 38));
mmList.printList();
}
int main()
{
testList();
}
/*输出*/
3
2
1
小美 38
小丽 28
小芳 18
注意:提供的接口是返回值,是一个const类型的对象(常属性) 需要把MM的重载函数改为const MM& mm
-
返回值是值,不可以成为左值 ---> 返回值充当函数参数,需要加const修饰
-
加const是为了 传入const 对象的时候这个重载函数也能用
-
函数调用时函数参数类型必须要一致 ---> 用右值引用只能使用传入右值不能传入变量
-
由于const 类型的参数,既可以传入变量也可以传入常量,所以用const MM& mm
friend ostream& operator<<(ostream& out, const MM& mm)
{
out << mm.name << " " << mm.age;
return out;
}
while (pmove != nullptr)
{
cout << pmove->getData() << endl;
pmove = pmove->getNext();
}