代码改变世界

C++Primer 第六章 函数

2016-05-25 22:11  szn好色仙人  阅读(257)  评论(0)    收藏  举报
//1.我们通过调用运算符来执行函数。调用运算符的形式是一对圆括号,他作用于一个表达式,该表达式是一个函数或者指向函数的指针。圆括号之内是用逗号分隔的实参列表,用于初始化函数形参。调用表达式的类型就是函数的返回类型。

//2.实参是形参的初始值,尽管实参与形参存在对应关系,但是没有规定实参的求值顺序。

//3.任意两个形参不能同名,而且函数最外层作用域中的局部变量也不能和函数形参名字一样
void fun0(int value = 10)
{
    //int value = 20;      //error C2082: 形参“value”的重定义
    {    
        int value = 20;    //正确
    }
}

//4.函数的返回类型不能是数组类型或者是函数类型,但是可以是指向数组或者函数的指针或引用。
//  数组的两个特殊性质:
//  不允许拷贝数组,导致无法以传值方式使用数组参数和返回数组类型
//  使用数组时通常将其转化为指针,导致我们为函数传递一个数组的时候,实际上传递的是指向数组首元素的指针。而这个指针将失去数组的所有特性,对于多维数组也是如此
void fun1(int a[10]);
void fun1(int a[20]);
void fun1(int *a);
//  以上三个函数的声明实际上是一致的,数组的第一个维度将被忽略。
void fun2(int (*a)[10]);
void fun2(int (*a)[20]);
//  以上两个函数的声明实际上是不一致的,数组的第二个及以上维度将不被忽略。int (*a)[10]可以看做是一个二维数组(第二个维度大小为10)的首地址。
//  对于多维数组,其除了第一维维度可以忽略外,其余维度是形参类型的一部分
//  在函数参数中,一维数组等价于一维指针,这是因为指针的偏移量可以通过指针类型得出
//  在函数参数中,二维数组不能等价于二维指针,这是因为指针的偏移量无法仅仅通过指针类型得出,还必须指定其第二维的长度才能知道
//  比如: int a[10], int* p在函数参数中是等价的,因为a[1]就等于指针p所指内存偏移4字节
//  而int a[2][2], int** p在函数参数中是不等价的,因为a[1]是数组首地址偏移了8字节,而p[1]无法正确的偏移8字节,所以多维数组除第一维外,其它的维度不可忽略
//  由上可以推出:在函数的参数中a[1][2] 和 a[1][3]是不同的,这是因为对于这两个二维数组来说a[0]的偏移量(相对于数组起始位置)一个是8字节,一个是12字节。

//5.不管是在定义变量还是在定义函数的形参列表的时候,要考虑好定义的基本类型,和其可变性。对于无符号的变量应定义为unsigned的,对于不变的量应使用const修饰。
//  比如在定义函数的时候,使用的形参类型为常量引用,可以极大的扩展其适用性。比如:形参const string &str可以接收常量版本和非常量版本,而string &str却只能接收非常量版本。其中定义为传引用调用是为了提高效率。

//6.一个返回值类型为void的函数可以使用return返回另一个void类型的函数。

//7.一个重要的规则:不要返回局部对象的引用和指针。在函数内部分配的资源最好在函数内部释放。

//8.函数返回一个指向数组的指针:
int (*fun3())[10];    //与fun右边的()是调用运算符其优先级比*高,所以理解方式是:fun3是一个函数,函数返回一个指针类型,指针指向的是一个数组,此数组的维度是10,存储的是int类型的值。
//int *fun3()[10];    //这是错误的声明,理解方式为:fun3是一个函数,函数返回一个数组,数组类型为int*类型且其维度为10。由于函数不能返回数组,所以这是错误的用法。
//使用尾置返回类型可以清晰的声明一个返回指向数组的指针的函数:
auto fun3() ->int (*)[10];
//auto fun3() ->int *[10];     //这是错误的,函数不能返回数组类型。
//使用decltype也可以很方便的声明返回一个指向数组的指针的函数。
int a[10] = {0};
decltype(a) *fun3();          //等价于int (*fun3())[10];    这里的注意点:decltype作用于数组将得到数组类型,所以声明中的*不能省略
//decltype(a) fun3();         //这是错误的,函数返回数组
int (*fun13(int (*parray)[10]))[10];//此函数的参数为数组指针,此函数的返回值也是数组指针
char (*(*x[3])())[5];
//x和[3]结合说明是一个大小为3的数组,该数组的每个元素为一类指针,该类指针指向一类函数,该类函数无参数,返回一类指针,该类指针指向一个大小为5的char型数组

