第8章 函数探幽-2

8.3 默认参数

默认参数指的是当函数调用中省略了实参时自动使用的一个值。如void wow(int n),在调用wow()时相当于调用wow(1)(void wow(int n)设置成n有默认值1)。
通过函数原型设置默认值,将值赋给原型中的参数,例如:

char *left(const char * str, int n = 1);

上述代码即表示n的默认值为1,省略该参数时,它的值便为1。
对于带参数列表的函数,必须从右向左添加默认值(要为某个参数设置默认值,必须为它右边的所有参数提供默认值),例如:

int harpo(int n, int m = 4, int j = 5);  //valid
int chico(int n, int m = 6, int j);   //invalid
int groucho(int k = 1, int m = 2, int n = 3);      //valid
调用:
beeps = harpo(2);     //同harpo(2, 4, 5)
beeps = harpo(1, 8);     //同harpo(1, 8, 5)
beeps = harpo(8, 7, 6);   //没有使用默认参数

函数调用时实参按从左到右的顺序依次被赋给相应的形参,不能跳过任何参数。即:

beeps = harpo(3,  , 8);   //invalid

设计类的时候,通过使用默认参数,可以减少要定义的析构函数、方法以及方法重载的数量
在这里插入图片描述
在这里插入图片描述
以下循环,当m的值等于n或到达字符串结尾时,循环将终止:

int m = 0;
while(m <= n && str[m] != ‘\0)
      m++;
char * p = new char[m+1];

上述代码,在str[m]不是空值字符时,表达式str[m] != ‘\0’的结果为true,否则为false。由于&&表达式会将非零值转换为true0转换为false,所以也可以写为:

while (m <= n && str[m])

8.4 函数重载

补充:左值引用右值引用

#include<iostream>
using namespace std;
int main(void)
{
   int a = 10;
   int b = 20;

//左值引用(&)
   int &c = a;    //valid
   int &d = 10;   //invalid,此处为左值引用而10非左值
   int &d = (a + b);  //同上(a + b)非左值

   const int &d = 10;    //valid,const会开辟临时空间进行转换
   const int &c = (a + b);   //同上,const

//右值引用(&&),可与右值产生关联
   int &&x = 10;  //valid
   int &&y = (a + b);   //valid
}

右值引用的设定是为了后续的移动语义。
默认参数使得可以使用不同数目的参数调用同一个函数,函数多态(函数重载)使得能够使用多个同名的函数。“函数多态”指的是允许函数可以有多种形式,“函数重载”指的是可以有多个同名的函数,实质是一回事,但通常使用函数重载。
可通过函数重载来设计一系列函数——它们完成相同的工作,但使用不同的参数列表。函数重载的关键是函数的参数列表——函数特征标(如果两个函数的参数数目和类型以及参数的排列顺序也相同,则它们的特征标相同,变量名无关紧要)。C++允许定义名称相同的函数,条件是它们的特征标不同(参数数目和/或参数类型不同)

void print(const char * str, int width);    
void print(double d, int width);
//调用print()函数,编译器将根据传递形参调用相应特征标的原型:
print(“Pancakes”, 15);
print(1999.0, 10);

编译器会将类型引用和类型本身视为同一个特征标,如下:

double cube(double x);
double cube(double &x)

函数匹配时不区分const和非const,函数原型形参有const时,在调用该函数时既可以传递const参数也可以非const

void dribble(char * bits);
void dribble(const char *cbits);
void dabble(char * bits);
void drivel(const char *bits);
//调用dribble()函数,编译器将根据实参是否是const来调用对应原型:
const char p1[20] = “how’s the weather?;
char p2[20] = “how’s business”;
dribble(p1);
dribble(p2);
dabble(p1);   //no match
dabble(p2);   //dabble(char *)
drivel(p1);    //drivel(const char *)
drivel(p2);    //drivel(const char *)

上述drivel()函数既可接收const修饰参数也可非const修饰参数。
C++函数重载不允许函数特征标和名字相同,但返回类型不同;当函数名相同而函数返回类型不同时,特征标也必须不同

8.4.2 何时使用函数重载

