- 浏览: 34897 次
- 性别:
- 来自: 南京
函数由函数名以及一组操作数类型唯一地表示。函数的操作数,也即形参,在一对圆括号中声明,形参与形参之间以逗号分隔。函数执行的运算在一个称为函数体的块语句中定义。每一个函数都有一个相关联的返回类型。
C++ 语言使用调用操作符(即一对圆括号)实现函数的调用。
函数体是一个作用域
类似于局部变量,函数的形参为函数提供了已命名的局部存储空间。它们之间的差别在于形参是在函数的形参表中定义的,并由调用函数时传递函数的实参初始化。
实参则是一个表达式。它可以是变量或字面值常量,甚至是包含一个或几个操作符的表达式。
实参个数必须与函数的形参个数完全相同,实参必须具有与形参类型相同、或者能隐式转换为形参类型的数据类型。
函数的返回类型可以是内置类型(如 int 或者 double)、类类型或复合类型(如 int& 或 string*),还可以是 void 类型,表示该函数不返回任何值。
函数不能返回另一个函数或者内置数组类型,但可以返回指向函数的指针,或指向数组元素的指针的指针
函数必须指定返回类型,在定义或声明函数时,没有显式指定返回类型是不合法的
函数形参表可以为空,但不能省略。没有任何形参的函数可以用空形参表或含有单个关键字 void 的形参表来表示。
如果两个参数具有相同的类型,则其类型必须重复声明
参数表中不能出现同名的参数
参数传递
形参的初始化与变量的初始化一样:如果形参具有非引用类型,则复制实参的值,如果形参为引用类型,则它只是实参的别名。
普通的非引用类型的参数通过复制对应的实参实现初始化,不会修改实参的值。
非引用形参表示对应实参的局部副本。对这类形参的修改仅仅改变了局部副本的值。一旦函数执行结束,这些局部变量的值也就没有了。
如果函数将新指针赋给形参,主调函数使用的实参指针的值没有改变。
如果函数形参是非 const 类型的指针,则函数可通过指针实现赋值,修改指针所指向对象的值
如果保护指针指向的值,则形参需定义为指向 const 对象的指针
在调用函数时,如果该函数使用非引用的形参(const、非const),则既可给该函数传递 const 实参也可传递非 const 的实参,因为是以副本的形式传递.
具有 const 形参或非 const 形参的函数并无区别
形参与 const 形参的等价性仅适用于非引用形参。
有 const 引用形参的函数与有非 const 引用形参的函数是不同的
不适宜复制实参的情况包括:
1. 当需要在函数中修改实参的值时
2. 当需要以大型对象作为实参传递时
3. 当没有办法实现对象的复制时
每次调用函数,引用形参被创建并与相应实参关联。
如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。
如果函数具有普通的非 const 引用形参,则显然不能通过 const 对象进行调用。比较容易忽略的是,调用这样的函数时,传递一个右值或具有需要转换的类型的对象同样是不允许的。
非 const 引用形参只能与完全同类型的非 const 对象关联。
应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用 const 对象初始化,也不能用字面值或产生右值的表达式实参初始化。
传递指向指针的引用
vector 和其他容器类型的形参
通常,函数不应该有 vector 或其他标准库容器类型的形参。调用含有普通的非引用 vector 形参的函数将会复制 vector 的每一个元素。
事实上,C++ 程序员倾向于通过传递指向容器中需要处理的元素的迭代器来传递容器
数组形参
处理数组的函数通常通过操纵指向数组指向数组中的元素的指针来处理数组。
形参的长度会引起误解
编译器忽略为任何数组形参指定的长度。当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型时是否匹配,而不会检查数组的长度。
不需要修改数组形参的元素时,函数应该将形参定义为指向 const 对象的指针
通过引用传递数组
如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身。在这种情况下,数组大小成为形参和实参类型的一部分。编译器检查数组的实参的大小与形参的大小是否匹配
f(int &arr[10]) // error: arr is an array of references
f(int (&arr)[10]) // ok: arr is a reference to an array of 10 ints
传递给函数的数组的处理
任何处理数组的程序都要确保程序停留在数组的边界内。
三种常见的编程技巧确保函数的操作不超出数组实参的边界。
1. 在数组本身放置一个标记来检测数组的结束,如C 风格字符串采用NULL作为结束标记
2. 传递指向数组第一个和最后一个元素的下一个位置的指针
3. 将第二个形参定义为表示数组的大小
main: 处理命令行选项
int main(int argc, char *argv[]) { ... }
int main(int argc, char **argv) { ... }
将实参传递给主函数 main 时,argv 中的第一个字符串(如果有的话)通常是程序的名字。
含有可变形参的函数
void foo(parm_list, ...);
void foo(...);
当需要传递给省略符形参时,大多数类类型对象都不能正确地复制,省略符暂停了类型检查机制
不带返回值的 return 语句只能用于返回类型为 void 的函数
隐式的 return 发生在函数的最后一个语句完成时
一般情况下,返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束,这种 return 的用法类似于循环结构中的 break 语句的作用
任何返回类型不是 void 的函数必须返回一个值,而且这个返回值的类型必须和函数的返回类型相同,或者能隐式转化为函数的返回类型。
尽管 C++ 不能确保结果的正确性,但能保证函数每一次 return 都返回适当类型的结果。
在含有 return 语句的循环后没有提供 return 语句是很危险的,因为大部分的编译器不能检测出这个漏洞,运行时会出现什么问题是不确定的。
主函数 main 的返回值,返回 0 表示程序运行成功,其他大部分返回值则表示失败,非 0 返回值的意义因机器不同而不同
cstdlib 头文件定义了两个预处理变量EXIT_FAILURE和EXIT_SUCCESS,分别用于表示程序运行成功和失败
如果返回类型不是引用,在调用函数的地方会将函数返回值复制给临时对象。当函数返回非引用类型时,其返回值既可以是局部对象,也可以是求解表达式的结果。
当函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。
理解返回引用至关重要的是:千万不能返回局部变量的引用。
返回引用的函数返回一个左值。因此,这样的函数可用于任何要求使用左值的地方
千万不要返回指向局部对象的指针
直接或间接调用自己的函数称为递归函数。
递归函数必须定义一个终止条件;否则,函数就会“永远”递归下去
主函数 main 不能调用自身。
函数声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型,但是不必对形参命名。这三个元素被称为函数原型,函数原型描述了函数的接口。
函数声明中的形参名会被忽略,如果在声明中给出了形参的名字,它应该用作辅助文档
函数应当在头文件中声明,并在源文件中定义。
义函数的源文件应包含声明该函数的头文件。
可为一个或多个形参定义默认值。但是,如果有一个形参具有默认实参,那么,它后面所有的形参都必须有默认实参。
调用包含默认实参的函数时,可以为该形参提供实参,也可以不提供。如果提供了实参,则它将覆盖默认的实参值;否则,函数将使用默认实参值。
函数调用的实参按位置解析,默认实参只能用来替换函数调用缺少的尾部实参。
默认情况下,局部变量的生命期局限于所在函数的每次执行期间。只有当定义它的函数被调用时才存在的对象称为自动对象。自动对象在每次调用函数时创建和撤销。
局部变量所对应的自动对象在函数控制经过变量定义语句时创建。
形参也是自动对象。形参所占用的存储空间在调用函数时创建,而在函数结束时撤销。
static 局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化。这种对象一旦被创建,在程序结束前都不会撤销。
将函数指定为 inline 函数,(通常)就是将它在程序中每个调用点上“内联地”展开。
inline 说明对于编译器来说只是一个建议,编译器可以选择忽略这个。
一般来说,内联机制适用于优化小的、只有几行的而且经常被调用的函数。
大多数的编译器都不支持递归函数的内联
内联函数应该在头文件中定义,这一点不同于其他函数。
内联函数应该在头文件中定义,这一点不同于其他函数。
在头文件中加入或修改 inline 函数时,使用了该头文件的所有源文件都必须重新编译。
出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数。
任何程序都仅有一个 main 函数的实例。main 函数不能重载。
函数重载和重复声明的区别
如果两个函数声明的返回类型和形参表完全匹配,则将第二个函数声明视为第一个的重复声明。如果两个函数的形参表完全相同,但返回类型不同,则第二个声明是错误的。
函数不能仅仅基于不同的返回类型而实现重载。
设计带有默认实参的函数,其中部分工作就是排列形参,使最少使用默认实参的形参排在最前,最可能使用默认实参的形参排在最后。
如果默认实参是一个表达式,而且默认值用作实参,则在调用函数时求解该表达式。
既可以在函数声明也可以在函数定义中指定默认实参。但是,在一个文件中,只能为一个形参指定默认实参一次。
通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中。
如果在函数定义的形参表中提供默认实参,那么只有在包含该函数定义的源文件中调用该函数时,默认实参才是有效的。
一般来说,局部地声明函数是一种不明智的选择。函数的声明应放在头文件中。
在 C++ 中,名字查找发生在类型检查之前。
函数匹配与实参转换
重载确定的三个步骤
1. 候选函数
与被调函数同名的函数,并且在调用点上,它的声明可见。
2. 选择可行函数
可行函数必须满足两个条件:第一,函数的形参个数与该调用的实参个数相同;第二,每一个实参的类型必须与对应形参的类型匹配,或者可被隐式转换为对应的形参类型。如果函数具有默认实参,则调用该函数时,所用的实参可能比实际需要的少。默认实参也是实参,在函数匹配过程中,它的处理方式与其他实参一样
。
3. 寻找最佳匹配(如果有的话)
为了确定最佳匹配,编译器将实参类型到相应形参类型转换划分等级。转换等级以降序排列如下:
(1) 精确匹配 实参与形参类型相同
(2) 通过类型提升实现的匹配
(3) 通过标准转换实现的匹配
(4) 通过类类型转换实现的匹配
必须注意的一个重点是较小的整型提升为 int 型
通过类型提升实现的转换优于其他标准转换
整数对象即使具有与枚举元素相同的值也不能用于调用期望获得枚举类型实参的函数
在使用有枚举类型形参的重载函数时,请记住:由于不同枚举类型的枚举常量值不相同,在函数重载确定过程中,不同的枚举类型会具有完全不同的行为。其枚举成员决定了它们提升的类型,而所提升的类型依赖于机器。
仅当形参是引用或指针时,形参是否为 const 才有影响。
注意不能基于指针本身是否为 const 来实现函数的重载
指向函数的指针
函数指针是指指向函数而非指向对象的指针
函数类型由其返回类型以及形参表确定,而与函数名无关
函数指针类型相当地冗长。使用 typedef 为指针类型定义同义词,可将函数指针的使用大大简化
在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针
可使用函数名对函数指针做初始化或赋值
直接引用函数名等效于在函数名上应用取地址操作符
函数指针只能通过同类型的函数或函数指针或 0 值常量表达式进行初始化或赋值。
将函数指针初始化为 0,表示该指针不指向任何函数。
指向不同函数类型的指针之间不存在转换
指向函数的指针可用于调用它所指向的函数
如果指向函数的指针没有初始化,或者具有 0 值,则该指针不能在函数调用中使用。只有当指针已经初始化,或被赋值为指向某个函数,方能安全地用来调用函数。
函数的形参可以是指向函数的指针
返回指向函数的指针
允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数。
C++ 语言允许使用函数指针指向重载的函数
指针的类型必须与重载函数的一个版本精确匹配
#include <iostream> #include <cstdlib> #include <string> #include <vector> using namespace std; // error: missing return type // ISO C++ forbids declaration of `test' with no type // test(double v1, double v2){ } void process() { /* ... */ } // implicit void parameter list //void process(void){ /* ... */ } equivalent declaration //int manip(int v1, v2) { /* ... */ } error int manip(int v1, int v2) { /* ... */ } // ok void fcn(const int i) { /* fcn can read but not write to i */ } //void fcn(int i) { /* ... */ } error: redefines fcn(int) int incr(int &val) { return ++val; } // swap values of two pointers to int void ptrswap(int *&v1, int *&v2) { int *tmp = v2; v2 = v1; v1 = tmp; } // pass iterators to the first and one past the last element to print void print(vector<int>::const_iterator beg,vector<int>::const_iterator end) { while (beg != end) { cout << *beg++; if (beg != end) { cout << " "; // no space after last element } } cout << endl; } // three equivalent definitions of printValues void printValues(int*) { /* ... */ } //void printValues(int[]) { /* ... */ } //void printValues(int[10]) { /* ... */ } // ok: parameter is a reference to an array; size of array is fixed void printValuesByRef(int (&arr)[10]) { for(size_t index=0; index!=10; ++index) { cout << arr[index] << " "; } cout << endl; } size_t count_calls() { static size_t ctr = 0; // value will persist across calls return ++ctr; } // inline version: find longer of two strings inline const string & shorterString(const string &s1, const string &s2) { return s1.size() < s2.size() ? s1 : s2; } void f(); void f(int); void f(int, int); void f(double, double = 3.14); enum Tokens{INLINE = 128, VIRTUAL = 129}; void ff(Tokens); void ff(int); void fff(int *){} //void fff(int *const){} error redeclaration // pf points to function returning bool that takes two const string references bool (*pf)(const string &, const string &); // declares a function named pf that returns a bool* bool *pd(const string &, const string &); // compares lengths of two strings bool lengthCompare(const string &, const string &); typedef bool (*cmpFcn)(const string &, const string &); // compares lengths of two strings bool lengthCompare(const string &, const string &); /* useBigger function's third parameter is a pointer to function * that function returns a bool and takes two const string references * two ways to specify that parameter: */ // third parameter is a function type and is automatically treated as a pointer to function void useBigger(const string &, const string &, bool(const string &, const string &)); // equivalent declaration: explicitly define the parameter as a pointer to function void useBigger(const string &, const string &, bool (*)(const string &, const string &)); // ff is a function taking an int and returning a function pointer // the function pointed to returns an int and takes an int* and an int int (*fs(int))(int*, int); // PF is a pointer to a function returning an int, taking an int* and an int typedef int (*PF)(int*, int); PF fd(int); // ff returns a pointer to function void ff(vector<double>); void ff(unsigned int); int main() { short v1 = 0; const int v2 = 42; int v3 = 33; //v3 = incr(v1); //error: v1 is not an int //v3 = incr(v2); error: v2 is const //v3 = incr(0); error: literals are not lvalues //v3 = incr(v1 + v2); error: addition doesn't yield an lvalue int v4 = incr(v3); // ok: v3 is a non const object type int int i = 10; int j = 20; int *pi = &i; // pi points to i int *pj = &j; // pj points to j cout << "Before ptrswap():\t*pi: " << *pi << "\t*pj: " << *pj << endl; ptrswap(pi, pj); // now pi points to j; pj points to i cout << "After ptrswap():\t*pi: " << *pi << "\t*pj: " << *pj << endl; int ia[10] = {0,1,2,3,4,5,6,7,8,9}; printValuesByRef(ia); for (size_t i = 0; i != 10; ++i) { cout << count_calls() << endl; } f(5.6); //calls void f(double, double) Tokens curTok = INLINE; ff(128); // exactly matches ff(int) ff(INLINE); // exactly matches ff(Tokens) ff(curTok); // exactly matches ff(Tokens) cmpFcn pf1 = 0; // ok: unbound pointer to function cmpFcn pf2 = lengthCompare; // ok: pointer type matches function's type cmpFcn pf3 = &lengthCompare; // the same sa above pf1 = lengthCompare; // ok: pointer type matches function's type pf2 = pf1; // ok: pointer types match cmpFcn pf = lengthCompare; lengthCompare("hi", "bye"); // direct call pf("hi", "bye"); // equivalent call: pf1 implicitly dereferenced (*pf)("hi", "bye"); // equivalent call: pf1 explicitly dereferenced // which function does pf1 refer to? void (*pf4)(unsigned int) = &ff; // ff(unsigned) if(true) { return EXIT_SUCCESS; } } void f() { cout << "f() ..." << endl; } void f(int) { cout << "f(int) ..." << endl; } void f(int, int) { cout << "f(int ,int) ..." << endl; } void f(double, double) { cout << "f(double, double = 3.14) ..." << endl; } void ff(Tokens) { cout << "ff(Tokens) ..." << endl; } void ff(int) { cout << "ff(int) ..." << endl; } bool lengthCompare(const string &s1, const string &s2) { return s1 < s2 ; } void ff(vector<double>) { cout << "ff(vector<double>) ..." << endl; } void ff(unsigned int) { cout << "ff(unsigned int) ..." << endl; }
发表评论
-
析构函数 管理指针成员
2008-02-23 20:10 4408析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为 ... -
复制构造函数
2008-02-19 00:36 5254只有单个形参,而且该形参是对本类类型对象的引用(常用 cons ... -
友元 static 类成员
2008-02-17 21:09 4560友元机制允许一个类将 ... -
名字查找 构造函数 explicit
2008-02-16 20:50 4026每个类都定义了自己的 ... -
成员函数 this指针 可变数据成员
2008-02-16 00:11 4147类的成员函数可以访问 ... -
类class(一)
2008-02-14 23:30 3602最简单地说,类就是定义了一个新的类型和一个新作用域每个类可以没 ... -
输入输出IO
2008-02-12 22:00 4219每一个 IO 头文件都定义了 char 和 wchar_t 类 ... -
语句(...)
2008-02-12 21:55 4219简单语句C++ 中,大多数 ... -
表达式
2008-02-12 21:52 3229表达式由一个或多个操 ... -
数组和指针
2008-02-12 21:48 5173数组是由类型名、标识符和维数组成的复合数据类型数组也是一种存储 ... -
标准库类型
2008-02-12 21:35 3093string 类型支持长度可变 ... -
变量和基本类型
2008-02-12 21:12 3980类型是所有程序的基础,类型告诉我们数据代表什么意思以及可以对数 ... -
Hello world续
2008-02-04 00:15 2921/*the first C++ program*/ #inc ... -
经典再现 Hello world
2008-02-03 01:54 3806语言:ANSI C++,即标准C++编译和运行环境:红旗 Re ...
相关推荐
本文将通过一个具体的示例,深入解析MATLAB中子函数function的用法,帮助读者更好地理解和掌握这一关键概念。 ### MATLAB中的子函数 在MATLAB中,子函数是一种被定义在主函数脚本或函数文件之后的函数,它们只能被...
在创建标量函数时,其语法包括`CREATE FUNCTION`,定义参数列表,返回值类型,并使用`BEGIN...END`块来编写函数体。例如,创建一个查找员工工号的函数`F_GONGHAO`,它接受一个员工姓名作为输入,返回相应的工号: ...
VB程序实例-自定义函数Function用法.zip
javascript的关于函数一些特性的测试函数
在 matlab function模块中编写的饱和函数代码,可以直接移植到模块中,运行可靠,并且可以根据需求更改斜率。
最新的查询谷歌PR值函数(接口),CH值最新算法,绝对准确。 使用方法: require_once 'googlepr.php'; echo getPR('http://yanglu.org'); ?> 显示结果为PR数值
在MATLAB环境中,s-function(系统函数)是一种高级的编程工具,用于构建定制的Simulink模块,以处理复杂的系统模型或实现特定的算法。在本案例中,s-function被应用来解决一个倒立摆的非线性问题。倒立摆是一个经典...
在上述代码中,`AnotherFunction` 可以是一个接受 `MyCallbackDelegate` 类型参数的函数,它可以在适当的时候调用传入的回调函数。这样,即使VB不支持函数指针,我们依然可以实现回调机制。 回调函数在VB中的应用...
这段代码解释了foo和其构造函数Foo和Foo的构造函数Function的关系。 Object:顶层对象 ---------------- Object是最顶层的对象,所有的对象都将继承Object的原型。但是,你也要明确的知道Object也是一个函数对象,...
methodName[String|opt]:匿名函数需显式传入目标函数名eg:this.Begin = function(){....}; } [bool]unhook:params{ realFunc[String|must]:用于保存原始函数的函数名称,用于unHook; funcName[String|must]:被Hook...
在C++中,我们可以通过typedef或者std::function来声明类函数指针类型。例如: ```cpp class MyClass { public: void myFunction(int arg); }; typedef void (MyClass::*MyClassFuncPtr)(int); // 类函数指针类型...
C语言程序设计-编写函数fun对主程序中用户输入的具有10个数据的数组a按由大到小排序,并在主程序中输出排序结果;
7. 其他功能:ABAP函数模块还包括XML处理(FUNCTION GROUP 'XML_utilities')、Web服务(FUNCTION GROUP 'SOAP')、批处理处理(FUNCTION GROUP 'JOB_QUEUES')等,涵盖了企业信息系统开发的各个方面。 通过“ABAP ...
### 回调函数Callback Function #### 一、基本概念 **回调函数**是一种常见的编程模式,广泛应用于多种编程语言中,特别是在C/C++等语言中非常常见。简单来说,回调函数是指通过函数指针传递的一个函数,该函数会...
【AI 大模型】函数调用 Function Calling ② ( Plugins、Actions 扩展 | 函数调用 Function Calling 引入 | 函数调用开发流程 | 代码示例 ) https://hanshuliang.blog.csdn.net/article/details/140380268 博客代码...
### S-Function函数编写 #### 什么是S-Function S-Function(系统函数)是一种用于扩展Simulink功能的方法,允许用户使用高级编程语言来创建自定义模块。这些语言包括MATLAB、C、C++、Ada和Fortran。通过使用MEX...
在标题中提到的“ekf-sfunction.zip”,表明这是一个与EKF相关的MATLAB s函数工程。 **MATLAB S函数** MATLAB S函数是MATLAB Simulink环境中的一种特殊函数,用于自定义仿真模型的内部行为。S函数允许用户编写C、...
在ECMAScript中,函数是一种特殊的数据类型,被称为`function`类型。函数在JavaScript中扮演着核心角色,因为它们不仅可以执行特定的任务,还具备对象的特性,如拥有属性和方法。下面我们将深入探讨函数作为对象的...
在上面的例子中,我们定义了一个函数Function,然后定义了一个函数指针p,将Function的入口地址强制转换成int类型的数据,然后将其赋值给函数指针p,最后通过函数指针p来调用函数Function。 四、函数指针的好处 ...