peakyblinder的博客

C++大白的学习之路

第七章

        C++ 语言使用调用操作符(即一对圆括号)实现函数的调用。

        如果将形参定义为非引用的 const 类型:

void fcn(const int i) { /* fcn can read but not write to i */ }

        则在函数中,不可以改变实参的局部副本。由于实参仍然是以副本的形式传递,因此传递给 fcn 的既可以是 const 对象也可以是非 const 对象。

        令人吃惊的是,尽管函数的形参是 const,但是编译器却将 fcn 的定义视为其形码被声明为普通的 int 型:

void fcn(const int i) { /* fcn can read but not write to i */ }
void fcn(int i) { /* ... */ } // error: redefines fcn(int)
const引用与非const引用

        const 引用可以初始化为不同类型的对象或者初始化为右值

int i = 42;
// legal for const references only
const int &r = 42;
const int &r2 = r + i;

        同样的初始化对于非 const 引用却是不合法的,而且会导致编译时错误。其原因非常微妙,值得解释一下。

        观察将引用绑定到不同的类型时所发生的事情,最容易理解上述行为。假如我们编写

double dval = 3.14;
const int &ri = dval;

        编译器会把这些代码转换成如以下形式的编码 :

int temp = dval; // create temporary int from the double
const int &ri = temp; // bind ri to that temporary

        如果 ri 不是 const,那么可以给 ri 赋一新值。这样做不会修改 dval,而是修改了 temp。期望对 ri 的赋值会修改 dval 的程序员会发现 dval 并没 有被修改。

        非 const 引用只能绑定到与该引用同类型的对象。 const 引用则可以绑定到不同但相关的类型的对象或绑定到右值。

7.2.4

        编译器忽略为任何数组形参指定的长度。 当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型时是否匹配,而不会检查数组的长度。

        如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身。在这种情况下,数组大小成为形参和实参类型的一部分。编译器检查数组的实参的大小与形参的大小是否匹配:

void printValues(const int ia[10])
{
// this code assumes array has 10 elements;
// disaster if argument has fewer than 10 elements!
for (size_t i = 0; i != 10; ++i)
{
cout << ia[i] << endl;
}
}
int main()
{
int i = 0, j[2] = {0, 1};
printValues(&i); // ok: &i is int*; probable run-time
error
printValues(j); // ok: j is converted to pointer to 0th
// element; argument has type int*;
// probable run-time error
return 0;
}
        虽然编译没有问题,但是这两个调用都是错误的,可能导致运行失败。在这两个调用中, 由于函数 printValues 假设传递进来的数组至少含有 10 个元素,因此造成数组内在的越界访问。程序的执行可能产生错误的输出,也可能崩溃,这取决于越界访问的内存中恰好存储的数值是什么。 
// ok: parameter is a reference to an array; size of array is fixed
void printValues(int (&arr)[10]) { /* ... */ }
int main()
{
int i = 0, j[2] = {0, 1};
int k[10] = {0,1,2,3,4,5,6,7,8,9};
printValues(&i); // error: argument is not an array of 10 ints
printValues(j); // error: argument is not an array of 10 ints
printValues(k); // ok: argument is an array of 10 ints
return 0;
}

        这个版本的 printValues 函数只严格地接受含有 10 个 int 型数值的数组,这限制了哪些数组可以传递。

7.3.1

        return 语句有两种形式:

        return;
        return expression;

        返回类型为 void 的函数通常不能使用第二种形式的 return 语句,但是,它可以返回另一个返回类型同样是 void 的函数的调用结果: 
void do_swap(int &v1, int &v2)
{
int tmp = v2;
v2 = v1;
v1 = tmp;
// ok: void function doesn't need an explicit return
}
void swap(int &v1, int &v2)
{
if (v1 == v2)
return false; // error: void function cannot return a value
return do_swap(v1, v2); // ok: returns call to a void function
}

7.3.2

        理解返回引用至关重要的是:千万不能返回局部变量的引用。 (effective C++ 条款20)

7.6

        将函数指定为 inline 函数,(通常)就是将它在程序中每个调用点上“内联地”展开。假设我们将 shorterString 定义为内联函数,则调用:

cout << shorterString(s1, s2) << endl;

        在编译时将展开为:

cout << (s1.size() < s2.size() ? s1 : s2)<< endl;

        内联函数应该在头文件中定义,这一点不同于其他函数inline 函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码。此时,仅有函数原型是不够的。

        编译器隐式地将在类内定义的成员函数当作内联函数

7.7.3

        如果没有为一个类显式定义任何构造函数,编译器将自动为这个类生成默认构造函数。 由编译器创建的默认构造函数通常称为默认构造函数, 它将依据如同变量初始化 的规则初始化类中所有成员。

7.8

        如果两个函数的形参表完全相同,但返回类型不同,则第二个声明是错误的: 

Record lookup(const Account&);
bool lookup(const Account&); // error: only return type is different

