[C/C++基础--笔试突击] 6.函数
概述:
函数是有名字的计算单元,对程序的结构话至关重要。
C++中,函数原型就是函数的声明,要加上分号。
这一部分还是比较轻松的~~
6.1 参数传递
形参:出现在函数定义中,在整个函数体内都可以使用,函数体结束后被收回。
实参:主函数中调用,进入被调函数后,实参不能使用。
形参和实参的功能是数据传送,发生函数调用时,主调函数把实参的值传给被调函数的形参,从而实现主调函数向被调函数的数据传送。
函数调用时,C里面的两种传递:
1)值传递
2)指针传递(严格来说也属于值传递,只不过传递的是地址)
函数调用时,C++里面的三种传递:
1)值传递
2)指针传递
3)引用传递
给函数传递实参遵循变量初始化的规则。非引用类型的形参以相应实参的副本(值)初始化。对(非引用)形参的任何修改仅作用域局部副本,并不影响实参本身。为了避免传递副本的开销,可将形参指定为引用类型。对引用形参的任何修改都会直接影响实参本身。应将不需要修改响应实参的引用形参定义为const引用。
传递指针的引用
假设我们想编写一段代码实现两个指针的交换。已知需要*定义指针,用&定义引用。现在问题在于如何将这两个操作符结合起来以获得指向指针的引用,下面给出一个例子:
void pswap(int *&v1, int *&v2) { // int *&v1 从右至左理解:v1是一个引用,与之相int型对象的指针相关联 //也就是说,v1是传递进pswap函数的任意指针的别名 int *tmp = v2; v2 = v1; v1 = tmp; }
再写一个笔试题,下面程序打印的结果是什么:
void swap_int(int a, int b) { int temp = a; a = b; b = temp; } void swap_str(char *a, char *b) { char* temp = a; a = b; b = temp; } int main(void) { int a = 10; int b = 5; char* str_a = "hello world"; char* str_b = "world hello"; swap_int(a, b); swap_str(str_a, str_b); printf("%d %d %s %s\n", a, b, str_a, str_b); return 0; }
解答:10 5 hello world world hello。
首先a, b是值传递,不会修改实参,a、b的值不变;
在调用swap_str前,str_a和str_b的指向情况如下:
然后传参,使得a与str_a,b与str_b指向相同:
然后函数体内交换的结果:
可见,swap_str函数仅仅交换了a和b两个指针的指向,但实参的指向并没有改变,如果题目中改成指针的就可以交换实参的内容了。
6.2 内联函数
内联函数一般在代码中用inline修饰,内联函数有两种:
1)成员函数成为内联函数
2)普通函数成为内联函数
通常编译时,调用内联函数的地方,将不进行函数调用,而是使用函数体替换调用处的函数名,形式类似宏替换,这种替换成为内联扩展。
如我们将下面的shortString定义为内联函数:
inline const string &shortString(const string &s1, const string &s2) { return s1.size() < s2.size() ? s1 : s2 ; }
则在调用的时候,如果是:
cout<<shortString(s1, s2)<<endl;
则在编译时可能将展开为:
cout<< ( s1.size() < s2.size() ? s1 : s2 ) <<endl;
从而消除了把shortString写成函数的额外执行开销。
注:一般来说,内联机制适用于优化下的、只有几行的而且经常被调用的函数。大多数的编译器都不支持递归函数的内联。
宏定义与内联函数的区别:
1)宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码;
2)宏定义没有类型检查,内联函数有类型检查。
6.3 默认参数
在函数声明或定义时,直接对参数赋值,这就是默认参数。在函数调用时没有指定与形参相对应的实参时候,就自动调用默认参数。
int mal(int a, int b = 3, int c = 6, int d = 8); mal(1); // 相当于 mal(1, 3, 6, 8);
注意以下几点:
1)默认参数只可在函数声明中设定一次。只有在无函数声明时,才可以在函数定义中设定。
2)默认参数定义的顺序为自右到左。即如果一个参数设定了默认值,其右边的参数都要有默认值。
3)默认参数调用时,遵循参数调用顺序,自左到右逐个调用。
4)默认值可以使全局变量、全局常量,甚至是一个函数,但不可以是局部变量。因为默认参数的调用是在编译时确定的,而局部变量位置与默认值在编译时无法确定。
可变参数
用三个点(...)做参数占位符,下面一个例子用可变参数的函数实现多个数的相加:
int add(int num, ...) { // num为加数的个数 int sum = 0; int index = 0; int* p = (int*)&num + 1; for(; index < num ; ++index) sum += *p++; return sum; } int main(void) { int i = 1, j = 2, k = 3; cout<<add(3, i, j, k); return 0; }
6.4 函数重载
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。
进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同。不能够仅仅基于不同的返回类型而实现重载。
下面的笔试题:
在C++语言中,若类C中定义了一个方法 int f(int a, int b),那么方法(A)不能与该方法同时存在与类C中。
A. int f(int x, int y) B. int f(float a, int b) C. float f(int x, float y) D. int f(int x, float y)
6.5 函数模板与泛型
泛型编程:独立于任何特定类型的方式编写代码。
模板:创建类或函数的蓝图或公式。
6.5.1 函数模板
模板定义以关键字template开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,不能为空。
template<typename T> T fun(const T &a, const T &b) { .... }
模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。模板形参选择的名字没有本质含义。可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类型形参。如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。模板非类型形参是模板定义内部的常量值。
6.5.2 类模板
就像可以定义函数模板一样,也可以定义类模板。
template<class Type> class Queue { public: Queue() ; // 默认构造函数 Type &front(); const Type &front () const; void push(const Type &); void pop(); bool empty() const; private: //.. };
使用类模板时,必须为模板形参显示指定实参:
Queue<int> qi;
这一部分还是比较简单的,多用用就都能够理解了,欢迎一起讨论~~ 0.0
返回目录 -> [C/C++基础--笔试突击] 概述