趁着最近有空多谢两篇博客。嘿嘿嘿,加油!嘿嘿嘿,加油!
5.1 将C-风格的字符串用作string对象的引用参数
先看一个函数
<span style="font-size:18px;">string version1(const string &s1,const string& s2)
{
string temp;
temp=s2+s1+s2;
return temp;
}
//调用该函数
result=version2(input,'###');</span>
这里我们注意到“###”的类型为const char*。此时函数怎么能够接受char指针赋给string引用呢?这里有两点需要说明。首先,string类定义了一种char*到string的转换功能,这使得可以使用C-风格的字符串来初始化string对象。其次是之前讨论过的const引用的形参的一个属性。假设实参的类型与引用参数的类型不匹配,但可以被转化为引用类型,程序将创建一个正确的临时变量,使用转换后的实参值来初始化它,然后传递一个指向该临时变量的引用。例如在之前介绍的,将int实参传递给const double&形参时,就是以这种方法处理的。
5.2 对引用参数的一个小结
使用引用参数的原因有两个:
- 程序员能够修改调用函数中的数据对象。
- 通过传递引用参数而不是整个数据对象,可以提高程序的运行速度。
什么时候应使用引用,什么时候应使用指针,什么时候使用按值传递,下面给出一个指导原则:
- 如果数据对象很小,如内置数组或小型结构,则按值传递。
- 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为const的指针。
- 如果数据对象是教大的结构,则使用const指针或者是const引用。用来提高程序的效率。这样可以节省复制结构所需的时间和空间。
- 如果数据对象是类对象,则应该使用const引用。类设计的语义常常要求使用引用。因此传递类对象的参数的标准方式是引用传递。
- 如果数据对象是内置的数组类型,则应该使用指针。
- 如果数据对象是数组,则只能使用指针。
- 如果数据对象是结构,则使用引用或是指针。
- 如果数据对象是类对象,则应该使用引用。
5.3 函数的重载
函数重载就像是有多重含义的动词。例如PP小姐可以再棒球场为家乡的球队助威(root),也可以在天地里种植(root)作物。可以通过上下文来知道每一种情况下root的含义,同样C++也通过上下文来确定使用的重载函数版本。
函数重载的关键是函数的参数列表————也成为函数的特征标。如果两个函数的参数数目相同,且类型相同,参数的顺序也一样,则他们的特征标相同。但是有些看起来彼此不同的特征标是不能共存的,比如看下面:
<span style="font-size:18px;">double cube(double x);
double cube(double &x);</span>
如果并有下面的代码
<span style="font-size:18px;">cout<<cube(x);</span>
参数x与double &x的类型都汽配,因此编译器无法识别究竟应该使用哪个原形。为了避免这种混乱,编译器将在检查特征标时,将把类型引用和类型本身看做是同一个特征标。
另外在匹配函数时,并不区分const和非const变量。还有一种重载也是C++多不允许的
<span style="font-size:18px;">long gronk(int n,float m);
double gronk(int n,float m);</span>
返回的类型可以不同,但是特征标也必须相同。
5.4 函数模板函数模板是一个通用的函数描述,也就是说,他们使用泛型来定义函数,其中的泛型可以用于具体的类型。通过将类型作为函数传递给模板,可是编译器生成该类型的函数。这里给出一个交换模板
<span style="font-size:18px;">template<typename T>
void swap(T&a,T&b)
{
T temp;
temp=a;
a=b;
b=temp;
}</span>
在标准C++98添加关键字typename之前,其使用关键字class来创建
5.5 模板的具体化
实验其他的具体化方法后,C++98标准选择了下面的方法。
- 对于给定的函数名,可以有非模板函数、模板函数与现实具体化模板函数以及它们的重载版本。
- 显示具体化的原型和定义应以template<>打头,并通过名称来指定其类型。
- 具体化优先于常规的模板,而非模板函数优先于具体化和常规模板。
下面给出用于交换job结构的非模板函数、模板函数和具体化的原型:
<span style="font-size:18px;">void swap(job&,job&);//non template function prototype
template<typename T>//template prototype
void swap(T&,T&);
template<>void swap(job&,job&);explicit specialization for the job</span>
则在下面的调用过程中,第一次会使用通用版本,而第二次则会使用显式具体化版本
<pre class="cpp" name="code"><span style="font-size:18px;">int main()
{
double u,v;
...
swap(u,v);//use template
job a,b;
...
swap(a,b);//use void swap<job>(job&,job&)
}
</span>
这里我再来介绍一下为什么会有需要显示具体化,这里我给出两个例子大家就会很容易理解
<span style="font-size:18px;">struct job
{
char name[40];
double salary;
int floor;
};
template<typename T>
void swap(T&a,T&b)
{
T temp;
temp=a;
a=b;
b=temp;
}
template<>void swap<job>(job&j1,job&j2)
{
double t1;
int t2;
t1=j1.salary;
j1.salary=j2.salary;
j2.salary=t1;
t2=j1.floor;
j1.floor=j2.floor;
j2.floor=t2;
}</span>
虽然都是交换,但是二者显然有着明显的区别。
5.6 再论模板的实例化与具体化
为了进一步了解模板,必须理解术语实例化和具体化。在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例。模板并非函数定义,但是使用int的例是函数定义,这种实例化的方式被称为隐式实例化。最初,编译器只能通过隐式实例化,来使用模板生成函数定义,但是现在C++还允许现式实例化。如前面的例子,它需要<>来指示类型,并加上关键字template。
隐式实例化、现实实例化和现实具体化均成为具体化。它们的相同之处在于,它们表示的都是具体类型的函数定义,而不是通用描述。