//9.函数的重载:
//  main()函数不能重载
//  不允许两个函数除了返回类型外其他所有的要素都相同。
//  一个拥有顶层const的形参无法和另一个不具有顶层const的形参区分开来。
//  在不同的作用域中无法进行函数重载。
//  在C++语言中,名字查找发生在类型检查前。比如:在内层作用域中定义的变量会隐藏外部作用域中的同名函数名字。

//10.关于重载函数匹配:
//   匹配要求有两个:
//     1.该函数每个实参的匹配都不劣于其他可行函数需要的匹配
//     2.至少有一个实参的匹配优于其他可行函数提供的匹配
//     如果不满足上述两个条件,那么函数匹配将出错
//   注意点:
//     1.由于小整形运算将被提升为大整形,所以假如同时提供了void fun(short); void fun(int); 那么调用函数的时候:char x = 10;fun(x);反而会调用void fun(int)的版本。只有当fun参数为short类型的时候才会调用fun(short);
//     2.由于浮点数运算默认以double类型进行,所以如果同时提供了void fun(float); void fun(int);那么调用函数fun(3.14);会造成二义性错误,这是由于所有算数转换的级别都一样
//     3.由于所有算数转换的级别都一样,所以如果同时提供了:void fun(int); void fun(double);那么:unsigned int z = 10; fun(z);将产生二义性错误
//   匹配等级:
//     1.精确匹配:实参类型和形参类型相同。实参从数组类型或函数类型转换为对应的指针类型。向实参添加或删除顶层const属性。
//     2.含有底层const的实参接受对应的无底层const的形参(非常量可以转为常量,但是反之则不行)
            int value = 10;
            const int *pValue = &value;
//     3.通过类型提升(小整数转为大整数类型,有符号整数转为无符号整数等)实现的匹配
//     4.通过算数类型转换或指针转换实行的匹配(常量整数值0或者字面量nullptr能转换为任意类型的指针,以及任意指向非常量的指针向void*的转换和任意指向常量的指针向const void*的转换,以及在有继承关系的类的指针的转换)
//     5.类类型转换实现的匹配

//11.关于默认形参:在给定的作用域中一个形参只能被赋予一次默认实参,通常应该在函数的声明中使用函数默认实参而不是在函数的定义中使用。
//   一个函数可以声明(但是不可以定义)在另一个函数中。局部变量不能作为默认实参。其原因是:用作默认实参的名字在函数声明的作用域内解析,而这些名字的求值发生在函数调用时,这时候的局部变量是未定义的。

void Fun()
{

  void Fun1();
  Fun1();

}


void Fun1()
{
  printf("Fun1\n");//运行至此
}


int _tmain(int argc, _TCHAR* argv[])
{
  Fun();
  return 0;
}

//12.内联机制用于优化规模小,流程直接,频繁调用的函数。

//13.使用assert预处理宏。assert(expt);若expr为假,则输出信息并终止程序。需包含头文件cassert。使用预处理命令#define NDEBUG (置于cassert之前)可以关闭assert判断。assert语句只在Debug下有效,Release下不会被执行

//14.当我们把函数名当做一个值使用的时候,其会自动转为指针。对函数名解引用即是对函数指针解引用,得到的还是函数类型。
void Fun0(){};
auto temValue = *Fun0;        //tenValue是一个函数指针,其等价于: void(*tenValue)();
void (*Fun())();              //Fun是一个函数而不是函数指针,其返回类型是一个函数指针,指向一个返回型为void类型,不接受任何实参的一个函数。
void (*Fun1)() = Fun0;        //Fun1是一个函数指针而不是一个函数

//15.函数返回一个指向函数类型的指针:
void fun4();
decltype(&fun4) fun5();        //void (*fun5())()
decltype(fun4)* fun5();        //void (*fun5())()