新月传媒做网站 怎么样,wordpress 论坛插件,如何创建一个平台,最近国际新闻热点模块化编程--函数的进阶1.内联函数1.1 inline基本情况1.2 inline 的前世今生-带参的宏替换2.传递引用#xff08;重点#xff09;2.1引用、理由、注意事项2.3 交换两个变量的数值3.返回引用3.1不要返回局部变量的引用3.2函数可以不返回值#xff0c;默认返回传入的引用对象本…
模块化编程--函数的进阶1.内联函数1.1 inline基本情况1.2 inline 的前世今生-带参的宏替换2.传递引用重点2.1引用、理由、注意事项2.3 交换两个变量的数值3.返回引用3.1不要返回局部变量的引用3.2函数可以不返回值默认返回传入的引用对象本身3.3返回引用时要求函数的参数中必须包含被返回的引用对象4.参数的默认值5.随堂练习--使用函数实现游戏中的私聊6.函数重载重点7.函数模版难点《老九学堂C课程》《C primer》学习笔记。《老九学堂C课程》详情请到B站搜索《老九零基础学编程C入门》-------------简单的事情重复做重复的事情用心做用心的事情坚持做(老九君)---------------1.内联函数
1.1 inline基本情况
在大多数的机器上调用函数都要做很多工作调用前要先保存寄存器并在返回时恢复复制实参程序还必须转向一个新位置执行。在想利用函数的好处代码复用修改效率高但又想避免函数调用的时间资源消耗就可以使用内联函数。
内联(inline)函数内部联结是C为了提高程序运行速度所做的一项改进C中没有与常规函数的主要区别在于被调用时的运行机制不同编写方式是一致的在函数头部多一个incline关键字
常规函数运行机制依据函数名找到函数具体实现处执行函数 内敛函数运行机制编译器使用函数代码替换函数调用
内联函数在函数声明处/函数定义时加关键字二者取其一即可。
inline void func1(){cout 1;cout 2;}
int main(){func1();
}在编译将展开函数func1()
int main(){cout 1;cout 2;
}如果 执行函数代码的时间 比 处理函数调用的时间 长则节约的时间只占整个过程的很小一部分。
建议函数较短的时候使用内联可以节省函数调用的时间。如果函数较长将会耗费较多的内存空间存代码呀后续之后可能会用在类中用到
如果函数体太长的话编译器可能会选择忽略inline关键字转而做普通函数调用
1.2 inline 的前世今生-带参的宏替换
C时代如何实现内联呢内敛等价于替换C中常用的替换操作是宏定义
#include iostream
#define N 5 // 以后在所有使用到N的场合都会被替换成5
#define S(num) num*num // 宏定义了一个S函数参数是num, 以后在所有使用到S(num)的地方都会被自动的体化成 num * num 还不需要考虑参数的数据类型整型和浮点型都能传但是直接替换会处问题
using namespace std;int main(){int result1 S(5);float result2 S(5.5);int result3 S(5 10);cout result1 \t result2 \t result3 endl;return 0;
}C 中为了升级了带参数宏定义直接替换的bug但是也失去了内连函数的能传任意类型数据的优势
2.传递引用重点
2.1引用、理由、注意事项
引用为对象起了另一名字实际底层是对对象地址的封装。引用并非对象只是对象的一个别名。
引用更接近于const 指针一旦于某个变量关联起来一般不会变换它效忠的对象而是为这个对象做一些事情。
int value 1024;
int refValue value; // 引用必须初始化使用refValue和value操作的是同一个对象
// 不可以直接引用常量(字面量)
// double d 12.3; ❌
const double d 12.3; // ☑️ 因为之后对d的修改编译器会报错使用引用的理由
可以更加简便的书写代码省掉指针传参时的取地址符号可以直接传递某个对象而不是把对象复制一份
将引用变量作为参数时函数对原始变量进行操作而非原始变量的副本。 但数据所占的内存比较大时建议使用引用参数。
注意传递数组只能用指针并使用const 关键字
2.3 交换两个变量的数值
demo 1: 交换两个变量的值
// 三个版本的交换两个数字
void Swaps1(int, int);
void Swaps2(int *, int *);
void Swaps3(int , int );int main(){int num1 10, num2 5;Swaps1(num1, num2);cout num1 \t num2 endl;Swaps2(num1, num2);cout num1 \t num2 endl;Swaps3(num1, num2); cout num1 \t num2 endl;return 0;}void Swaps1(int num1, int num2){// 书写简单功能不达int tmp num1;num1 num2;num2 tmp;
}
void Swaps2(int *p1, int *p2){// 书写繁杂功能实现// 交换目标地址的值// 取p1目标值给指针变量tmpint tmp *p1;// 将p1目标值改为p2的目标值*p1 *p2;// 将p2的目标值改为tmp中存的值*p2 tmp;
}
void Swaps3(int ref1, int ref2){// 写法和方式1一致但是能起到交换的结果// 结合1和2 的优点int tmp ref1;ref1 ref2;ref2 tmp;
}
使用引用的注意点在不想修改变量的地方需要使用cons限制
demo2: 只想现实一下变量的值而不想改造他们
void show(const int , const int );
int main(){int num1 10, num2 5;show(num1, num2);return 0;
}
void show(const int ref1, const int ref2){// ref1 998; 赋值编译不过cout ref1 \t ref2 endl;}3.返回引用
函数的返回值可以是引用类型
3.1不要返回局部变量的引用
函数调用完局部变量就被销毁了返回其引用是一个无意义的东西
void test();
int Sum();
int Sum(){// rnum是在Sum()函数中定义的局部变量其生存期只在Sum()函数中int num 10;int rnum num;return rnum;// 编译 warning: reference to stack memory associated with local variable num returned [-Wreturn-stack-address]// 内存回收并不是把内存保存数值清零而是没有变量去操作他了那一块空间还是在}
void test(){int x 1;
}输出是对是10
int main(){// result 引用了一个局部变量int result Sum();cout result; // 正常输出return 0;
}多加一个test输出就不是10相当于那一块地址的值被1 占了所以原来的result 引用就是后来的1了。 另一个函数将覆盖先前函数栈上的数据
int main(){// result 引用了一个局部变量int result Sum();test(); // 多加一个测试函数就会变成1cout result;return 0;
}3.2函数可以不返回值默认返回传入的引用对象本身
编译可以过但是无法运行 zsh: illegal hardware instruction /Users/chenyingying/CppProject/Helloworld/main 老师返回的是16最后一个修改的参数
int Sum2(int , int );int main(){int num1 10;int num2 15;int result Sum2(num1, num2); // 编译可以过但是无法运行// zsh: illegal hardware instruction /Users/chenyingying/CppProject/Helloworld/main// 老师返回的是16最后一个修改的参数cout result result endl;return 0;
}
int Sum2(int num1, int num2){num1;num2;// 编译warming : control reaches end of non-void function [-Wreturn-type]
}3.3返回引用时要求函数的参数中必须包含被返回的引用对象
(不能返回带运算的引用return num1 num2 是不对的)
int Sum2(int num1, int num2){num1;num2;// ❌提示非常量引用的初始值必须为左值C/C(461return num1 num2;
}
在返回引用类型时为了避免在设计过程中存在模糊的情况在函数定义时增加const限定规避由模糊引起的犯错。 4.参数的默认值
参数可以有默认值在声明时可以传递默认值
void sample(int 10);
int main(){sample();sample(123);
}
void sample(int num){cout num endl;
}注意点 1.默认值可以在函数原型或者定义中给出不能再两个位置同时出现 实验时在定义中给出会报错main.cpp:16:6: note: candidate function not viable: requires 1 argument, but 0 were provided我还是在声明的时候给默认值吧 2.对于带参数列表的函数必须从右向左添加默认值有默认值得得放在参数列表的后端
void test1(int a, int b 5, int c 10); // ✔️对的调用时 test1(1); test1(1,2);test3(1,2,3);都对
void test2(int a 2, int b 5, int c); // ❎错默认参数后的参数也必须有默认值
void test3(int a 1, int b 5, int c 10); // ✔️对的调用时 test3(1); test3(1,2);test3(1,2,3);都对5.随堂练习–使用函数实现游戏中的私聊 // 模拟现实游戏中的私聊函数
string chatTo(const string toName, const string content);
string chatFrom(const string 系统, const string 快睡觉);
/*** 跟某人聊天--负责字符串的拼接聊天格式* Param tonName 聊天对象的名称* Param content 聊天的内容* return 按某格式拼接聊天信息后的字符串
*/
string chatTo(const string toName, const string content){string msg ❄️ 你悄悄的对[ toName ]说 content;return msg;
}
string chatFrom(const string fromName, const string content){string msg ❄️[ fromName ]悄悄对你说 content;return msg;
}int main(){cout 请输入对方的名称:;string toName,content,chatMsg;getline(cin, toName);cout 请输入发送对方的聊天信息;getline(cin, content);chatMsg chatTo(toName, content); // 后台应该是需要做处理的cout endl chatMsg endl;cout 请输入来自对方的信息; // 应该是从网络上接收对的但是这里只是模拟getline(cin, content);string fromName toName;chatMsg chatFrom(fromName, content); // 不能通过等号的方式进行传参么cout endl chatMsg endl;return 0;
}
6.函数重载重点
overloading: 可以有多个同名函数 但是参数列表不同特征标不同
重载决议编译器在编译时会依据参数列表对函数进行重命名来实现重载函数的唯一性
(返回值不同并产生不同的函数重载)
void Swap(int a, int b) 编译器处理为 Swap_int_int()
void Swap(float a, float b) 编译器处理为 Swap_float_float()deno1: 类型的引用和类型本身所表达的参数列表是一致的不是函数重载
void eating(string food){cout 吃 food 1 endl;
}
void eating(string food){cout 吃 food 2 endl;
}从编译器的角度来看eating(sting) 和eating(string)的特征标参数列表是一致的
还需要注意函数重载时不区分const和非const变量。
demo2: 使用重载实现不同数据类型数组的排序
void sort(int[], int);
void sort(float[], int);
void sort(double[], int);
void Show(int[], int);
void Show(float[], int);
void Show(double[], int);void sort(int nums[], int len){int tmp;// cout sizeof(nums): sizeof(nums) endl;for(int i 0; i len; i){for(int j 0; j len - i -1; j){if(nums[j] nums[j 1]){tmp nums[j];nums[j] nums[j1];nums[j1] tmp;}}}}
void sort(float nums[], int len){float tmp;for(int i 0; i len; i){for(int j 0; j len - i -1; j){if(nums[j] nums[j 1]){tmp nums[j];nums[j] nums[j1];nums[j1] tmp;}}}}void sort(double nums[], int len){double tmp;for(int i 0; i len; i){for(int j 0; j len - i -1; j){if(nums[j] nums[j 1]){tmp nums[j];nums[j] nums[j1];nums[j1] tmp;}}}}void Show(int nums[], int len){for(int i0; i len; i){cout nums[i] \t ;}cout endl;
}
void Show(float nums[], int len){for(int i0; i len; i){cout nums[i] \t ;}cout endl;
}
void Show(double nums[], int len){for(int i0; i len; i){cout nums[i] \t ;}cout endl;
}int main(){int iNums[] {1, 3, 2, 4, 6, 5};float fNums[] {1.2, 3.4, 7.8, 5.5, 6.9};double dNums[] {1.2, 3.4, 7.8, 5.5, 6.9};cout 排序前;Show(iNums, sizeof(iNums)/sizeof(iNums[0]));sort(iNums, sizeof(iNums)/sizeof(int));cout 排序后;Show(iNums, sizeof(iNums)/sizeof(iNums[0]));cout 排序前;Show(fNums, sizeof(fNums)/sizeof(fNums[0]));sort(fNums, sizeof(fNums)/sizeof(float));cout 排序后;Show(fNums, sizeof(fNums)/sizeof(float));cout 排序前;Show(dNums, sizeof(dNums)/sizeof(dNums[0]));sort(dNums, sizeof(dNums)/sizeof(double));cout 排序后;Show(dNums, sizeof(dNums)/sizeof(double));return 0;
}输出
排序前1 3 2 4 6 5
排序后1 2 3 4 5 6
排序前1.2 3.4 7.8 5.5 6.9
排序后1.2 3.4 5.5 6.9 7.8
排序前1.2 3.4 7.8 5.5 6.9
排序后1.2 3.4 5.5 6.9 7.87.函数模版难点
模板函数实际上就是建立一个通用函数在该函数的定义时不指定具体的数据类型使用虚拟类型代替。模板函数被调用时编译器会根据实参反推数据类型类型参数化
// 模板头与函数声明定义需要写在一起
template typename 类型参数1 typename 类型参数2
返回值类型 函数名(形参列表){// 在函数体重可以使用类型参数
}templatetypename T void Swap(T , T );
// T 可以为任意类型, 可以看成类型参数化 typename(新推荐)/class(旧习惯)
templatetypename T // 模版头部
void Swap(T a, T b){T temp a;a b;b temp;
}demo1: 模版函数实现-函数重载part 6 中demo2 templatetypename T void Sort(T tArray[], int len);
templatetypename T void Show(T tArray[], int len);templatetypename T
void Sort(T tArray[], int len){// 冒泡排序法T temp;for(int i 0; i len-1; i){for(int j 0; j len - i ; j){if(tArray[j1] tArray[j]){temp tArray[j];tArray[j] tArray[i];tArray[i] temp;}}}
}templatetypename T
void Show(T tArray[], int len){for(int i 0; i len; i){cout tArray[i] \t;}cout endl;
}
int main(){//模版函数demoint iNums[] {1, 3, 2, 4, 6, 5};float fNums[] {1.2, 3.4, 7.8, 5.5, 6.9};double dNums[] {1.2, 3.4, 7.8, 5.5, 6.9};cout 排序前;Show(iNums, sizeof(iNums)/sizeof(iNums[0]));Sort(iNums, sizeof(iNums)/sizeof(int));cout 排序后;Show(iNums, sizeof(iNums)/sizeof(iNums[0]));cout 排序前;Show(fNums, sizeof(fNums)/sizeof(fNums[0]));Sort(fNums, sizeof(fNums)/sizeof(float));cout 排序后;Show(fNums, sizeof(fNums)/sizeof(float));cout 排序前;Show(dNums, sizeof(dNums)/sizeof(dNums[0]));Sort(dNums, sizeof(dNums)/sizeof(double));cout 排序后;Show(dNums, sizeof(dNums)/sizeof(double));return 0;
}输出
排序前1 3 2 4 6 5
排序后1 2 4 6 3 5
排序前1.2 3.4 7.8 5.5 6.9
排序后5.5 1.2 6.9 3.4 7.8
排序前1.2 3.4 7.8 5.5 6.9
排序后5.5 1.2 6.9 3.4 7.8函数重载的优秀之处相同的函数名可以拥有不同的行为每个人的吃饭的方式都是存在差异的 函数模版的优秀之处重载函数实现的逻辑都一致时可以直接使用函数模版进行精简。中等级别的代码用函数模版的还是比较少在书写框架的时候函数模版就十分有用
两者并非替代关系当重载函数中的实现逻辑是一致时才能用函数模版替代精简代码