logo资料库

《C++ Primer Plus第6版中文版》学习笔记(第八章).doc

第1页 / 共66页
第2页 / 共66页
第3页 / 共66页
第4页 / 共66页
第5页 / 共66页
第6页 / 共66页
第7页 / 共66页
第8页 / 共66页
资料共66页,剩余部分请下载后查看
(一〇二)内联函数 内联函数和普通函数有所区别。 普通函数需要①函数定义②函数原型③函数调用,具体调用形式是,先调用时, 储存当前函数的内存地址(假设为地址 A),然后调用函数,跳到被调用函数的 内存地址(地址 B),然后在地址 B 处依次执行,等执行完被调用的函数(或许 还有返回值),再返回之前储存的地址 A 处。 内联函数,可以省略②函数原型(也可以不省略),但需要在函数定义和函数原 型的函数头之前加上关键字 inline(如果省略函数原型的话,则一般将函数定义 放在函数原型的地方), 他的机制,是把函数代码,放在函数调用时的内存地址处,这样的话,无需像普 通函数那样,从地址 A 处跳到地址 B 处,调用完再跳回来,而是地址 A 处之后 的内存位置,就是原本是地址 B 处函数的代码内容。就像直接将函数命令放在调 用处一样。 例如: int main() { int a=x(1); return 0; } int x(int m) { return m+3; } 在普通函数时,int a 的内存地址是地址 A,而 x 函数被储存在地址 B 处(注意, B 和 A 并不一定相邻,或者说,几乎不会相邻)。于是程序访问内存时的顺序: 地址 A——地址 B——地址 A 这样。 而在内联函数时,在 int x(int m)之前加上 inline,在 main 函数中,有点类似于: int a={ 1 + 3 }; 这样的形式。 由于内联函数是直接放在 main 函数中(而不是调用的时候才占用内存),因此, 使用内联函数更耗费内存。但相对而言,使用内联函数的优点则是速度更快(因 为不用在内存之中跳来跳去)。 但,由于在内存地址中跳来跳去速度其实很快,所以节省的时间的绝对值,并不 大,除非该函数经常被调用(每次节约 0.01 秒,100 次就是 1 秒了)。
内联函数的优点:速度更快——适合执行代码时间很短的函数; 内联函数的缺点:占用内存更多——不适合较长的函数。 编译器不允许使用内联函数的两种情况: ①递归函数——内联函数不允许自己调用自己; ②函数过大。 内联函数的使用方式: ①在函数原型和函数定义的函数头之前加上关键字 inline 即可; ②可省略函数原型,若省略,则将函数定义放置于函数原型处较好; ③其他同普通函数。 如代码: #include inline int chengfang(int a) //函数定义前置于函数原型处,并可省略函数原型 { } return a*a; int main() { } using namespace std; int b = chengfang(2); //像普通函数那样调用内联函数 int c = chengfang(5); cout << "b = " << b << ". c = " << c << endl; cin.get(); return 0; 输出: b = 4. c = 25 与内联函数对应的宏: 在 C 语言中,使用预处理器语句#define 来提供宏——内联代码的原始实现。 例如宏:#define SQUAR(X) X*X; 宏的特点是文本替换,而不像内联函数那样是函数调用——也就意味着,没有括 号的情况下,结果和内联函数的结果不同。
如 define 的代码: #include #define square(x) x*x; int main() { using namespace std; int a[5]; a[0] = square(1); //实质是 1*1=1 a[1] = square(2); //实质是 2*2=4 a[2] = square(2 * 2); //实质是 2*2 * 2*2=16 a[3] = square(2 + 2); //实质是 2 + 2*2 + 2=8 a[4] = square(2 + 0); //实质是 2 + 0*2 + 0=2 for (int i = 0;i < 5;i++) cout << "a[" << i << "] = " << a[i] << endl; system("pause"); return 0; } 输出: a[0] = 1 a[1] = 4 a[2] = 16 a[3] = 8 a[4] = 2 请按任意键继续. . . 使用内联函数的代码: #include inline int square(int x) { return x*x; } int main() { using namespace std; int a[5]; a[0] = square(1); //实质是 1*1 a[1] = square(2); //实质是 2*2=4 a[2] = square(2 * 2); //实质是(2*2) * (2*2)=16 a[3] = square(2 + 2); //实质是(2+2) * (2+2)=4*4=16 a[4] = square(2 + 0); //实质是(2+0) * (2+0)=2*2=4 for (int i = 0;i < 5;i++) cout << "a[" << i << "] = " << a[i] << endl;
system("pause"); return 0; } 输出: a[0] = 1 a[1] = 4 a[2] = 16 a[3] = 16 a[4] = 4 请按任意键继续. . . 由上述两个例子可以看出,#define 宏的效果,和 inline 内联函数的机制完全不同。 #define 是文本替换,将括号里的内容直接赋值到表达式之中——而不是括号里 表达式的结果。 inline 是像函数调用那样,将表达式的值得出值的结果后,再放入函数内部的表 达式之中。 特别是比如说:#define MM(m) m*m*m; 这样的宏, 当遇见调用 int a=1; int b=MM(a++);时, 在代码中,b=1*2*3=6, 而不是内联函数的 b=1*1*1(因为 a++是先执行完这行代码之后,a=a+1); (一〇三)引用变量 格式: int a = 1; int &b = a; //b 作为 a 的别名,是一个 a 变量的引用,需要在声明时进行初始化 效果: ①b 是 a 的引用变量,b 的值和地址,和 a 是相同的,且不会改变; ②在声明引用变量 b 时,需要同时对变量 b 进行初始化,即不能先声明,然后再 初始化;原因是引用变量 b 需要根据他被引用的变量,而拥有地址,而不是有自 己独立的内存地址; ③引用变量的地址,由初始化时的变量决定,而不是由最近一次赋值而决定。例
如 b 是 a 变量的引用,然后又有变量 c 将值赋给 b,那么 b 的地址依然是 a,而 不是 c; ④引用变量其在声明的时候,变量名前的“&”是类型标识符的一部分,而不是 地址运算符。 在之后在引用变量名前加上“&”,才是地址运算符; ⑤引用变量的类型,需要和被引用的变量的类型相同。即 a 是 int 类型,那么引 用变量 b 也应该是 int 类型; ⑥引用变量和指针不同。 指针是指向某个内存地址,而引用变量是变量的别名。 指针有其储存自己的内存地址(即指针指向的地址,是储存指针的内存地址的 值),而引用变量的地址,就是被引用的变量的内存地址。 ⑦可以将引用变量理解为变量的另外一个名字,例如上面代码中的 a 和 b,在很 多地方的使用方式是相同的。只不过引用变量还有自己独特的使用范围。 如代码: #include int main() { using namespace std; int a = 1; int &b = a; //b 作为 a 的别名,是一个 a 变量的引用,需要在声明时进行初始化 cout << a << "," << b << endl; //输出 a、b 的值 cout << &a << "," << &b << endl; //输出 a、b 的地址 cout << endl; int c = 2; b = c; //将 c 的值赋给 b,注意,这不是让 b 成为 c 变量的别名。 cout << c << "," << &c << endl; cout << a << "," << b << endl; cout << &a << "," << &b << endl; system("pause"); return 0; } 输出: 1,1 003EFEC0,003EFEC0 2,003EFEA8 2,2
003EFEC0,003EFEC0 请按任意键继续. . . 总结: ①从上面的代码中可以看出,b 和 a 的地址、值是相同的; ②在中间 b=c,然而 b 并没有和 c 的地址保持相同,而是和 a 的地址依然保持相 同,但 b 的值、以及 a 的值,和 c 的值相同,相当于将 c 的值,赋给了 a 和 b; ③当引用变量或者被引用的变量的值改变时,另外一个值也一样被改变(因为实 质上是一个值有两个变量名); ④可以一个变量有多个引用变量。如在之前代码的基础上,加上 int &d=a 或 int &d=b; 然后 d 的地址和值,也将和 a、b 保持相同; ⑤必须声明引用变量的时候进行初始化。 引用变量作为函数参数时: 引用变量作为函数参数时,可以像指针那样直接修改变量的值,但是具体方式不 同。 使用指针修改实参时,传递的实参应为变量的地址,在函数内部,应该如同使用 指针那样,通过地址进行操控值; 例如代码: //使用指针修改变量的值 #include int chenger(int*); //传递的参数应为指针 int main() { } using namespace std; int a = 1; int b = chenger(&a); //调用函数时,传递的是变量的地址 cout << a << ", " << b << endl; system("pause"); return 0; int chenger(int*m) {
*m *= 2; //用指针的方式,修改指针指向的值 return *m; //返回应为指针加上接触运算符* } 使用引用修改实参时,传递的值是变量自己,参数是引用变量的标识符“&”, 在函数内部,像使用变量那样进行修改。 如代码: //使用引用修改变量的值 #include int chenger(int&); //传递的参数为变量的别名 int main() { } using namespace std; int a = 1; int b = chenger(a); //调用函数时,传递的是变量本身,而非加上地址运算符的&a cout << a << ", " << b << endl; system("pause"); return 0; int chenger(int&m) { } m *= 2; //用修改变量的方式修改变量 return m; //返回值为变量名或者相关的表达式 二者的效果是相同的。输出的内容都是“2,2” 引用变量和指针作为函数的参数时的差别: ①参数的形式不同:引用变量是运算符&,指针是*; ②传递的内容不同:引用变量传递的是值,指针传递的是地址; ③使用方式不同:引用变量像使用变量一样使用,指针需要按指针的方式进行使 用; ④方便程度不同:引用变量在某些时候,由于不用加解除运算符“*”,在操纵 值的时候更简单;
⑤都能直接对参数进行修改,而直接按值传递(不使用引用变量或者指针时), 修改的值是实参的副本(形参)。 引用变量和常量作为参数时: ①函数原型和函数头中的参数不同; ②在调用函数时,引用变量的参数通常不能是表达式(而是变量名)(使用 const 作为参数的限定除外),而常量作为参数时,可以是表达式; 如 int A(int&); 中, 参数可以这么写:A(a)——a 为变量名, 但不能这么这么写:A(a+1) 或 A(a+b)——b 也为变量名 ③在使用引用变量作为参数时,假如需要不能修改引用变量的值,那么应该使用 常量引用,即在函数头之前加 const 进行限定(如果被修改了,编译器会提示出 错); ④对于基本类型的传递,假如需要限定不能被修改,那么不如使用按值传递的方 式(即让函数在调用时,创建变量的副本); ⑤对于数据比较大(如结构和类)时,引用参数将会很有用。 临时变量: 之前提到,使用引用变量的函数,通常在调用的时候,参数不能是表达式。 但是在使用 const 进行限定时,则可以在调用的时候使用表达式,如: int chenger(const int&); int b = chenger(a+1); 在这两行代码中,第一行是函数原型,使用引用变量,但同时使用了关键字 const。 于是,在调用时,就可以使用表达式 a+1。 但同样,因为关键字 const 的限定,因此变量 a 在函数 chenger 中,并没有像普 通引用变量那样被修改。 如果实参与引用参数不匹配,C++将生成临时变量。 当前,仅当参数为 const 引用时,C++才允许这么做(如上)。 在引用参数是 const 的情况下,编译器将在两种情况下生成临时变量(早期的 C++
分享到:
收藏