仅当函数基本上执行相同的任务,但使用不同形式的数据时,才应采用函数重载。使用默认参数也可实现同样的目的,但仅适用于参数类型相同(参数个数不同)的函数,如下例。默认参数适用于大多数使用默认值,极个别需要自定义值。

char *left(const char *str, unsigned n);     //2个参数
char *left(const char *str);        //1个参数

如果需要使用不同类型的参数,应该使用函数重载。

8.5 函数模板

例如8.4,一个交换两个int值的函数,假设要交换两个double值,一种方式是复制原来的代码并用double替换所有的int,如果交换char值,同理。
此种情况使用函数模板,可节省时间,且更可靠。
函数模板允许以任意类型的方式来定义函数。如上例可建立如下的交换模板:

template <typename AnyType>      //建立一个模板,并将类型命名为AnyType
void Swap(AnyType &a, AnyType &b)
{
   anytype temp;
   temp = a;
   a = b;
   b = temp;
}

关键字templatetypename为必需项,也可用关键字class代替typename(推荐使用typename,class或与类class冲突);必须使用尖括号<>。类型名可以任意选择(此处为AnyType)。模板不创建函数,只是告诉编译器如何定义函数。需要交换int的函数时,编译器将创建这样的函数,并用int代替AnyTypedoublechar同理。
程序清单8.11

#include<iostream>
using namespace std;
void Swap(T &a, T &b);
int main(void)
{
   int i = 10;
   int j = 20;
   cout << “i、j =<< i <<,<< j << endl;
   Swap(i, j);     //编译器接收两个int参数,将自动将T替换为int生成int型交换函数
   cout << “After swap, now i、j =<< i <<,<< j << endl;

   double x = 23,5;
   double y = 74,8;
   cout << “x、y =<< x <<,<< y << endl;
   Swap(x, y);   //编译器接收两个int参数,将自动将T替换为double生成double交换//换函数
   cout << “After swap, now x、y =<< x <<,<< y<< endl;

   template <typename T>    //or class T
   void Swap(T &a, T &b)
{
   T temp;
   temp = a;
   a = b;
   b = temp;
}
   return 0;
}

函数模板不能缩短可执行程序,上述程序仍将由两个独立的函数定义;但使用模板可以使我们只需编写一个函数,编译器调用时可以自动生成多个函数,使得函数定义更简单可靠。
通常将模板放在头文件中,并在需要使用模板的文件中包含头文件
8.5.1 重载的模板
有时候并非所有的类型都使用的相同的算法,此时可像重载常规函数定义那样重载模板定义。同常规重载,模板重载也必须满足被重载的模板的函数特征标不同。
例如程序清单8.12交换模板,原来模板的特征标是(T &, T &),新模板(交换两个数组)的特征标为(T [], T [], int),最后一个参数类型为具体类型(int)而非泛型。
程序清单8.12

#include<iostream>
using namespace std;
const int LIM = 8
void Swap(T a[], T a[], int n);
int main(void)
{
   int i = 10;
   int j = 20;
   cout << “i、j =<< i <<,<< j << endl;
   Swap(i, j);     //编译器接收两个int参数,将自动将T替换为int生成int型交换函数
   cout << “After swap, now i、j =<< i <<,<< j << endl;

   int d1[DIM] = {0, 6,, 8, 9};
   int d2[DIM] = {0, 4,, 6, 9};
   cout << “Original arrays” << endl;
   show(d1, LIM);
   show(d2, LIM);
   Swap(d1, d2, LIM);   //编译器接收两个数组,将自动将T替换为数组生成数组交换//换函数
   cout << “After swap, now x、y =<< x <<,<< y<< endl;

//Swap1
template <typename T>    //or class T
void Swap(T &a, T &b)
{
   T temp;
temp = a;
a = b;
b = temp;
}

//Swap2
template <typename T>    //or class T
void Swap(T a[], T a[], int n) //重载Swap
{
   T temp;
   for(int i =0 ; i < n; i++)
   {
   temp = a[i];
a[i] = b[i];
b[i] = temp;
   }
}

//
void show(int a[])
{
   for(int i =0 ; i < n; i++)
   {
      cout << a[i];
      cout << endl;
      }
}
   return 0;
}

