传值:
传变量值: 将实参内存中的内容拷贝一份给形参, 二者是不一样的两块内存编程
传地址值: 将实参所对应的内存空间的地址值给形参, 形参是一个指针, 指向实参所对应的内存空间数组
传引用:
形参是对实参的引用, 形参和实参是同一块内存空间安全
将实参对象的值传递给形参对象, 形参是实参的备份, 当在函数中改变形参的值时, 改变的是这个备份中的值, 不影响原来的值bash
像这样:函数
void fakeSwapAB(int x , int y) {
int temp = x;
x = y;
y = temp;
}
int a = 5;
int b = 8;
cout << "交换前: " << a << ", " << b << endl;
// 传变量值
fakeSwapAB(a, b);
cout << "交换后: " << a << ", " << b << endl;
复制代码
形参是对象指针, 实参是对象的地址值, 虽然参数传递方式仍然是传值方式, 由于形参和实参的地址值同样, 因此它们都指向同一块内存, 咱们经过指针更改所指向的内存中的内容, 因此当在函数中经过形参改变内存中的值时, 改变的就是原来实参的值学习
像这样:ui
void realSwapAB(int * p, int * q) {
int temp = *p;
*p = *q;
*q = temp;
}
int a = 5;
int b = 8;
cout << "交换前: " << a << ", " << b << endl;
// 传地址值
realSwapAB(&a, &b);
cout << "交换后: " << a << ", " << b << endl;
复制代码
对于数组, 因数组名就是表明的数组首地址, 因此数组也能用传数组地址值的方式spa
void swapArrFirstAndSecond(int a[]) {
int temp = a[0];
a[0] = a[1];
a[1] = temp;
}
int main(int argc, const char * argv[]) {
int a[] = {2, 3};
cout << "交换前: " << a[0] << ", " << a[1] << endl;
swapArrFirstAndSecond(a);
cout << "交换后: " << a[0] << ", " << a[1] << endl;
return 0;
}
复制代码
在函数调用时, 实参对象名传给形参对象名, 形参对象名就成为实参对象名的别名. 实参对象和形参对象表明同一个对象, 因此改变形参对象的值就是改变实参对象的值设计
像这样:指针
void citeSwapAB(int & x, int & y) {
int temp = x;
x = y;
y = temp;
}
int a = 5;
int b = 8;
cout << "交换前: " << a << ", " << b << endl;
// 传引用
citeSwapAB(a, b);
cout << "交换后: " << a << ", " << b << endl;
复制代码
优势: 引用对象不是一个独立的对象, 不单独占内存单元, 而对象指针要另外开辟内存单元(内存中放实参传过来的地址), 因此传引用比传指针更好用.
不要求程序在调用时必须设定该参数, 而由编译器在须要时给该参数赋默认值.
规则1. 当程序须要传递特定值时须要显式的指明. 默认参数必须在函数原型中说明.
若是函数在main函数后面定义, 而在声明中设置默认参数, 在定义中不须要设置默认参数
像这样:
// 在main函数前声明函数, 并设置默认参数
void PrintValue(int a, int b = 0, int c = 0);
int main(int argc, const char * argv[]) {
// 调用函数
PrintValue(5);
return 0;
}
// 在main函数后定义函数, 不须要设置默认参数
void PrintValue(int a, int b, int c) {
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
复制代码
若是函数在main函数前面定义, 则在定义中设置默认参数
像这样:
// 在main前定义函数, 须要设置默认参数
void PrintValue(int a, int b = 0, int c = 0) {
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int main(int argc, const char * argv[]) {
// 调用函数
PrintValue(5);
return 0;
} 复制代码
规则2: 默认参数能够多于一个, 但必须放在参数序列的后部.
像这样:
能够有一个默认参数:
void PrintValue(int a, int b, int c = 0);
复制代码
能够是有多个默认参数:
void PrintValue(int a, int b = 0, int c = 0);
复制代码
不能够在中间设置默认参数:
void PrintValue(int a, int b = 0, int c);
复制代码
规则3. 若是一个默认参数须要指定一个特定值时, 则在此以前的全部参数都必须赋值
// 调用函数 第一种: 三个参数所有有特定值
PrintValue(5, 8, 9);
// 调用函数 第二种: 咱们给第二个参数设特定值, 它前面全部参数必须赋值, 因此能够
PrintValue(5, 8);
/*
调用函数 第三种: 当一个默认参数有特定值时, 它前面全部的参数都必须赋值,
咱们给第三个默认参数设特定值 也就是说第一, 二个参数也必须赋值 因此不能够
*/
// PrintValue(5, , 9);
复制代码
用const修饰要传递的参数, 该函数只能使用参数, 而无权修改参数, 以提升系统的自身安全.
像这样:
// 拼接字符串的函数
void catStr(const string str) {
string str2 = str + " Ray!";
// 函数内部不能修改const修饰的形参, 因此不能这么使用
// str = "Hi";
cout << str2 << endl;
}
int main(int argc, const char * argv[]) {
// 实例化一个字符串
string str = "Hello";
// 调用函数
catStr(str);
return 0;
}
复制代码
C++函数返回值类型能够是除数组和函数之外的任何类型
当返回值是指针或引用对象时, 须要注意函数返回值所指的对象必须存在, 所以不能将函数内部的局部对象做为函数返回值, 由于函数内, 局部变量或者对象在函数运行完毕后内存就释放啦
函数能够返回一个引用, 目的是为了让该函数位于赋值运算符的左边
格式: 数据类型 & 函数名(参数列表);
像这样:
// 全局数组
int arr[] = {2, 4, 6, 8};
// 得到数组下标元素
int & getValueAtIndex(int i) {
return arr[i];
}
int main(int argc, const char * argv[]) {
cout << "更改前: " << arr[2] << endl;
// 调用函数, 而且用于计算或者从新赋值
getValueAtIndex(2) = 10;
cout << "更改后: " << arr[2] << endl;
return 0;
}
复制代码
返回值是存储某种数据类型数据的内存地址, 这种函数称为指针函数
格式: 数据类型 * 函数名(参数列表);
像这样:
// 返回指针的函数
int * getData(int n) {
// 根据形参, 申请内存空间
int * p = new int[n];
// 给申请下来的内存空间赋值
for (int i = 0; i < n; i++) {
p[i] = i + 10;
}
// 返回这段内存空间的首地址
return p;
}
int main(int argc, const char * argv[]) {
// 调用函数, 并接收返回值, 不要忘记释放函数中分配的内存
int * p = getData(5);
// 打印指针所指向的内存中的内容
for (int i = 0; i < 5; i++) {
cout << p[i] << endl;
}
return 0;
}
复制代码
格式: 数据类型 函数名(参数列表);
像这样:
// 返回对象的函数
string sayHello(string s) {
// 咱们拼接好一个字符串, 给str
string str = "Hello " + s;
// 并把str这个对象返回
return str;
}
int main(int argc, const char * argv[]) {
// 调用函数, 接收函数返回的对象
string str = sayHello("Ray");
cout << str << endl;
return 0;
}
复制代码
若是函数返回值做为另外一个函数的参数, 那么这个返回值必须与另外一个函数的参数类型一致
像这样:
// 求最大值的函数
int getMax(int x, int y) {
return x > y ? x : y;
}
int main(int argc, const char * argv[]) {
// 先求8, 9返回最大值; 返回值再跟5比较, 返回最大值
int maxValue = getMax(5, getMax(8, 9));
cout << maxValue << endl;
return 0;
}
复制代码
使用关键字inline声明的函数称为内联函数, 内联函数必须在程序中第一次调用此函数的语句出现以前定义, 这样编译器才知道内联函数的函数休, 而后进行替换
像这样:
// 判断输入的字符是否为数字
inline bool isNumber(char c) {
if (c >= '0' && c <= '9') {
return true;
} else {
return false;
}
}
int main(int argc, const char * argv[]) {
// 声明字符c
char c;
// 从键盘输入字符
cin >> c;
// 进行判断, 这里的isNumber(c), 在程序编程期间就会被isNumber()函数体所替换, 跟宏同样同样的
// 若是函数体特别大, 替换的地方特别多, 就增长了代码量
if (isNumber(c)) {
cout << "输入了一个数字" << endl;
} else {
cout << "输入的不是一个数字" << endl;
}
return 0;
}
复制代码
在C++中, 除具备循环语句, switch语句的函数不能说明为内联函数外, 其它函数均可以说明为内联函数.
使用内联函数能够提升程序执行速度, 但若是函数体语句多, 则会增长程序代码量.
一个函数名具备多种功能, 具备多种形态, 称这种我为多态性, 一个名字, 多个函数
函数重载要知足的条件:
参数类型不一样或者参数个数不一样
像这样:
// 求和的函数 2两个整型参数
int sumWithValue(int x, int y) {
return x + y;
}
// 求和的函数 3两个整型参数
int sumWithValue(int x, int y, int z) {
return x + y + z;
}
// 求和的函数 2个浮点型参数
double sumWithValue(double x, double y) {
return x + y;
}
// 求和的函数 3个浮点型参数
double sumWithValue(double x, double y, double z) {
return x + y + z;
}
int main(int argc, const char * argv[]) {
// 两个整型变量求和
int sumValue1 = sumWithValue(8, 9);
// 三个整型变量求和
int sumValue2 = sumWithValue(8, 9, 10);
// 两个浮点型变量求和
double sumValue3 = sumWithValue(1.2, 2.3);
// 三个浮点型变量求和
double sumValue4 = sumWithValue(1.2, 2.3, 3.4);
cout << sumValue1 << endl;
cout << sumValue2 << endl;
cout << sumValue3 << endl;
cout << sumValue4 << endl;
return 0;
}
复制代码
当函数重载与默认参数相结合时, 可以有效减小函数个数及形态, 缩减代码规模.
这样咱们每种数据类型只保留一个函数便可完成咱们的功能, 直接少了两个函数.
像这样:
// 整型参数求和
int sumWithValue(int x = 0, int y = 0, int z = 0) {
return x + y + z;
}
// 浮点型参数求和
double sumWithValue(double x = 0, double y = 0, double z = 0) {
return x + y + z;
}
int main(int argc, const char * argv[]) {
// 两个整型变量求和
int sumValue1 = sumWithValue(8, 9);
// 三个整型变量求和
int sumValue2 = sumWithValue(8, 9, 10);
// 两个浮点型变量求和
double sumValue3 = sumWithValue(1.2, 2.3);
// 三个浮点型变量求和
double sumValue4 = sumWithValue(1.2, 2.3, 3.4);
cout << sumValue1 << endl;
cout << sumValue2 << endl;
cout << sumValue3 << endl;
cout << sumValue4 << endl;
return 0;
}复制代码
若是使用默认参数, 就不能对参数个数少于默认个数的函数形态进行重载, 只能对于多于默认参数个数的函数形态进行重载.
像这样
// 求和的参数, 而且使用默认参数, 最多三个整型参数求和
int sumWithValue(int x = 0, int y = 0, int z = 0) {
return x + y + z;
}
// 像这样是不行的, 不能对参数个数少于默认个数的函数形态进行重载
//int sumWithValue(int x, int y) {
// return x + y;
//}
// 像这样是能够的, 当调用时传入4个整型参数时就会调用该参数
int sumWithValue(int x, int y, int z, int t) {
return x + y + z + t;
}
int main(int argc, const char * argv[]) {
// 求和, 只给两个特定值
int sumValue1 = sumWithValue(8, 9);
// 求和, 给三个特定值
int sumValue2 = sumWithValue(8, 9, 10);
// 求和, 有4个整型参数
int sumValue3 = sumWithValue(8, 9, 10, 11);
cout << sumValue1 << endl;
cout << sumValue2 << endl;
cout << sumValue3 << endl;
return 0;
}
复制代码
从而上面能够看出, 它们是逻辑功能彻底同样的函数, 所提供的函数体也同样, 区别仅仅是数据类型不一样, 为了统一的处理它们, 引入了函数模板.
如今咱们的函数从4个缩减成一个, 可是咱们的功能没有减小, 反而增长了. 好比咱们能够计算char, float类型
在程序设计时没有使用实际存在的类型, 而是使用虚拟的参数参数, 故其灵活性获得增强.
当用实际的类型来实例化这种函数时, 就好像按照模板来制造新的函数同样, 因此称为函数模板
格式: 通常用T来标识类型参数, 也能够用其它的
Template <class T>
像这样:
// 定义模板
template <class T>
// 定义函数模板
T sumWithValue(T x, T y) {
return x + y;
}
int main(int argc, const char * argv[]) {
// 调用模板函数
int sumValue1 = sumWithValue(3, 5);
// 调用模板函数
double sumValue2 = sumWithValue(3.2, 5.1);
cout << sumValue1 << endl;
cout << sumValue2 << endl;
return 0;
}
复制代码
当用用函数模板与具体的数据类型连用时, 就产生了模板函数, 又称为函数模板实例化
函数模板名<模板参数>(参数列表);
咱们能够将参数列表的数据强制转换为指定的数据类型
像这样
int sumValue2 = sumWithValue<int>(3.2, 5.1);
复制代码
咱们将参数列表里的数据强制转换为int类型, 再参与计算
也能够样:
double sumValue2 = sumWithValue(3.2, (double)5);
复制代码
咱们也能够将参数列表里的单个参数进行强制类型转换, 再参与计算
不过咱们通常不会加上模板参数.
用途就是代替template参数列表中的关键字class
像这样
template <typename T>
只是将class替换为typename, 其它同样使用.
强烈建议你们使用typename, 由于它就是为模板服务的, 而class是在typename出现以前使用的, 它还有定义类的做用, 不直观, 也会在一些其它地方编译时报错.
可能对于初学者来讲, 函数有点不是很好理解, 包括我当初也是, 不要想得过于复杂, 其实它就是一段有特定功能的代码, 只不过咱们给这段代码起了个名字而已, 这样就会提升代码的可读性和易维护性.