C++示例分析内联函数与引用变量及函数重载的使用

 更新时间:2022年08月22日 11:38:36   作者:Shawn-Summer  
为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数

1.内联函数

1.1为什么使用内联函数

  • 减少上下文切换,加快程序运行速度。
  • 是对C语言中的宏函数的改进。

1.2语法

#include<iostream>
using namespace std;
inline double square(double x){
    return x*x;
}
int main(){
    cout<<square(2.2)<<endl;
}

其实就是在函数声明或者定义前加上关键字inline

2.引用变量

2.1为什么要使用引用变量

  • 主要用途是用作函数的形参。通过引用变量做参数,函数将使用原始数据,而不是其副本。
  • 高效。

2.2语法

引用实际上就是定义一个别名。看看下面代码:

#include<iostream>
using namespace std;
int main(){
    int a=50;
    int &b=a;//定义并初始化,这里b是a的引用。
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    cout<<"address of a:"<<&a<<endl;
    cout<<"address of b:"<<&b<<endl;
    b=100;
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    int c=200;
    b=c;//试图将b作为c的引用。行不通。
    cout<<"a:"<<a<<endl;
    cout<<"b:"<<b<<endl;
    cout<<"c:"<<c<<endl;
    cout<<"address of a:"<<&a<<endl;
    cout<<"address of b:"<<&b<<endl;
    cout<<"address of c:"<<&c<<endl;    
}

a:50
b:50
address of a:0x61fe14
address of b:0x61fe14
a:100
b:100
a:200
b:200
c:200
address of a:0x61fe14
address of b:0x61fe14
address of c:0x61fe10

a和b的数据地址是一样的,这说明b相当于a的别名,我们改变b的值,也会改变a的值,而且后面我们试图将b转变为c的引用,但是行不通,b=c这个代码做的是赋值语句,相当于a=c.

引用和指针的区别

引用在声明的时候必须初始化

int &b;这句话是不允许的。

引用的本质就是指针常量。因为引用变量一旦初始化就不能更改。

int &b=aint* const p=&a 这两句中b*p是一模一样的。

引用作为函数参数

#include<iostream>
using namespace std;
void swap(int &a,int &b){
    int c;
    c=a;
    a=b;
    b=c;
}
int main(){
int a=2;
int b=3;
swap(a,b);
cout<<a<<b<<endl;
}

可以看出把引用作为参数的函数,只需在声明时,把参数设置成引用即可。

临时变量

试想一下,在参数传递过程中,我们把常数或者错误类型的实参,传给引用参数,会发生什么?这个引用参数会变成这个实参的引用吗?显然不会,因为常数不能修改,引用是错误的,正如int &a=2;会报错一样;错误类型的实参,也不能直接引用。

为了解决这个事,c++允许临时变量的产生。但是只有const引用才会产生临时变量,const引用不允许变量发生赋值。

总结来说,临时变量的产生条件是,在传参给const引用参数时:

实参不是左值.(左值指的是const变量 和 常规变量。)

实参类型不正确且可类型转换。

所以说,为了使得引用参数传递的兼容性和安全性,请多使用const。

#include<iostream>
using namespace std;
double square(const double &a){
    return a*a*a;
}
int main(){
    int a=3;
    cout<<square(3+a)<<endl;
}

可以看出来这里square函数可以接受非左值,类型错误的实参。

你可能觉得这样做很复杂,直接使用按值传参就行了。double square(double a)double square(const double &a),从效果来说,这两一样,但是我们使用第二种传参的好处是高效,试想一下我们同时传一个double类型的变量,const引用传参不需要数据的拷贝,更快。

右值引用

采用 && 来对右值做引用,这么做的目的是用来实现移动语义。

#include<iostream>
using namespace std;
int main(){
    double a=3.1;
    double && b=a*1.2+2.3;
    cout<<b<<endl;
    b=3;
    cout<<a<<endl;
    cout<<b<<endl;
}

