我是靠谱客的博主 勤劳路人,最近开发中收集的这篇文章主要介绍const 和引用作为形参,返回类型,函数类型详解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

const作用:const 声明的变量、(常)函数或者返回类型其主要作用就是不可改变(相当于常量),即变量值不能改变,返回值不能改变,(常)函数定义内部参数不能改变。下面依次介绍。

1. 临时变量、引用参数和 const声明的形参
在参数传递过程中,如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为 const 引用时,C++才允许这么做,但以前不是这样。如果引用参数是 const,则编译器将在下面两种情况下生成临时变量:

实参的类型正确,但不是左值;
实参的类型不正确,但可以转换为正确的类型。
注:左值参数是可以被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值(可以通过地址访问)。非左值包括字面常量(如 10.0)和包含多项的表达式(如 x + y)。

引用参数声明为常量数据的引用特点:

使用 const 可以避免无意中修改数据的编程错误;
使用 const 使函数能够处理 const 和非 const 实参,否则将只能接受非 const 数据;
使用 const 引用使函数能够正确生成并使用临时变量。
因此,应尽量将引用类型形参声明为 const 类型

double refcube(const double &ra)
{ return ra * ra * ra; }


//有如下调用方式
double side = 3.0;
double *pd = &side;
double &rd = side;
long edge = 5L;
double lens[4] = { 2.0, 5.0, 10.0, 20.0 };
double c1 = refcube(side);          //ra is side
double c2 = refcube(*pd);           //ra is *pd is side
double c3 = refcube(rd);            //ra is rd is side
double c4 = refcube(lens[2]);       //ra is lens[2]
double c5 = refcube(edge);          //ra is temporary(临时的) variable
double c6 = refcube(7.0);           //ra is temporary variable
double c7 = refcube(side + 10.0);   //ra is temporary variable
参数 side 、len[2] 、 rd 和 *pd 都是有名称的、double 类型的数据对象,因此可以为其创建引用,而不需要临时变量。然而 edge 虽然是变量,但是类型却不正确,double 引用不能指向 long。另一方面,参数 7.0 和 side + 10 的类型都正确,但没有名称,在这种情况下,编译器将生成一个临时匿名变量,并让 ra 指向它。这些临时变量只在函数调用期间存在,此后编译器便可以随意将其删除。那为什么对于 const引用这种行为是可行的,而对于普通的引用是不行的呢?

void swapr(int &a, int &b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
 
//执行下面语句
long a = 3, b = 5;
swapr(a, b);
如果顺利通过编译(有些优秀的编译器会提示错误),则 a 和 b  的值没有发生交换。因为这里的类型不匹配,编译器将创建两个临时 int 变量,将他们初始化为 3 和 5 ,然后交换临时变量的内容,而a 和 b 保持不变。如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决的方法是,禁止创建临时变量,现在的 C++编译器一般都是这样的,禁止创建临时变量。

对于开始写的 refcube() 函数。该函数的目的只是使用传递的值,而不是修改它们,因此临时变量不会造成任何不利的影响,反而会使函数在处理的参数种类方面更便利。因此,如果声明将引用指定为 const ,C++将在必要时生成临时变量。实际上,对于形参为 const引用的 C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。

 2.返回引用和返回 const 引用
引用主要应用在结构和类的参数传递中,主要原因是可以节省内存开销,节省了了传递时拷贝构造函数和数据备份的时间和内存空间。注意,应避免返回函数终止时不再存在的内存单元引用,即返回临时变量的引用是不允许的。

//结构体声明
struct free_throws
{
    std::string name;
    int made;
    int attempts;
    float percent;
}
 
//打印函数定义
void display(const free_throws &ft)
{
    using std::cout;
    cout << "name: " << ft.name << 'n';
    cout << " Made: " << ft.made << 't';
    cout << "Attempts: " << ft.attempts << 't';
    cout << "Percent: " << ft.percent << 'n';
}
 
//某计算函数定义
free_throws& accumulate(free_throws& target, const free_throws& source)
{
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);  //根据 made 和 attempts 值计算出 percent
    return target;
}
有以下几条调用:

