【泛型编程】
若多组类型不同的数据需要使用相同的代码处理,在C语言中需要编写多组代码分别处理,这样做显然太过繁琐,C++增加了虚拟类型,使用虚拟类型可以实现一组代码处理多种类型的数据。
虚拟类型是暂时不确定的数据类型,它在定义时不指定具体类型,而是在使用时指定类型,可以用于单个数据类型,也可以用于数组、结构体,使用虚拟类型编程也称为泛型编程,意为广泛类型。
使用虚拟类型的代码称为模板,但是全局数据不能使用虚拟类型,全局数据也无需定义为模板。
全局函数模板
函数模板的参数、返回值可以使用虚拟类型,参数与返回值的数量必须相同,只是类型不同。
cpp
#include <iostream>
template <typename T> //使用template关键词定义虚拟类型,T为虚拟类型名称
T add(T t1, T t2)
{
return t1+t2;
}
int main()
{
printf("%d\n", add(1, 2)); //T指定为int类型
printf("%f\n", add(0.1f, 0.2f)); //T指定为float类型
return 0;
}
虚拟类型使用template关键词定义,也可以使用class定义,可以在<>符号内定义多个虚拟类型,不同虚拟类型使用,符号隔开,但是每个定义的虚拟类型都必须使用,否则编译报错。
函数模板内可以使用多个虚拟类型,执行函数模板时需要为其内部虚拟类型指定具体类型,当多次执行函数模板并为虚拟类型设置不同的具体类型时,编译器会将函数模板编译为多个函数,分别使用不同的指令处理数据,上述代码中的add函数实际上会被编译器编译成两个函数,分别使用整数运算指令和浮点数运算指令处理数据,模板只是简化了程序员的工作量,并没有减少程序编译后的代码量。
成员函数模板
cpp
#include <iostream>
class math
{
public:
template <typename T> //定义虚拟类型
T add(T t1, T t2) const
{
return t1+t2;
}
};
int main()
{
math math1;
printf("%d\n", math1.add(1, 2));
printf("%f\n", math1.add(0.1f, 0.2f));
return 0;
}
类模板
成员数据使用虚拟类型的类称为类模板。
cpp
#include <iostream>
template <typename T>
class math
{
private:
T a,b;
public:
math(T t1, T t2)
{
a = t1;
b = t2;
}
T add() const
{
return a+b;
}
};
int main()
{
math<int> math1(1, 2); //创建对象时需要使用<>符号设置数据类型
printf("%d\n", math1.add());
math<float> math2(0.1, 0.2);
printf("%f\n", math2.add());
return 0;
}
成员数组模板
虚拟类型用于类成员数组时可以额外定义一个变量,此变量用于设置数组模板的长度,从而实现数组的类型、长度都在定义时临时确定。
cpp
#include <iostream>
template <typename T, int i> //变量i设置数组长度
class array
{
private:
T a[i];
public:
//......
};
int main()
{
array<int, 5> array1; //成员数组类型为int,包含5个元素
array<float, 6> array2; //成员数组类型为float,包含6个元素
return 0;
}
【符号重载】
在C++中可以借助operator关键词将语法中的某些符号、关键词当做函数名,从而重新定义此符号的功能,这种函数称为符号函数,调用符号函数执行时可以无需指定operator关键词,直接使用重载的符号即可,全局函数和成员函数都可以设置为符号函数。
使用符号函数会让代码更简洁,比如连接字符串函数使用+符号作为函数名,即可通过+符号连接两个字符串。
可重载的符号如下:
cpp
+ - * / ++ -- % << >>
&& || ! & | ~ ^
< > == != >= <=
= += -= *= /= %= ^= &= |= >>= <<=
, () []
-> ->*
new delete new[] delete[]
符号重载注意事项:
1.只能使用C++语法中原有符号,不能使用C++语法没有的符号。
2.= () [] -> 这四种符号只能用于成员函数,不能用于全局函数。
3.重载不同符号时,函数可以设置的参数个数不同。
4.重载相同符号时,全局函数与成员函数可以设置的参数个数不同。
5.重载不同符号有不同的限制,比如:重载-符号不能计算两个double数据相加、重载%符号不能只有一个操作数、重载new的函数返回值只能是void类型指针。
重载 + 符号
重载+符号连接固定长度字符串对象,并返回连接结果。
cpp
#include <iostream>
class string
{
public:
char strvar[100] = {0};
/* operator+符号函数连接string对象 */
string operator+(const string & conobj) const
{
string result; //存储连接结果
unsigned int strlen = 0; //存储本类strvar空字符下标
unsigned int conlen = 0; //存储conobj.strvar空字符下标
result.strvar[0] = 0; //初始空字符
/* 查询本类strvar空字符下标,若没有空字符则当做不合规string对象处理,strlen = 0 */
for(int i = 0; i < 100; i++)
{
if(strvar[i] == 0)
{
strlen = i;
break;
}
}
/* 查询conobj.strvar空字符下标 */
for(int i = 0; i < 100; i++)
{
if(conobj.strvar[i] == 0)
{
conlen = i;
break;
}
}
/* result连接本类strvar */
if(strlen != 0)
{
result = *this;
}
/* result连接conobj.strvar */
if(conlen != 0)
{
if(strlen == 0)
{
result = conobj;
}
else
{
for(int i = 0; strlen<99 && i<=conlen; i++)
{
result.strvar[strlen] = conobj.strvar[i];
strlen++;
}
result.strvar[99] = 0; //确保末尾元素是空字符
}
}
return result;
}
/* 重载operator+符号函数,连接字符串 */
string operator+(const char * conobj) const
{
string result;
unsigned int strlen = 0;
unsigned int conlen = 98; //若字符串长度超标,则最多使用99个字符
result.strvar[0] = 0;
/* 查询本类strvar末尾下标 */
for(int i = 0; i < 100; i++)
{
if(strvar[i] == 0)
{
strlen = i;
break;
}
}
/* 查询conobj末尾下标,只需查询99个字节 */
for(int i = 0; i < 99; i++)
{
if(conobj[i] == 0)
{
conlen = i;
break;
}
}
/* 连接本类strvar */
if(strlen != 0)
{
result = *this;
}
/* 连接conobj */
if(conlen != 0)
{
for(int i = 0; strlen<99 && i<=conlen; i++)
{
result.strvar[strlen] = conobj[i];
strlen++;
}
result.strvar[99] = 0;
}
return result;
}
};
int main()
{
string ali = {"阿狸"};
string taozi = {"桃子"};
string zoo = ali + taozi; //调用ali.operator+函数,连接string对象
printf("%s\n", zoo.strvar);
zoo = zoo + "喜羊羊美羊羊"; //调用zoo.operator+函数,连接字符串
printf("%s\n", zoo.strvar);
return 0;
}
重载 = 符号
重载=符号修改动态长度字符串对象,字符串成员定义为私有,只能通过operator=符号函数修改。
cpp
#include <iostream>
#include <string.h>
class string
{
private:
char * strvar;
public:
string()
{
strvar = new char;
*strvar = 0;
}
string(const char * assobj)
{
size_t asslen = strlen(assobj);
strvar = new char[asslen+1];
strcpy(strvar, assobj);
strvar[asslen+1] = 0; //末尾空字符
}
~string()
{
delete [] strvar;
}
/* 返回字符串地址 */
const char * get() const
{
return strvar;
}
void operator=(const char *assobj)
{
size_t asslen = strlen(assobj);
/* 释放旧内存 */
delete [] strvar;
/* 申请新内存 */
strvar = new char[asslen+1];
/* 内存赋值 */
strcpy(strvar, assobj);
strvar[asslen+1] = 0;
}
};
int main()
{
string zoo("阿狸");
printf("%s\n", zoo.get());
zoo = "喜羊羊";
printf("%s\n", zoo.get());
return 0;
}