6.02
3.1 
3   

结构引用

引用非常适合于结构和类

#include<iostream>
using namespace std;
struct apple
{   
    string name;
    double weight;
};
apple & swap(apple &a, apple &b){
    apple temp;
    temp=a;
    a=b;
    b=temp;
    return a;
}
int main(){
apple a={"Bob",230};
apple b={"Alice",190};
swap(a,b);
cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl;
cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl;
swap(swap(a,b),b);
cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl;
cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl;
swap(swap(swap(a,b),b),b);
swap(swap(a,b),b);
cout<<"a:"<<endl<<"name:"<<a.name<<endl<<"weight:"<<a.weight<<endl<<endl;
cout<<"b:"<<endl<<"name:"<<b.name<<endl<<"weight:"<<b.weight<<endl<<endl;
}

a:
name:Alice
weight:190

b:
name:Bob
weight:230

a:
name:Alice
weight:190

b:
name:Bob
weight:230

a:
name:Bob
weight:230

b:
name:Alice
weight:190

swap()函数的返回值是一个引用变量,所以swap(swap(swap(a,b),b),b)是合法的,且它等价于swap(a,b)

为何要返回引用?高效。 因为传统返回机制,会把返回结果复制到一个临时位置。 但是应该避免返回 函数终止时不再存在的内存单元引用。例如避免返回临时变量的引用。

2.3对于C语言的改进

  • 用const引用传参传递 代替 按值传递。
  • 对于要修改原始数据的函数,采用引用传参方式。

3. 函数重载

3.1默认参数

默认参数指的是函数调用中省略了实参时自动使用的一个值。

如何设置默认值?必须通过函数原型。 例如这里的void display(int a,int n=999); 这里n=999 就是默认参数 默认参数的作用是,不给这个参数传参时,他会采用默认值。

#include<iostream>
using namespace std;
void display(int a,int n=999);
int main(){
display(1);
display(3,31);
}
void display(int a,int n){
    cout<<a<<endl;
    cout<<n<<endl;
}

1
999
3
31

3.2函数重载

默认参数能让我们使用不同数目的参数调用同一个函数,而函数重载能让我们使用多个同名的函数。

函数重载的关键是函数的参数列表–也称函数特征标。如果两个函数的名字和特征标相同,那么这两个函数就完全相同。C++允许定义名称相同,函数特征标不同的函数,这就是所谓的函数重载。

#include<iostream>
using namespace std;
struct apple{
    string name;
    double weight;
};
void print(int);
void print(double);
void print(char *);
void print(apple &a,string str="apple",double w=100);
int main(){
    int a=2;
    double b=3.14;
    char c[10]="hello!";
    apple d;
    print(a);
    print(b);
    print(c);
    print(d);
    print(d,"Alice",250);
}
void print(int a){
    cout<<"int ="<<a<<endl;
}
void print(double a){
    cout<<"double ="<<a<<endl;
}
void print(char * a){
    cout<<"char* ="<<a<<endl;
}
void print(apple &a,string str,double b){
    a.name=str;
    a.weight=b;
    cout<<"the name:"<<a.name<<endl;
    cout<<"the weight:"<<a.weight<<endl;
}

int =2        
double =3.14  
char* =hello!
the name:apple
the weight:100
the name:Alice
the weight:250

可以看出来print函数有多个重载,现代编译器会根据你传递的参数类型,而选择最匹配的函数。

关于函数重载的一些细节

  1. 类型引用和类型本身视为同一个特征标,例如double cube(double x);double cube(double &x);是不能共存的。
  2. 匹配函数时,会区分const和非const变量,例如 void display(char* a);void display(const char* a);是函数重载。
  3. 请记住是特征标,而不是函数类型使得可以对函数进行重载。例如 long gronk(int,float);double gronk(int,float);是不能共存的。

函数重载的shortcoming