1.display(accumulate(team, two));

首先,将结构对象 team 作为第一个参数传递给了 accumulate()。这意味着,在 accumulate()中,target 指向的是 team。函数 accumulate()修改 team,再返回指向它的引用。注意到返回语句

return target;

光看这条语句并不能知道返回的是引用,但函数头和原型指出了这一点:

free_throws& accumulate(free_throws& target, const free_throws& source)

如果返回类型被声明为 free_throws 而不是 free_throws& ,上述语句将返回 target(也就是 team)的拷贝。但返回类型为引用,这意味着返回的是最初传递给 accumulate()的 team 对象。

接下来,将 accumulate()的返回值作为参数传递给了display()。display()的参数为引用,这意味着 display()中的 ft 指向的是 team,因此将显示 team 的内容。所以下述代码:

display(accumulate(team, two));

与下面的代码等效:

accumulate(team, two);

display(team);

2.accumulate(dup, five) = four;

这条语句将赋值给函数调用,这是可行的,因为函数的返回值是一个引用。其效果如下:首先 five 的数据添加到 dup 中,再使用 four 的内容覆盖 dup 的内容。在赋值语句中,左边必须是可修改的左值,也就是说,在赋值表达式中,左边的子表达式必须标识一个可修改的内存块。在这里,函数返回指向 dup 的引用,它确实标识的是一个这样的内存块,因此这条语句是合法的。如果函数 accumulate()按值返回,这条语句将不能通过编译。 为什么常规函数返回值是右值呢?这是因为这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。

由于返回的是 dup 的引用,因此上述代码与下面的代码等效:

accumulate(dup, five);

dup = four;

其中第二条语句消除了第一条语句所做的工作,因此在原始赋值语句使用 accumulate()的方式并不友好。假设我们想使用引用返回值,但又不允许执行像给 accumulate()赋值这样的操作,只需将返回类型声明为 const 的引用:

const free_throws&
    accumulate(free_throws& target, const free_throws& source);
现在返回类型是 const,是不可修改的左值,因此下面的赋值语句不合法:

accumulate(dup, five) = four;

但下面的语句是合法的:

display(accumulate(team, two));

这是因为 display()的形参也是 const free_throws& 类型。但下面的语句不合法,因为 accumulate()的第一个形参不是 const:

accumulate(accumulate(team, three), four);

这影响大吗?这里而言是不大的,因为可以这样使用:

accumulate(team, three);

accumulate(team, four);

另外,还可以在赋值语句右边使用 accumulate()。

3.常成员函数
在类中使用关键字 const 说明的函数为常成员函数,常成员函数的说明格式如下:

类型  函数名(参数表)  const;

const 是函数类型的一个组成部分,因此在声明函数和定义函数时都要有关键字 const。 在调用时不必加 const。同时 const 也可以作为函数重载的区分标志。

常成员函数可以访问常数据成员,也可以访问普通数据成员。常数据成员可以被常成员函数访问,也可以被普通成员函数访问。具体情况如下表:
普通成员函数和常成员函数的访问特性比较
函数类型    普通数据成员    常数据成员    常对象的数据成员
普通成员函数    
可以访问,也可以改变值

可以访问,但不可以改变值    不允许访问和改变值 
常成员函数    可以访问,但不可以改变值    可以访问,但不可以改变值    可以访问,但不可以改变值
如果将一个对象说明为常对象,则通过该对象只能调用它的常成员函数,而不能调用普通成员函数。常成员函数是常对象唯一的对外接口,这是 C++从语法机制上对常对象的保护。
常成员函数不能更新对象的数据成员,也不能调用该类中的普通成员函数,这就保证了在常函数中绝对不会更新数据成员的值。
————————————————
版权声明:本文为CSDN博主「一丁_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41291253/article/details/101202246

最后

以上就是勤劳路人为你收集整理的const 和引用作为形参,返回类型,函数类型详解的全部内容,希望文章能够帮你解决const 和引用作为形参,返回类型,函数类型详解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部