17.处理函数和数组声明
指向函数的指针声明与指向数组的指针声明很容易混淆,主要原因在于函数和数组修饰符的优先级比指针修饰符的优先级高,因此通常需要使用圆括号。
int *f1(); //一个返回值为int* 的函数
int (*fp1)(); //一个指针,指向一个返回值为int的函数
const int N =12;
int* a1[N]; //一个具有N个int *元素的数组
int (*ap1)[N]; //一个指针,指向一个具有N个int元素的数组
int *(*fp3)(); //一个指针,指向一个返回值为int*的函数
注意,参数和返回值都会影响函数或函数指针的类型。
char* (*fp4)(int,int);
char* (*fp5)(short,short)= 0;
fp4 = fp5; //错误!类型不匹配
当函数和数组修饰符出现在同一声明中时,事情的复杂性会变得难以估计。如下:
int (*)()afp1[N]; //语法错误!
在以上错误的声明中,企图声明一个函数指针数组。但函数修饰符()的出现表示到了声明的末尾,而后附加的afp1则表示开始出现了一个语法错误。
函数指针数组的正确声明方式,是将数组名字和简单的函数指针声明放在一起。
int (*afp2[N])(); //一个具有N个元素的数组,其元素类型是指向返回值为int的函数指针。
条款18函数对象
与智能指针一样,函数对象时一个普通的类对象。智能指针重载->和*操作符来模仿指针的行为;而函数对象类型则重载函数调用操作符(),来创建类似函数指针的东西。
class A{
public:
int operator()();
}
A a;
a(); //可以像调用函数一样调用函数对象。
条款19 Command模式与好莱坞法则
命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
好莱坞法则:不要call我们,我们会call你
条款20 STL函数对象
STL中诸如sort,find_if等模板函数允许有第三个参数(sort中是一个比较函数指针或比较函数对象,find_if是一个判断函数指针或一个判断函数对象),这个参数可以是函数指针,也可以是函数对象。
用函数对象叫函数指针的好处是比较操作可以被内联,但是用函数指针则不允许内联。原因在于,当一个sort或find_if函数模板实例化时,编译器知道比较函数对象的类型,从而比较函数对象的operator ()将被调用,接着使他内联函数,而函数指针无法做到。
条款21重载和重写并不相同
条款22 Template Method模式
模板方法模式:模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
通常来说,Template Method被实现为一个公有非虚函数,它调用被保护的虚函数。派生类必须接受它继承的非虚基类所指明的全部实现,同时还可以通过重写该共有函数所调用的被保护的虚函数,以有限的方式来定制其行为。
class App{
public:
virtual ~App();
void startup()
{
initialize();
if( !validate())
altInit();
}
protected:
virtualbool validate() const = 0;
virtualvoid alInit();
private:
voidinitialize();
};
class MyApp : public App{
public:
private:
boolvalidate() const;
voidaltInit();
};
Template Method是好莱坞法则的例子。startup函数的整体流程由基类决定,客户通过基类的接口来调用startup,因此派生类设计者不知道validate或altInit何时会被调用。但他们知道当这两个方法被调用时,它们各自应该做什么。
条款23 名字空间
条款24成员函数查找
步骤1.编译器检查查找函数的名字
步骤2.从可用候选者中选择最佳匹配函数
步骤3.检查是否具有访问该匹配函数的权限
条款25实参相依的查找
实参相依的查找(ADL)思想:当查找一个函数调用表达式中的函数名字时,编译器也会到“包含函数调用实参的类型”的名字空间中检查。考虑如下代码:
namepaceorg_semantics{
class X{…};
void f(const X&);
void g(X*);
X operator+ (const X&, const X&);
class String{…};
std::ostream operator << (std::ostream &, const String&);
}
//…
int g(org_semantice::X *);
voidaFunc()
{
org_semantics::Xa;
f(a); //调用org_semantics::f
g(&a); //错误!调用歧义
a= a+a; //调用org_semantics::operator+
}
普通的查找是不会发现函数org_semantice::f的,因为它被嵌套在一个名字空间内,并且对f的使用需要以该名字空间的名字加以限定。然而,由于实参a的类型被定义org_semantics名字空间中,因此,编译器也会到该名字空间中检查候选函数。
但对函数g的调用就会产生歧义。