函数重载在实现同函数名多种功能的同时,也应当付出代价。

标准类型转化、强制匹配能力下降。

#include<iostream>
using namespace std;
void print(double);
void print(char *);
int main(){
    int a=2;
    double b=3.14;
    char c[10]="hello!";
    print(a);
    print(b);
    print(c);
}
void print(double a){
    cout<<"double ="<<a<<endl;
}
void print(char * a){
    cout<<"char* ="<<a<<endl;
}

double =2     
double =3.14  
char* =hello!

可以看出来这里print(a)这里a是int类型,编译器会将其类型转化成double,然后调用对应函数。

但是,我们稍微改动一下代码

#include<iostream>
using namespace std;
void print(int);
void print(double);
void print(char *);
int main(){
    int a=2;
    double b=3.14;
    char c[10]="hello!";
    print(a);
    print(b);
    print(c);
    print(12L);
}
void print(int a){
    cout<<"int ="<<a<<endl;
}
void print(double a){
    cout<<"double ="<<a<<endl;
}
void print(char * a){
    cout<<"char* ="<<a<<endl;
}

这段代码中print(12L);会报错,因为12L是long类型的常量,如果我们试着强制匹配会发现,12L既可以转化成int类型,也可以转化成double类型,从而编译器不知道到底调用哪个函数。

不要滥用函数重载

仅当函数基本执行相同的任务,但使用不同类型的数据时,才应当使用函数重载。

到此这篇关于C++示例分析内联函数与引用变量及函数重载的使用的文章就介绍到这了,更多相关C++内联函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C/C++ Qt 选择夹TabWidget组件实现导航栏切换

    C/C++ Qt 选择夹TabWidget组件实现导航栏切换

    Tab切换在很多地方都可以使用的到,本文就使用TabWidget组件来实现一下,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • OpenCV实现相机标定板

    OpenCV实现相机标定板

    这篇文章主要为大家详细介绍了OpenCV实现相机标定板,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • C++实现LeetCode(6.字型转换字符串)

    C++实现LeetCode(6.字型转换字符串)

    这篇文章主要介绍了C++实现LeetCode(6.字型转换字符串),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言 数据结构中栈的实现代码

    C语言 数据结构中栈的实现代码

    这篇文章主要介绍了C语言 数据结构中栈的实现代码的相关资料,需要的朋友可以参考下
    2016-10-10
  • C++详解非类型模板参数Nontype与Template及Parameters的使用

    C++详解非类型模板参数Nontype与Template及Parameters的使用

    除了类型可以作为模板参数,普通值也可以作为模板函数,即非类型模板参数(Nontype Template Parameters)。下面让我们一起了解一下
    2022-06-06
  • C++中关于Crt的内存泄漏检测的分析介绍

    C++中关于Crt的内存泄漏检测的分析介绍

    本篇文章介绍了,在C++中关于Crt的内存泄漏检测的分析说明。需要的朋友参考下
    2013-04-04
  • C语言中条件编译详解

    C语言中条件编译详解

    预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。条件编译有三种形式,下面分别介绍。
    2017-05-05
  • strncpy与snprintf 的用法比较

    strncpy与snprintf 的用法比较

    以下是对strncpy与snprintf的具体用法以及区别进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-07-07
  • C/C++ Qt StatusBar底部状态栏应用教程

    C/C++ Qt StatusBar底部状态栏应用教程

    Qt窗体中默认会附加一个QstatusBar组件,状态栏组件位于主窗体的最下方,其作用是提供一个工具提示功能。本文主要介绍了StatusBar底部状态栏的应用教程,需要的同学可以学习一下
    2021-12-12
  • 利用C语言实现“百马百担”问题方法示例

    利用C语言实现“百马百担”问题方法示例

    百马百担是道经典的算法题,下面这篇文章主要给大家介绍了利用C语言实现“百马百担”问题的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-12-12

最新评论