C++中函数名字前的&或*

简介: C++中函数名字前的&或*

一、&


函数名称前面加引用符号“&”的意思是返回引用类型。


格式:类型标识符 &函数名(形参列表及类型说明){ //函数体}。


在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error。



&扩展资料


使用返回引用类型注意事项:


1、不能返回局部变量的引用。这条可以参照Effective C++的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。


2、不能返回函数内部new分配的内存的引用。这条可以参照Effective C++的Item。虽然不存在e799bee5baa6e59b9ee7ad9431333431373839局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。


例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。


3、可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。


如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。


二、*


函数名称前面加指针符号“*”,代表它是函数指针。


函数指针是一个指向函数的指针,函数指针表示一个函数的入口地址。使用函数指针的好处就是在处理“在运行时根据数据的具体状态来选择相应的处理方式”这种需求时更加灵活。


定义一个函数指针


指针是变量,所以函数指针也是变量,因此可以使用变量定义的方式来定义函数指针,对于普通的指针,可以这么定义:


int a=10;
int *pa=&a;


这里,pa是一个指向整型的指针,定义这个指针的形式为:


int * pa;


它说明了两点:(1)这是一个指针(2)这是一个指向整型变量的指针


以下给出三个函数指针定义的形式,第一个是C语言的函数指针,第二个和第三个是C++的函数指针的定义形式(都是指向非静态函数成员的函数指针):


int (*pFunction)(float,char,char)=NULL;
int (MyClass::*pMemberFunction)(float,char,char)=NULL;
int (MyClass::*pConstMemberFunction)(float,char,char) const=NULL;


首先,要记住一点的就是形式一定要具备完备性,能表达出我们所要表达的内容,即指向函数这个事实。我们知道普通变量指针可以指向对应类型的任何变量,同样函数指针也应该能够指向对应类型的任何变量。


对应的函数类型靠什么来确定?这个我们可以想一下C++的函数重载靠什么来区分不同的函数?


这里,函数类型是靠这几个方面来确定的:(1)函数的参数个数(2)函数的参数类型(3)函数的返回值类型。所以我们要设计一种形式,这种形式定义的函数指针能够准确的指向这种函数类型的任何函数。


在C语言中这种形式为:


返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);  
//注意这是在定义函数指针


定义变量的形式显然不是我们通常见到的这种形式:


类型名称 变量名称;


因为函数指针不加括号就会产生二义性,就像下面这个:


返回类型 *函数指针名称(参数类型,参数类型,参数类型,…);  //注意这是在定义函数


这样的定义形式定义了一个返回类型为‘返回类型*’参数为(参数类型,参数类型,参数类型,…)的函数而不是函数指针了。


接下来,对于C++来说


下面这样的定义形式也就不难理解了(加上类名称是为了区分不同类中定义的相同名称的成员函数):


返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….)


函数的调用规则


一般来说,不用太关注这个问题。调用规则主要是指函数被调用的方式,常见的有_stdcall,_fastcall,_pascal,_cdecl等规则。


不同的规则在参数压入堆栈的顺序是不同的,同时在有调用者清理压入堆栈的参数还是由被调用者清理压入堆栈的参数上也是不同的。一般来说,如果你没有显式的说明调用规则的话,编译器会统一按照_cdecl来处理。


给函数指针赋值和调用


给函数指针赋值,就是为函数指针指定一个函数名称。这个过程很简单,下面是两个例子:


int func1(float f,int a,int b){return f*a/b;}


int func2(float f,int a,int b){return f*a*b}


然后我们给函数指针pFunction赋值:


pFunction=func1;
pFunction=&func2;


上面这段代码说明了两个问题:


(1)一个函数指针可以多次赋值(引用不能这样)


(2)取地址符号是可选的,却是推荐使用的。


问题思考


我们可以思考一下为什么取地址符号是可选的,在普通的指针变量赋值时,如上面所示,需要加取地址符号,而这里却是可选的?这是由于要同时考虑到两个因素(1)避免二义性(2)形式一致性。


在普通指针赋值,需要加取地址符号是为了区别于将地址还是将内容赋给指针。而在函数赋值时没有这种考虑,因为这里的语义是清晰的,加上&符号是为了和普通指针变量一致—“因为一致的时候就不容易出错”。


最后我们来使用这个函数,以下两者调用函数的方法都是可以的


pFunction(10.0,’a’,’b’);
(*pFunction)(10.0,’a’,’b’);


同时函数指针可以当参数使用实现回调函数,让程序变得更加简单。


相关文章
|
4月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
94 0
|
7月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
356 6
|
12月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
12月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
445 6
|
12月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
108 3
|
12月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
197 0
C++ 多线程之线程管理函数
|
12月前
|
安全 编译器 C++
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
84 3
|
12月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
2055 1
|
12月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
139 1
|
12月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
183 0
C++入门6——模板(泛型编程、函数模板、类模板)