文章目录
1 对象作为函数参数
将对象作为函数参数,是将实参对象的值传递给形参对象,这种传递是单向的。形参拥有实参的备份,当在函数中改变形参的值时,改变的是这个备份中的值,不会影响原来的实参的值。不能将数组的值作为函数参数,但可以将数组的地址值作为参数,即使用指针作为参数。
#include <iostream>
#include <string>
using namespace std;
void swap(string str1, string str2) {
string temp = str1;
str1 = str2;
str2 = temp;
}
int main() {
string str1 = "now";
string str2 = "before";
swap(str1, str2);
cout << str1 << "," << str2 << endl;
return 0;
}
输出结果:
now,before
2 对象指针作为函数参数
将指向对象的指针作为函数参数,形参是对象指针,实参是对象的地址值。虽然参数传递方式仍然是传值方式,但因为形参传递的就是实参本身,所以当在函数中改变形参的值时,改变的就是原来实参的值。大多数程序设计者为提高运行效率,常使用传对象地址值的方式,如传递类或结构对象的地址值。
#include <iostream>
#include <string>
using namespace std;
// 传入的对象是指向该对象的地址的,在函数中修改形参的值会对实际参数有影响
void swap(string* str1, string* str2) {
string temp = *str1;
*str1 = *str2;
*str2 = temp;
}
int main() {
string str1 = "now";
string str2 = "before";
swap(&str1, &str2);
cout << str1 << "," << str2 << endl;
return 0;
}
输出结果:
before,now
数组作为形参传入,数组名是数组指针名,数组指针名指向数组首地址,所以相当于传地址值,可以实现交换。
3 引用作为函数参数
可以使用“引用”作为函数的参数(引用形参)。在函数调用时,实参对象名传给形参对象名,形参对象名就成为实参对象名的别名。实参对象和形参对象代表同一个对象,所以改变形参对象的值就是改变实参对象的值。
实际上,在虚实结合时是把实参对象的地址传给形参对象,使形参对象的地址取实参对象的地址,从而使形参对象和实参对象共享同一个单元。这就是地址传递方式。
在说明引用参数时,不需要提供初始值,其初始值在函数调用时由实参对象提供。
#include <iostream>
#include <string>
using namespace std;
// 使用引用的方式作为形参传入,形参拿到的时实参对象的地址
void swap(string& str1, string& str2) {
string temp = str1;
str1 = str2;
str2 = temp;
}
int main() {
string str1 = "now";
string str2 = "before";
swap(str1, str2);
cout << str1 << "," << str2 << endl;
return 0;
}
输出结果:
before,now
系统向形参传递的是实参的地址而不是实参的值。通过使用引用参数,一个函数可以修改另一个函数内的变量。因为引用对象不是一个独立的对象,不单独占用内存单元,而对象指针要另外开辟内存单元(其内容是地址),所以传引用比传指针更好。
4 默认参数
默认参数是在函数原型中说明的,默认参数可以多于1个,但必须放在参数序列的后部。如果一个默认参数需要指明一个特定值,则在其之前的所有参数都必须赋值。
#include <iostream>
#include <string>
using namespace std;
void display(string s1, string s2 = "", string s3 = "");
void display(string s1, string s2, string s3) {
if (s2 == "" && s3 == "") {
cout << s1 << endl;
} else if (s3 == "" && s2 != "") {
cout << s1 << "," << s2 << endl;
} else {
cout << s1 << "," << s2 << "," << s3 << endl;
}
}
int main() {
string str1 = "now";
string str2 = "before";
string str3 = "after";
display(str1);
display(str1, str2, str3);
display(str3, str1);
display(str2, str3);
return 0;
}
使用默认参数,就不能对少于参数个数的函数进行重载。
5 使用const保护数据
用 const
修饰传递参数,意思是通知函数,它只能使用参数而无权修改它。这主要是为了提高系统的自身安全。C++中普遍采用这种方法。
6 深入讨论函数返回值
C++函数的返回值类型可以是除数组和函数以外的任何类型。数组只能返回地址。当函数返回值是指针或引用对象时,需要特别注意:函数返回所指的对象必须继续存在,因此不能将函数内部的局部对象作为函数的返回值。
6.1 返回引用的函数
函数可以返回一个引用,将函数说明为返回一个引用的主要目的是为了将该函数用在赋值运算符的左边。函数原型表示方法如下:
数据类型& 函数名(参数列表);
#include <iostream>
using namespace std;
int a[] = {2, 4, 6, 8, 10};
int& index(int); // 返回引用的函数原型
int& index(int i) {
return a[i];
}
int main() {
index(3) = 16; // 将数组a[3]的值更改为16
cout << index(3) << endl;
return 0;
}
6.2 返回指针的函数
类型标识符* 函数名(参数列表);
#include <iostream>
using namespace std;
float* input(int&);
float* input(int& n) {
cout << "input number:";
cin >> n;
auto * buf = new float[n];
for (int i = 0; i < n; i++) {
cin >> buf[i];
}
return buf;
}
int main() {
int num;
float* data;
data = input(num);
if (data) {
for (int i = 0; i < num; i++) {
cout << data[i] << " ";
}
delete data;
}
}
在C++中,除了内存分配失败之外,new不会返回空指针,并且没有任何对象的地址值为0。
7 内联函数
#include <iostream>
using namespace std;
inline int isNumber(char c) {
return (c >= '0' && c <= '9') ? 1 : 0;
}
int main() {
char c;
cin >> c;
// 可以将isNumber()函数定义为内联函数添加关键字inline,让C++编译器将isNumber()自动转换为函数中的表达式提高执行效率
if (isNumber(c)) {
cout << "you entered a digit";
} else {
cout << "you entered non-digit";
}
}
如果程序中多次调用这个函数,将会大大降低使用效率。为提高效率,常将函数main中对函数isNumber()的调用替换成表达式:
if ( (c >= '0' && c <= '9') ? 1 : 0)
这种替换用手工做是很繁琐的,可以让C++编译程序来做。添加关键字inline定义为内联函数:
inline int isNumber(char c)
则C++编译器在遇到对函数isNumber()调用的地方都用这个函数体替换该调用表达式。使用关键字inline说明的函数称内联函数。**在C++中,除具有循环语句、switch语句的函数不能说明为内联函数外,其他函数都可以说明为内联函数。**使用内联函数能加快程序执行速度,但如果函数体语句多,则会增加程序代码的大小。使用小的内联函数在代码速度和大小上可以取得折衷,其他情况下取决于程序员是追求代码速度,还是追求代码的规模。
由于编译器必须知道内敛函数的函数体,才能进行内联替换,因此,内兰函数必须在程序中第一次调用此函数的语句出现之前定义。
8 函数模板
Type max(Type m1, Type m2) {
return (m1 > m2) ? m1 : m2;
}
这里 Type
并不是一种实际的数据类型,在这个函数实例化时,希望编译器能用实际的类型来替代它。由于函数在设计时没有使用实际的函数,而是使用虚拟的类型参数,故其灵活性得到加强。当用实际的类型来实例化这种函数时,就好像按照模板来制造新的函数一样,所以称这种函数为函数模板。
一般都选T作为标识符来标识类型参数,规定模板以template关键字和一个形参表开头。
#include <iostream>
#include <complex>
#include <string>
using namespace std;
// 函数模板,class可以使用typename代替
template < class T >
T max(T m1, T m2) {
return (m1 > m2) ? m1 : m2;
}
template < class T >
void printer(complex<T> a) {
cout << "real is " << a.real() << ",imag is " << a.imag() << endl;
}
int main() {
cout
<< max(2, 5) << "\t"
<< max(2.0, 5.0) << "\t"
<< max('w', 'a') << "\t"
<< max("ABC", "ABD") << endl;
complex<int> num1(2, 3);
complex<double> num2(3.5, 4.5);
printer(num1);
printer(num2);
return 0;
}
函数模板中如果使用 min(double, int)
,如果用 min(8.5, 6)
就无法通过编译。这时可以对参数表中的参数进行强制转换,如min(8.5, (double)6);
更一般的方法是使用显示方式 min<double>(8.5, 6)
以便正确推断出模板参数。而显示调用方式 max<char>(95, 121)
则是输出字符。