7.8.1

        如果局部地声明一个函数,则该函数将屏蔽而不是重载在外层作用域中声明的同名函数 :

void print(const string &);
void print(double); // overloads the print function
void fooBar(int ival)
{
void print(int); // new scope: hides previous instances of
print
print("Value: "); // error: print(const string &) is hidden
print(ival); // ok: print(int) is visible
print(3.14); // ok: calls print(int); print(double) is hidden
}

        在 C++ 中,名字查找发生在类型检查之前。

void print(const string &);
void print(double); // overloads print function
void print(int); // another overloaded instance
void fooBar2(int ival)
{
print("Value: "); // ok: calls print(const string &)
print(ival); // ok: print(int)
print(3.14); // ok: calls print (double)
}

7.8.3

候选函数:

        候选函数是与被调函数同名的函数,并且在调用点上,它的声明可见。

可行函数 :

        可行函数必须满足两个条件:

        第一,函数的形参个数与该调用的实参个数相同;

        第二,每一个实参的类型必须与对应形参的类型匹配,或者可被隐式转换为对应的形参类型。

        如果没有找到可行函数,则该调用错误。

寻找最佳匹配 :

        实参类型与形参类型越接近则匹配越佳。因此,实参类型与形参类型之间的精确类型匹配比需要转换的匹配好。

多个形参的重载确定 :

        编译器通过依次检查每一个实参来决定哪个或哪些函数匹配最佳。如果有且仅有一个函数满足下列条件,则匹配成功:

        1. 其每个实参的匹配都不劣于其他可行函数需要的匹配。

        2. 至少有一个实参的匹配优于其他可行函数提供的匹配。

7.8.4

        为了确定最佳匹配,编译器将实参类型到相应形参类型转换划分等级。转换等级以降序排列如下:
        1. 精确匹配。实参与形参类型相同。
        2. 通过类型提升实现的匹配(第 5.12.2 节)。
        3. 通过标准转换实现的匹配(第 5.12.3 节)。

        4. 通过类类型转换实现的匹配(第 14.9 节将介绍这类转换)。 


7.9

        在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针。假设有函数:
bool lengthCompare(const string &, const string &);

        除了用作函数调用的左操作数以外,对 lengthCompare 的任何使用都被解释为如下类型的指针:

bool (*)(const string &, const string &);
        函数指针只能通过同类型的函数或函数指针或 0 值常量表达式进行初始化或赋值。

        将函数指针初始化为 0,表示该指针不指向任何函数。指向不同函数类型的指针之间不存在转换。

通过指针调用函数:

        指向函数的指针可用于调用它所指向的函数。可以不需要使用解引用操作符,直接通过指针调用函数

cmpFcn pf = lengthCompare;
lengthCompare("hi", "bye"); // direct call
pf("hi", "bye"); // equivalent call: pf1 implicitly dereferenced
(*pf)("hi", "bye"); // equivalent call: pf1 explicitly dereferenced
函数指针形参:

        函数的形参可以是指向函数的指针。这种形参可以用以下两种形式编写:

// third parameter is a function type and is automatically treated as a pointer to function
void useBigger(const string &, const string &,bool(const string &, const string &));
// equivalent declaration: explicitly define the parameter as a pointer to function
void useBigger(const string &, const string &,bool (*)(const string &, const string &));
返回指向函数的指针:
int (*ff(int))(int*, int);

        阅读函数指针声明的最佳方法是从声明的名字开始由里而外理解。 要理解该声明的含义,首先观察:ff(int),将 ff 声明为一个函数,它带有一个 int 型的形参。该函数返回int (*)(int*, int);它是一个指向函数的指针,所指向的函数返回 int 型并带有两个分别是int* 型和 int 型的形参。使用 typedef 可使该定义更简明易懂:

typedef int (*PF)(int*, int);
PF ff(int); // ff returns a pointer to function

        具有函数类型的形参所对应的实参将被自动转换为指向相应函数类型的指针。但是,当返回的是函数时,同样的转换操作则无法实现:

// func is a function type, not a pointer to function!
typedef int func(int*, int);
void f1(func); // ok: f1 has a parameter of function type
func f2(int); // error: f2 has a return type of function type
func *f3(int); // ok: f3 returns a pointer to function type
指向重载函数的指针

        C++ 语言允许使用函数指针指向重载的函数:

extern void ff(vector<double>);
extern void ff(unsigned int);
// which function does pf1 refer to?
void (*pf1)(unsigned int) = &ff; // ff(unsigned)

        指针的类型必须与重载函数的一个版本精确匹配(不允许转换)。如果没有精确匹配的函数,则对该指针的初始化或赋值都将导致编译错误:

// error: no match: invalid parameter list
void (*pf2)(int) = &ff;
// error: no match: invalid return type
double (*pf3)(vector<double>);
pf3 = &ff;
阅读更多
想对作者说点什么? 我来说一句

通信原理第六版答案(第七章

2010年07月02日 173KB 下载

没有更多推荐了,返回首页

不良信息举报

第七章

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