昨晚写完笔记,发现已过23:30,今日补之。今天可能还有一份笔记。
2011-10-07(Functions: C++'s Programming Modules)
1、函数的返回值可以是声明的返回值类型或者可转换成该类型的类型。返回值类型不能是数组,其他的的都可以,包括指针、含有数组成员的结构体、对象。
2、函数原型(prototypes)是用于提醒编译器接下来的程序可能要用到什么样的自定义函数,减少出错。一般形式如下:
returnType funcName(parameterList);
C++中必须提供函数原型。有两类函数声明原型的时候要注意:无参数和不指定参数,分别这样声明:
void funcName(); //括号里无参数代表void
void funcName(...); //不指定参数
3、如果传给函数的是数组,且只读,应该用const修饰,这样在这个函数中,数组就会被看成常量而不能修改,如:
void display(const int [], int); //prototype
void display(const int a[], int size)
{
a[2] = 5; //invalid
}
4、const和指针。I、一级指针情况。const修饰的对象有两种:修饰变量和修饰指针。前者,很明显,变量本身不能改;后者,修饰的方式有两种,所获效果亦有两种。
①可以将非const变量赋给const指针,有两种形式:
int a = 10;
const int* p1 = &a; //Form I
int* const p2 = &a; //Form II
Form
I,对于p1来说,a跟自己一样是const的,不能通过p1来修改a的值,可以改变其指向:*p1 = 20; //invalid
a = 20; //valid
int newA = 100;
p1 = &newA //valid
Form
II,可以通过*p2来修改a的值,但不能改变p2的指向:*p1 = 20; //valid
int newA = 100;
p1 = &newA //invalid
当然还可以同时使用两种形式,效果叠加:const int* const p;
②不能将const变量赋给非const指针:
const int a = 10;
int* p = &a; //invalid
如果上述代码可写,意味着可以通过*p来改变a的值,那将a修饰为const将无意义。
II、二级指针情况。一个非const指针不能赋给一个const二级指针,这与变量和一级指针关系不同:
int* p;
const int** p = &p //invalid
为什么呢?假设上述代码编译可通过,如果我们接下来这样处理:
const int a = 5; //a constant variable
*pp = &a; //valid, both *pp and &a is constant. Meanwhile, p = &a
*p = 10; //valid, p is not const, but you change a's value,which is forbidden
最终结果是将const变量a的值改变。总结I、II,如果数据本身不是指针,则可以将const数据或非const数据(的地址)赋给const指针,对于非指针,只能将非const数据赋给它。
5、函数与结构体。传递与返回结构体,在C++中又三种方法:按值传递、传递地址和按引用传递。这里按引用传递暂不表。按值传递,就是把结构体看成一个基本数据类型,不同的是它可以用“.”来引用结构体成员。按值传递有个缺点,当结构体很大的时候,将其复制并赋给函数将费去很多时间,所以很多时候都是按地址传递的。比如现在声明一个loc结构体,里面包含成员x,y。再编写函数sum以两个loc为形参,而后返回loc,成员值是形参x,y各自的和。两种实现形式:
#include <iostream>
using namespace std;
struct loc
{
double x;
double y;
};
loc sum(loc, loc);
int main()
{
loc l1 = {3.5, 7.5};
loc l2 = {2, 3};
loc l3 = sum(l1, l2);
cout << "l3.x = " << l3.x << endl;
cout << "l3.y = " << l3.y << endl;
return 0;
}
loc sum(loc l1, loc l2)
{
loc l3 = {l1.x + l2.x, l1.y + l2.y};
return l3;
}
按地址:#include <iostream>
using namespace std;
struct loc
{
double x;
double y;
};
loc* sum(loc*, loc*);
int main()
{
loc l1 = {3.5, 7.5};
loc l2 = {2, 3};
loc* l3 = new loc;
l3 = sum(&l1, &l2);
cout << "l3.x = " << l3->x << endl;
cout << "l3.y = " << l3->y << endl;
delete l3;
return 0;
}
loc* sum(loc* l1, loc* l2)
{
loc* l3 = new loc;
l3->x = l1->x + l2->x,
l3->y = l1->y + l2->y;
return l3;
}
6、函数指针。函数也有地址,可以将函数A作为另一个函数B的形参,而后使用它。当然这样和在B中直接使用A有什么区别,我现在说不上。但教材提到:如果未提到函数指针,则对C或者C++的函数讨论将是不完整的。显示是对这个方法的一种肯定,有其实用价值。回到正题,在以函数指针为参数,传给另一个函数,需要了解三件事:I、获取函数的地址。很简单,函数名就是其地址。比如函数Func(),Func就是它的地址。
II、声明函数指针。了解这点就知道了怎么书写函数的参数列表。区别一个函数,需要知道它的返回值类型和参数列表,函数指针类型亦是这样区别的。参考函数原型写法:
returnType Func(parameterList);
如果将函数名Func换为*pf,即returnType (*pf) (parameterList),因为*pf是函数,则pf是指向函数的指针。
III、使用函数指针来调用函数。比如某函数接收了一个名为pf的函数指针,在这个函数内使用它,可以采用两种形式(假设pf的返回值和唯一的参数是int):
int a = pf(5); //Form I
int b = (*pf) (6); //Form II
这是两种矛盾的形式。不过在C++均可。
理论过后,举个例子:
#include <iostream>
using namespace std;
void f1(int); //tow basic functions
void f2(int);
//main function to use tow basic functions
void mainf(int, void(*pf) (int));
int main()
{
mainf(10, f1);
mainf(50, f2);
return 0;
}
void f1(int a)
{
cout << "You use f1 as parameter, ";
cout << "and f1's parameter value: " << a << endl;
}
void f2(int a)
{
cout << "You use f2 as parameter, ";
cout << "and f2's parameter value: " << a << endl;
}
void mainf(int x, void(*pf) (int))
{
(*pf) (x); //or pf(x)
}
输出: You use f1 as parameter, and f1's parameter value: 10
You use f2 as parameter, and f2's parameter value: 50
函数mainf的原型表明它可以接受任何以void为返回值(即:无返回值),int为参数列表的函数的地址,这可能比直接调用函数通用。