第七章
- 数组作为函数的参数:
常规变量传入是传入变量的拷贝,而传入数组是传入数组的地址。
int sum_arr(int arr[], int n)
{
int total = 0;
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
方括号表示可传入任意长度,arr是指针而不是数组,但在函数中看作数组来处理。或者写为int sum_arr(int * arr, int n)是等价的。以下两个式子是等价的:
arr[i] == * (arr + i);
&arr[i] == ar + i
若为二维数组,函数声明为int sum(int *ar2[4], int size);或者int sum(int ar2[][4], int size);
ar2[r][c] == * ( * (ar2+r) + c)是等价的。一种数组提前结束循环的方式:
int fill_array(double ar[], int limit)
{
using namespace std;
double temp;
int i;
for (i = 0; i < limit; i++)
{
cout << "Enter value #" << (i + 1) << ": ";
cin >> temp;
if (!cin) // bad input
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input; input process terminated.\n";
break;
}
else if (temp < 0) // signal to terminate
break;
ar[i] = temp;
}
return i;
}
若不想让函数改变数组可以这样声明:void show_array(const double ar[], int n);
- 使用数组区间的函数:
int sum_arr(const int * begin, const int * end)
{
const int * pt;
int total = 0;
for (pt = begin; pt != end; pt++)
total = total + *pt;
return total;
}
sum = sum_arr(cookies, cookies + 3);
sum = sum_arr(cookies + 4, cookies + 8);
调用中开头是要调用的数组对象,结束是需要调用的最后一个数组对象的下一个位置。
指针和const注意以下几点
- const int *pt指向的对象的值不能变,即不能利用pt指针去改变它所指对象的值。
- int * const pt表示指针的指向不能变,即该指针是一个常量。
- const变量必须由const指针去指向,const指针不一定指向const变量,若指向常规变量,可以通过常规变量改变值,不能通过指针改变值。
char数组字符串和string字符串作为函数参数
unsigned int c_in_str(const char * str, char ch)
{
unsigned int count = 0;
while (*str) // quit when *str is '\0'
{
if (*str == ch)
count++;
str++; // move pointer to next char
}
return count;
}
void display(const string sa[], int n)
{
for (int i = 0; i < n; i++)
cout << i + 1 << ": " << sa[i] << endl;
}
函数指针:
函数名即为函数地址:
process(think);
thought(think());
前者是传递函数地址,后者是传递函数返回值。因为函数名即为函数地址:所以double pam(int)是函数,double ( * pf)(int)也是函数。pf是一个指向pam的指针。注意( * pf)的括号不能省略,否则表示返回值是一个指针。
在调用过程中,可以用函数地址调用,也可以用指针调用:即pf和(*pf)是等价的。例如:
pf = pam;
pam(4);
pf(5);
(*pf)(5);const double * f1(const double ar[], int n);
const double * f2(const double ar[], int n);
const double * f3(const double * ar, int n);
以上三种形式表示同一个函数。
若声明指向他们的指针其实就把f1用( * p1 )代替即可。即为const double * ( * p1)(const double *, int ) = f1;
利用C++11的自动类型推断,可写为auto p2 = f2;
若声明一个他们的指针数组,[]运算符优先级高于*,所以形式如下:
const double * ( * p1[3])(const double *, int );
若要声明指向以上数组的指针,则表示[3]之前的类型就应该是一个指针,形式如下:
const double * ( * ( * pd)[3])(const double *, int );
第八章
内联函数的编译代码与其他程序代码”内联”起来了.编译器将使用相应的函数代码替换函数调用.内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存.所以应该有选择的使用内联函数.
- 要是用这项特性,必须采取下述措施之一:
在函数声明前加上关键字inline
在函数定义前加上关键字inline
- 要是用这项特性,必须采取下述措施之一:
引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它.如下:rats和rodents的值和地址都相同。
int rats;
int & reodents = rats;
和指针相比:rodents和*prats和rats等价;&rodents和prats和&rats等价。相当于rodents只是rats的另外一个名字而已。
- 引用作为函数的参数是引用传递,即把变量的地址给了函数,函数可以直接改变变量的值。
比较引用传递和指针传递:
void swapr(int & a, int & b) // use references
{
int temp;
temp = a; // use a, b for values of variables
a = b;
b = temp;
}
void swapp(int * p, int * q) // use pointers
{
int temp;
temp = *p; // use *p, *q for values of variables
*p = *q;
*q = temp;
}
调用时:swapr(wallet1, wallet2);
swapp(&wallet1, &wallet2);
- 在实参和引用形参不匹配时,且参数为const引用时,C++会生成临时变量。例如:
double refcube(const double &ra)
{
return ra*ra*ra;
}
- 返回类型是引用的函数:
sysop & use(sysop & sysopref)
{
cout << sysopref.name << " says:\n";
cout << sysopref.quote << endl;
sysopref.used++;
return sysopref;
}
如果不希望返回的引用被更改,可以在最前面加上const。如:const sysop & use(sysop & sysopref)
注意:不能用函数结束会释放的内存作为引用返回值。
- 带默认参数的函数:
只在原型指定默认值,函数定义不变。
char * left(const char * str, int n = 1);
int main()
{
}
char * left(const char * str, int n)
{
if(n < 0)
n = 0;
char * p = new char[n+1];
int i;
for (i = 0; i < n && str[i]; i++)
p[i] = str[i]; // copy characters
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
return p;
}
函数重载:
定义名称相同的函数,通过函数特征标来区别调用哪个函数。
类型引用和类型本身视为同一个特征标。
返回类型可以不同,特征标也必须不同。不能只通过返回类型来重载。函数模板:
template <typename T> // or class T
void Swap(T &a, T &b)
{
T temp; // temp a variable of type T
temp = a;
a = b;
b = temp;
}
比如在结构体中,不想用模板同时操作所有结构体数据,则可以显示具体化,编译器会优先考虑具体化定义,不会先调用模板,例如:
struct job
{
char name[40];
double salary;
int floor;
};
template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
// 只交换salary和floor
template <> void Swap<job>(job &j1, job &j2) // 具体化
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
如果想定义一个int类型的模板,则可以用显示实例化:
template void swap < int >(int, int);
注意:
隐式实例化:编译器根据所给参数的类型生成相应类型的模板函数实例
显式实例化:编译器指定的类型生成相应类型的模板函数实例
模板特化(显式具体化):对特定类型的需要专门编写的模板函数实例
template< typename T >
void swap(T a,T b){}
template void swap < int >(int a,int b);//显式实例化
template<> void swap < int > (int a,int b);//模板特化,针对int类型专门编写的函数
template<> void swap < float >(float a,float b);//模板特化,针对float类型专门编写的函数
- 编译器匹配问题:
遵循规则:完全匹配,提示转换,标准转换,用户定义的转换。
在同时完全匹配后会选择最佳匹配。
一般而言,指向非const指针和引用优先,非模板函数优先,较具体的模板优先。