C++ 泛型编程
C++泛型编程介绍
使用C++模板,泛型编程模式将方法推广到多种数据类型。我们在模板中指定一个占位符,而不是具体的数据类型,编译时会用实际使用的数据类型替代该占位符。因此,如果模板函数被调用来处理整数、字符和浮点数,编译器将生成3个函数的副本。这是因为C++使用静态类型。
使用模板的方法
为了理解模板的使用,让我们简要回顾一下标准编程。C++编程语言要求所有数据都存储在容器中,这些容器在技术上被称为数据类型,如int、float、用户定义等。
计算两个坐标之间的距离的过程将不受提供的坐标是整数还是浮点数的影响,因为我们知道该技术可以适用于多种数据形式。int数据类型无法存储float值,使用float数据类型来存储int值会浪费内存,因此,在C++编程中,我们必须为每种数据类型编写单独的方法。因此,C++中的模板为这个问题提供了一种通用的解决方案。
概述
使用模板编写C++中与数据类型无关的代码。在编译时,代码中的占位符会被实际的数据类型替代,这个过程被称为代码实例化。泛型是指定义为模板的类或函数,而在C++中,术语“泛型编程”指的是整个思想。
使用函数模板编写具有泛型类型的函数,这些函数可以根据函数调用时提供的数据类型改变其行为。这使得我们可以在多种数据类型上执行相同的操作,避免了代码重复。
使用函数模板编写具有泛型类型的函数,这些函数可以根据函数调用时提供的数据类型改变其行为。这使得我们可以在多种数据类型上执行相同的操作,避免了代码重复。
# include < iostream >
// Template Function with a Type T
// This T will be changed to the data type of the argument during instantiation.
template < class T >
T maxNum ( T x , T y ) {
return ( a > y ? a : y ) ;
}
int main ( )
{
int a = 5 , b = 2 ;
float i = 4.5 , j = 1.3 ;
std :: cout < < maxNum < int > ( a , b ) < < " \ n " ;
std :: cout < < maxNum < float > ( c , d ) ;
return 0 ;
}
输出:
5
4.5
???..
Process executed in 0.11 seconds
Press any key to continue.
解释
在上面的例子中,数据类型是在函数调用中提供的,但是这一步可以避免,因为编译器会根据我们传递给函数的值来推断出数据类型。
通用数据类型
有参数化类型的类或函数被称为通用类型。通过使用C++模板来实现这一点。通用数据类型是使用模板参数构建的。我们提供一些模板参数,并且函数将它们作为类型值获取,这与函数参数的思想类似。
下面是语法:
template < class id > function_decl ;
// or
template < typename id > function_decl ;
无论使用typename还是class关键字都可以。这两种策略是等价的。
同一个文件必须同时包含声明和定义
模板中没有典型的函数。它们只会在特定模板输入实例化时被编译。根据提供的参数和模板,编译器会生成精确的功能。无法将模板函数的定义和声明分开放在两个文件中,因为只有在必要时才会编译模板。
函数模板的重载
函数模板的重载是可能的。如果程序中出现某个函数调用,编译器会寻找重载函数的精确定义。如果找到了重载函数,它将会执行。如果找不到,则会执行匹配的模板函数。此外,如果函数调用没有伴随着模板函数,编译器会抛出错误。
# include < iostream >
using namespace std ;
template < class T >
void sumoftwonum ( T x , T y ) {
cout < < " Inside Template " < < x + y < < endl ;
}
Void sumoftwonum ( int x , int y ) {
cout < < " Inside Overload " < < x + y < < endl ;
}
int main ( )
{
// Template Function will be called.
sum ( 2.1 , 3.8 ) ;
// Overload will be called.
sum ( 5 , 5 ) ;
return 0 ;
}
输出:
Inside Template 5.9
Inside Overload 10
???????????..
Process executed in 0.11 seconds
Press any key to continue.
解释
- 调用sum(2.1,3.8)将导致编译器寻找精确的函数,但该函数不存在,然后寻找模板函数,因此可以执行该函数。
- 对于不同的函数调用sum(5,5),存在一个重载函数用于精确的数据类型。结果是不会调用模板函数。
模板递归函数
可以使用模板函数来实现递归,并从程序执行的角度看,一切都像常规递归函数一样运作。
使用用户定义类型的函数模板
我们可以自行决定将任何类型传递给函数模板的模板参数。这意味着可以使用用户定义类型来创建函数模板。
# include < iostream >
using namespace std ;
class Base
{
private :
int ageofp ;
string nameofp ;
public :
Person ( string _name_person , int _age )
{
ageofp = _age ;
nameofp = _name_person ;
}
void toString ( )
{
cout < < name < < " is " < < ageofp < < " years old. " < < endl ;
}
} ;
template < class T >
void printDataofbase ( T & obj )
{
obj.toString ( ) ;
}
int main ( )
{
Base b1 = Base ( " Vikram Sharma " , 21 ) ;
printDataofbase ( b1 ) ;
return 0 ;
}
输出:
Vikram Sharma is 21 years old.
???????????..
Process executed in 0.11 seconds
Press any key to continue.
解释:
- Base是一个我们构建的用户定义类型。它有一个函数toString() { [native code] }函数,一个函数Object() { [native code] }和一些私有成员。
- 然后有一个函数模板,可以通过调用它与适当的用户定义数据类型一起使用来打印数据。
- 当编译器解析调用printDataofbase(b1)时,将创建具有特定数据类型的模板函数的副本,这个调用将调用那个替换T的模板函数的副本,其中Person替换T。
类模板
类也可以使用模板,就像函数模板一样,使其与其他数据类型兼容。在C++编程中,您可能已经使用向量创建了一个动态数组,并且可以看到它与您传递给其中的任何数据类型(例如vector < int >)完美地配合。这完全归功于类模板。
template < class T >
class className {
// Class Definition.
// We can use T as a type inside this class.
};
类模板和友元函数
友元函数是一个在类外定义且可以访问类的保护和私有成员的非成员函数。它们被用于连接类和函数。在类模板中,我们可以明确指定我们的友元函数是一个模板函数还是普通函数。
类模板和静态函数
在C++中,类可以包含静态变量或非静态变量(实例)。类的每个对象都包含非静态变量。然而,因为静态变量在所有对象中都是常量,所以它也被所有新对象共享。需要注意的是,在讨论C++模板时,模板类中的静态变量仍然会被同一类型的所有对象共享。