8.5.2 模板的局限性

对于模板

template <class T>
void f(T a, T b)
{}

如果定义通过a = b进行赋值,那么T为数组时这种假设则不成立;定义<,对应语句if (a > b),如果T为结构,该假设也不成立。
总结:编写的模板函数可能无法处理某些类型。

8.5.3 显示具体化

如下结构:

struct job
{
   char name[40];
   double salary;
   int floor;
}

假设需要交换两个这种结构的内容,原来的模板可适用于两个结构之间所有变量的交换。但如果只想要交换salary和floor成员,则需要使用不同的代码。此时可通过显示具体化——提供一个具体化函数定义,其中包含所需的代码(当编译器找到与函数调用匹配的具体化定义时,将使用该定义不再寻找模板)。
 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本
 显示具体化的原型和定义应以template<>打头,并通过名称指出类型
 调用时具体化优先于常规模板,而非模板函数优先于具体化和常规模板
用于交换job结构的非模板函数,模板函数,具体化原型:

void Swap(job &, job &);
template <typename T>
void Swap(T &, T &);
template <> void Swap<job>(job &, job &);  

此处说明的job类型不适用与上述的模板类型,此时需要此函数里边的显示具体化版本

8.5.4 实例化和具体化

显示实例化——可以直接命令编译器创建特定的实例,如Swap<int>()对应的语法格式(无<>):

template void Swap<int>(int, int);

该声明意思是“使用Swap()模板生成int类型的函数定义”。对于上述代码编译器将使用Swap()模板生成一个使用int类型的实例。
显示具体化语法格式包含<>(以下两个等价):

template <> void Swap<int>(int &, int &);
template <> void Swap(int &, int &);

该声明意思是“不要使用Swap()模板来生成函数定义,而是使用专门为int类型显示定义的函数定义”。
注意在同一个文件或转换单元中使用同一种类型的显示实例和显示具体化将出错

8.5.5 编译器选择使用哪个函数版本

 指向非const数据的额指针和引用优先于非const指针和引用参数匹配(但const和非const的区别只适用于指针和引用指向的数据);
 非模板函数优先于模板函数;
 如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先(显式具体化由于隐式具体化)。
也可以通过编写合适的函数调用引导编译器按照自定义的选择调用。
在这里插入图片描述
在这里插入图片描述
cout << lesser(m, n) << endl;: 该函数调用与模板函数和非模板函数都匹配,故选择非模板函数
cout << lesser(x, y) << endl;: T为double,与模板匹配
cout << lesser<>(m, n) << endl;: “<>”指出应选择模板函数
cout << lesser<int>(x, y) << end;l: 显式实例化(使用int替代T),将使用显式实例化得到的函数。(xy都将被强转为int,最后返回一个int值))。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: 在C语言中,string函数是一个字符串处理函数库,它包含在<string.h>头文件中。其中常用的函数有strcpy、strlen和strnset。 strcpy函数用于将一个字符串复制到另一个字符串中。它的函数原型是:char *strcpy(char *dest, const char *src)。其中,dest是目标字符串,src是源字符串。这个函数会将src字符串的内容复制到dest字符串中,并返回dest字符串的指针。\[1\] strlen函数用于计算字符串的长度。它的函数原型是:size_t strlen(const char *str)。这个函数接收一个字符串的首地址,然后遍历字符串直到遇到'\0'字符,返回字符串的长度。\[2\] strnset函数用于将指定的字符替换字符串中的一部分字符。它的函数原型是:char *strnset(char *str, int c, size_t n)。其中,str是要操作的字符串,c是要替换的字符,n是要替换的字符个数。这个函数会将字符串中的指定部分字符替换为指定的字符。\[3\] 这些函数都是C语言中常用的字符串处理函数,可以帮助我们进行字符串的复制、长度计算和字符替换等操作。 #### 引用[.reference_title] - *1* *3* [C语言中string函数详解](https://blog.csdn.net/weixin_30902251/article/details/99781150)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c语言String字符串函数探幽](https://blog.csdn.net/Duary/article/details/106163396)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我宿孤栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值