目录
2-6 函数新特性、内联函数inline、const详解
一、函数回顾与后置返回类型
函数定义中,形参如果在函数体内用不到的话,则可以不给形参变量名字,只给其类型。
函数声明时,可以只有形参类型,没有形参名
把函数返回类型放到函数名字之前,这种写法,叫前置返回类型。
C++11中,后置返回类型,在函数声明和定义中,把返回值类型放到参数列表之后。
前面放auto,表示函数返回类型放到参数列表之后,而放在参数列表之后的返回类型是通过 -> 开始的。
#include <iostream>
using namespace std;
void func123(int , int); // 函数声明(函数原型);
void func123(int a,int b)// 函数实现
{
return;
}
// C++11后置返回类型
auto func(int a , int b) -> void; // 函数声明
auto func(int a , int b) -> void
{
return;
}
inline int myfunc(int testv) // 函数定义前加inline,这个函数为内联函数
{
return;
}
int main()
{
//函数定义中,形参如果在函数体内用不到的话,则可以不给形参变量名字,只给其类型。
func123(12,13);
return 0;
}
二、内联函数
在函数定义前加关键字inline,导致该函数变成内联函数。
函数体很小,调用很频繁,引入inline(内联函数)
(1)inline影响编译器,在编译阶段对inline这种函数进行处理,系统尝试将调用该函数的动作替换为函数本体。通过这种方式来提升性能。
int abc = myfunc(5); //==> int abc = 1;
(2)inline只是我们开发者对编译器的一个建议,编译器可以尝试去做,也可以不做,这取决于编译器的诊断功能。
(3)内联函数的定义放到头文件中。这样需要用到这个函数的时候能够通过#include 把这个内联函数的源代码#include进来,以便找到这个函数的本体源代码并尝试将该函数的调用替换为函数体内的语句。
优缺点:
代码膨胀的问题;所以内联函数函数体尽量要小。
注意:各种编译器对inline的处理各不相同,inline函数尽量简单,代码尽量少。循环,分支,递归调用尽量不要出现在inline函数中,否则的话编译器很可能会因为你写这些代码的原因拒绝让这个函数称为inline函数。
1 constexpr int mf()
2 {
3 // ..写的特别简单
4 }
constexpr函数可以看成为更严格的内联函数。
#define 宏展开也类似于inline(内联函数)。
三、函数杂合用法总结
(1)函数返回类型为void,表示函数不返回任何类型。但是我们可以调用一个返回类型为void的函数,让它作为另一个返回类型是void的函数的返回值。
1 void funa(){}
2 void funb()
3 {
4 return funa();
5 //return;
6 }
(2)函数返回指针和返回引用的情况
//返回指针
int* myfunc()
{
// 存在隐患
int tmpval = 9;
return &tmpval; // 这个地址出了这个函数,地址被系统回收。
}
int main()
{
int *p = myfunc();
*p = 6; // 你向一个不存的地址写了数据
}
//返回引用
int& myfunc()
{
int tmpval = 9;
cout << &tmpval << endl;
return tmpval; // 也存在隐患 返回已经回收了地址的引用
}
int main()
{
int& k =myfunc();
cout << &k << endl;
k = 10; //你向一个不存的地址写了数据
// 返回值
int x = myfunc(); // 这种写法是OK的
cout << &x << endl;
x = 20;
return 0;
}
(3)不带形参的函数定义()、(void)
1 int myfunc(void)
2 {
3 return 1;
4 }
(4)如果一个函数如果不调用的话,则该函数只有声明部分,没有定义部分。
(5)普通函数定义只能定义(.cpp)一次,声明可以声明(.h)多次。
(6)void func(int& a,int& b); 函数可以通过参数引用来实现返回多个值。
(7)C++中,函数允许同名,但是形参列表的参数类型或者数量必须有明显区别。
void fs(int i);
void fs(float i);
void fs(int i , int b);
四、const char*、char const*、char* const三者的区别
(1)const char* p;
int main()
{
char str[] = “I Love China”;
const char* p; // p指向的东西不能通过p来修改(p所指向的目标,目标中的内容不能通过p来改变)
p = str;
//*p = ‘Y’; // 语法错误
p++;
str[0] = ‘Y’;
return 0;
}
(2)char const* 与 const char*等价
(3)char* const
int main()
{
char str[] = “I Love China”;
char* const p = str; // 定义的时候必须初始化
// p一旦指向了一个东西之后,就不能再指向其他东西了。
*p = ‘Y’; // 但是可以修改p指向的目标中的内容
//char const* const p = str;
const char* const p =str; // p的指向不能变,p指向的内容也不能通过p来改变
int i = 100;
const int& a =i // 代表a的内容不能通过a自己来修改;
i=1000;
//a = 50; // 不合法
//int &b = 3; // 错误
const int &b = 3; // OK
//b = 45; // 错误
cout << b << endl;
return 0;
}
五、函数形参中带const
把形参写成const的好处:
(1)可以防止你无意中修改了形参值导致实参值无意被修改;
(2)实参类型可以更灵活。
struct student{int num;};
void fs(const student &stu)
{
//stu.num = 1010; // 加const stu不可以修改
}
void fs2(const int i)
{
//i=100; // 不可以修改
}
int main()
{
student student1;
student1.num = 100;
fs(student1);
cout << student1.num << endl;
return 0;
}
选自:https://blog.csdn.net/lengxinxuexic/article/details/79741625
还可以看一下:内联函数的意义和使用 https://blog.csdn.net/yellow_hill/article/details/72017551
c++内联函数解析(inline)
一、基本定义
inline是C++语言中的一个关键字,可以用于程序中定义内联函数,inline的引进使内联函数的定义更加简单。说到内联函数,这里给出比较常见的定义,内联函数是C++中的一种特殊函数,它可以像普通函数一样被调用,但是在调用时并不通过函数调用的机制而是通过将函数体直接插入调用处来实现的,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率。一般来说inline用于定义类的成员函数。
二、inline的基本使用
inline的使用比较简单,只需要在声明或者定义函数时在头部加上inline关键字即可,格式如下
1 inline 返回值类型 函数名(函数参数){
2 //此处定义函数体
3 }
一般来说,inline适用的函数有两种,一种是在类内定义的成员函数,另一种是在类内声明,类外定义的成员函数,对于这两种情况inline的使用有一些不同:
(1)类内定义成员函数
这种情况下,我们可以不用在函数头部加inline关键字,因为编译器会自动将类内定义的函数声明为内联函数,代码如下:
class temp{
public:
int amount;
//构造函数
temp(int amount){
this->amount = amount;
}
//普通成员函数,在类内定义时前面可以不加inline
void print_amount(){
cout << this-> amount;
}
}
从上面的代码可以看出,在类内定义函数时,可以不加inline关键字,编译器会自动将类内定义的函数(构造函数、析构函数、普通成员函数等)设置为内联,具有内联函数调用的性质。
(2) 类内声明函数,在类外定义函数
根据C++编译器的规则,这种情况下如果想将该函数设置为内联函数,则可以在类内声明时不加inline关键字,而在类外定义函数时加上inline关键字,代码如下所示:
class temp{
public:
int amount;
//构造函数
temp(int amount){
this->amount = amount;
}
//普通成员函数,在类内声明时前面可以不加inline
void print_amount()
}
//在类外定义函数体,必须在前面加上inline关键字
inline void temp::print_amount(){
cout << amount << endl;
}
从上面代码我们可以看出,类内声明可以不用加上inline关键字,但是类外定义函数体时必须要加上,这样才能保证编译器能够识别其为内联函数。
另外,我们可以在声明函数和定义函数的同时写inline,也可以只在函数声明时加inline,而定义函数时不加inline。只要在调用该函数之前把inline的信息告知编译系统,编译系统就会在处理函数调用时按内联函数处理。也就是说,上面说的几种方法都可以实现一个内联函数的定义,根据自己的需要来写即可。
三、内联函数的优缺点
优点:
1.inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高。
2.很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。(宏替换不会检查参数类型,安全隐患较大)
3.inline函数可以作为一个类的成员函数,与类的普通成员函数作用相同,可以访问一个类的私有成员和保护成员。内联函数可以用于替代一般的宏定义,最重要的应用在于类的存取函数的定义上面。
缺点:
1.内联函数具有一定的局限性,内联函数的函数体一般来说不能太大,如果内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。(换句话说就是,你使用内联函数,只不过是向编译器提出一个申请,编译器可以拒绝你的申请)这样,内联函数就和普通函数执行效率一样了。
2.inline说明对编译器来说只是一种建议,编译器可以选择忽略这个建议。比如,你将一个长达1000多行的函数指定为inline,编译器就会忽略这个inline,将这个函数还原成普通函数,因此并不是说把一个函数定义为inline函数就一定会被编译器识别为内联函数,具体取决于编译器的实现和函数体的大小。
四、内联函数和宏定义的区别
内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。内联函数与带参数的宏定义进行下比较,它们的代码效率是一样,但是内联欢函数要优于宏定义,因为内联函数遵循的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关联上内联扩展,将与一般函数一样进行调用,比较方便。
另外,宏定义在使用时只是简单的文本替换,并没有做严格的参数检查,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。
C++的inline的提出就是为了完全取代宏定义,因为inline函数取消了宏定义的缺点,又很好地继承了宏定义的优点,《Effective C++》中就提到了尽量使用Inline替代宏定义的条款,足以说明inline的作用之大。
五、使用注意事项
1.内联函数不能包括复杂的控制语句,如循环语句和switch语句;
2.只将规模很小(一般5个语句一下)而使用频繁的函数声明为内联函数。在函数规模很小的情况下,函数调用的时间开销可能相当于甚至超过执行函数本身的时间,把它定义为内联函数,可大大减少程序运行时间。
C++内联函数
对于经常要使用的代码段,为了方便使用会将其封装成函数。然而在调用函数时会建立栈帧,增加了额外的开销。为了节省开销,在C语言中会使用宏替换。然而宏具有一些缺点:
1)不能调试;
2)由于宏使用简单的文本替换,对于有些情况,在同一个作用域中同一个宏使用两次会出现重定义错误。
#define SWAP(a,b)\
int tmp = a; \
a = b; \
b = tmp;
int main()
{
int x = 10;
int y = 5;
SWAP(x, y);
SWAP(x, y);//此处会出错
system("pause");
return 0;
}
在C++中,以inline修饰的函数叫做内联函数,编译时C++编译器会调用内联函数的地方展开,没有函数压栈开销,内联函数提升程序运行的效率。
1)内联函数是以空间换时间的做法,省去调用函数的额外开销。所以代码很长或者有循环/递归的函数不适宜使用内联。
2)inline对编译器而言只是一个建议,如果定义的函数体内有递归/循环等,编译器优化时会自动忽略掉内联。
3)Inline必须与函数定义放在一起,才能成为内联函数,仅将内联放在声明前是不起作用的。
4)一般情况下,内联函数只会用在函数内容非常简单的情况,如果内联函数代码过多会造成可读性差等问题。
class A
{
public:
/*inline*/ void fun();//放在这里不起作用
private:
int a;
};
inline void A::fun()
{
cout << "inline void A::fun()" << endl;
}
定义在类内的成员函数默认定义为内联函数。可以使用所在类的保护成员和私有成员。
class A
{
public:
void fun()
{
//默认为内联
}
private:
int a;
};
1)为什么inline能够很好的取代预定义?
1.Inline定义的内联函数,函数的代码在 使用时直接被替换,没有调用开销,提升了效率。
2.类的内联函数也是函数,编译器在调用时会检查它的参数,消除隐患
3.定义在类内的成员函数默认定义为内联函数。可以使用所在类的保护成员和私有成员。