用相应的函数代码替换函数调用。
优点:运行速度更快;
缺点:代价是占有更多内存。调用函数结束之后会释放,而内联函数不会,将一直占据内存。
因此:函数代码较少时,优先选择内联函数。
使用方法:函数声明或者函数定义前加上关键字inline,通常做法是省略函数原型(声明),将整个定义放在原本提供原型的地方。
inline int sum(int a,int b)
{
return a+b;
}
定义位于类声明中的函数都将自动成为内联函数,无须inline标识。
2、引用2.1、什么是引用
引用变量:是一种特殊的变量,被认为是一个变量的别名,因此引用并不分配独立的内存空间,它与目标变量公用其内存空间。
引用的声明方法:类型标识符 &引用名 = 目标变量名(该变量可以在栈区或者堆区,但不能是常量区数据);
注意:引用必须初始化,初始化之后不可更改!!!!
1、&在此不是求地址运算,而是起标识作用。
2、引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名,以免造成混淆。
3、不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名
int value1 = 100;
int &ref = value1;
int temp = 50;
ref = temp; //因为ref是value1的别名,因此此语句相当于:value1 = temp;
引用的本质:指针常量
上述等价于:int * const ref = &value1;
2.2、引用的主要用途
引用变量的主要用途是用作函数的形参,为处理大型结构提供方便的途径。
1、 最经典的就是交换数值,类似于指针传递
void swap ( int& x,int& y )
{
int temp;
temp=x;
x=y;
y=temp;
}
这种情况下会修改原始值原始值。
2、 希望程序传递参数信息,但是不进行修改,可以使用常量引用
int func(const int &temp);
如果在函数中修改了temp,会产生报错信息
3、 引用是变量的别名,不可以将常量做参数
double ref_fun1(double &ra) //但是如果加上const,C++才允许这样做
{
return ra * ra;
}
double ref_fun2(const double &ra) //但是如果加上const,C++才允许这样做
{
return ra * ra;
}
int main()
{
using namespace std;
double x = 3.0;
cout << ref_fun1(x) << endl; //允许
cout << ref_fun1(x+3.0) << endl; //不允许
cout << ref_fun2(x+3.0) << endl; //允许,此时传递给ref_fun2是常量,符合const double&
return 0;
}
2.3、引用做函数返回值
使得函数返回的是对象,而不是拷贝值
1、 不要返回临时变量(局部变量)的引用:因为局部变量在函数调用结束后消亡
int &func(void)
{
int s_temp = 10;
return s_temp;
}
int main() {
int &ref = test01();
cout << ref << endl; //输出10
cout << ref << endl; //输出乱码值
2、 解决方法:返回一个作为参数传递给函数的引用
int &func(int & target,const int & source)
{
target = source
return target;
}
3、 //如果函数做左值,那么必须返回引用
int &func()
{
static int temp = 100;
return temp;
}
int main()
{
int &ref = func();
cout << ref << endl; //输出100
func() = 1000;
cout << ref << endl; //输出1000
}
3、函数高级用法
3.1、默认参数
函数调用中省略实参时,自动使用的一个值,即形参列表中可以有默认值。
1、 语法:返回值类型 函数名(参数 = 默认值); //默认值添加从右到左
eg: int func(int a, int b = 10, int c = 20)
{
return a+b;
}
//函数调用
cout << func(10) << endl; //输出40
cout << func(10,20) << endl; //输出50
cout << func(10,20,30) << endl; //输出60
2、上述函数直接放在主函数前,直接调用,若将函数声明和函数定义分开,则函数声明有默认值,函数定义就不能有默认值
int func(int a, int b = 10, int c = 20); //函数声明
func(10,20,30); //函数调用
int func(int a, int b, int c) //函数定义
{
return a+b;
}
3.2、函数重载
与函数默认值的区别:
默认参数:使用不同数目的参数调用同一个函数;
函数重载:能够使用多个同名的函数,提高复用性;
C++根据上下文来确定使用函数重载的版本
函数重载满足的条件:
* 1、同一个作用域下
* 2、函数名称相同
* 3、函数参数:类型不同、个数不同、顺序不同(函数特征标,而不是变量名)
注意事项
1、 类型引用和类型本身视为同一个特征标
int func(int a);
int func(int &a);
cout << func(b) << endl;
编译器无法区分上述两个函数
2、 区分const和非const变量
版本一:
void func_str(char *str) 函数一
void func_str(const char *str) 函数二
char p1[] = "We are Chinese";
const char p2[] = "I'm from Hubei";
func_str(p1); 调用函数一
func_str(p2); 调用函数二
版本二:
void func(int temp); 函数三
void func(const int temp); 函数四
int a = 10;
func(a); 调用函数三
func(20); 调用函数四
3、 函数返回值类型不作为重载的条件
void func(double a ,int b)
{
cout << "func (double a ,int b)的调用!" << endl;
}
double func(double a ,int b)
{
cout << "double func (double a ,int b)的调用!" << endl;
return a+b;
}
编译器无法区分仅按返回值类型区分的函数;
4、函数重载遇到默认参数
void func2(int a, int b = 10)
{
cout << "func2(int a, int b = 10) 调用" << endl;
}
void func2(int a)
{
cout << "func2(int a) 调用" << endl;
}
func2(20); 产生歧义,需要避免
3.3、函数模板
1、什么时候使用函数模板?
需要使用多个不同类型的同一种算法时,使用模板更好。(函数模板建议放在头文件中,使得多个文件可以使用该模板)
2、什么是函数模板?
函数模板是通用的函数描述,使用泛型来定义函数,其中的泛型可以使用具体的类型进行替换。
允许以任意类型的方式来定义函数,
3、具体语法:
template <typename Anytype> //后面没有分号;
/*其中template 和 typename 是必须的,且template可以使用class替换*/
void func(Anytype par1, Anytype par2)
{
//operation
函数模板不创建任何函数,只是告诉编译器如何定义函数,编译器根据传入的类型再去创建相应的函数实体
}
4、具体实例:
交换两个数值
template <typename Anytype>
void Swap(Anytype &par1, Anytype &par2)
{
Anytype temp;
temp = par1;
par1 = par2;
par2 = temp;
}
int main()
{
int a = 10,b = 20;
Swap(a,b);
}
5、小知识点:函数模板的模板参数并非必须是模板参数类型
template <typename Anytype>
void func(Anytype par1, Anytype par2, int m) 这里的m就是指定int类型,而不是Anytype
{
//operation
函数模板不创建任何函数,只是告诉编译器如何定义函数,编译器根据传入的类型再去创建相应的函数实体
}
3.4、函数模板重载
1、为什么需要函数模板重载?
需要对多个不同类型使用不同算法时,可以使用函数模板重载,和函数重载条件一样,被重载的模板特征标必须不同。
template <typename Anytype>
void Swap(Anytype &par1, Anytype &par2)
{
Anytype temp;
temp = par1;
par1 = par2;
par2 = temp;
}
template <typename Anytype>
void Swap(Anytype par1[], Anytype par2[], int m)
{
Anytype temp;
for(int i=0;i<m;i++)
{
temp = par1[i];
par1 = par2[i];
par2 = temp;
}
}
3.5、函数模板局限性
函数模板不是万能的,很有可能无法处理某些类型(比如数组赋值,结构比大小等等),这需要我们
1、事先定义好,以免产生歧义
2、为特定类型提供具体化的模板定义,也叫显示具体化
显示具体化相关标准如下:
1、对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。
2、显式具体化的原型和定义应以 template<>打头,并通过名称来指出类型。
3、非模板函数 > 显示具体化 > 常规模板函数(优先级)
上述第二点中,名称是指:该类型名与定义的类型名要一致
举例:
1、void Swap(int par1, int par2); 非模板函数
2、template <typename Anytype>void Swap(Anytype &par1, Anytype &par2); 模板函数
3、template <> void Swap<T_struct >(T_struct &par1, T_struct &par2); 显示具体化
struct T_struct
{
string name;
int age;
};
int main()
{
int a = 10,b = 20;
Swap(a,b); 调用非模板函数(如果没有非模板函数,调用模板函数)
T_struct struct_a = { "fhl",26 };
T_struct struct_b = { "xwy",24 };
Swap(struct_a, struct_b); 调用显示具体化
}
3.6、函数模板实例化
1、什么是函数模板实例化?(分为隐式实例化和显示实例化)
隐式实例化:函数模板本身不会生成函数定义,但根据传入的特定类型会生成函数定义,得到的是模板实例;
template<typename Anytype> void Swap(Anytype &par1, Anytype &par2);
显示实例化:直接命令编译器创建特定的实例;用<>指示类型,声明前加上template
template void Swap<int>(int&par1, int&par2);
显示具体化
template<> void Swap<int>(int&par1, int&par2);
==(等价于)
template<> void Swap(int&par1, int&par2);
3.7、函数模板的使用
由上述可以看出,函数模板很灵活,可以自动匹配,但是如果想直接调用模板该怎么做呢?
template <typename Anytype> Anytype func(Anytype par1, Anytype par2)
{
return (par1 > par2) ? par1 : par2;
}
int func(int a, int b)
{
return a>b?a:b;
}
int main()
{
int m = 10,n = 20;
int result;
result = func(m,n); 调用非函数模板(根据优先级)
result = func<>(m,n); 调用函数模板,这里的<>指出编译器应该选择函数模板,而不是非模板函数
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)