`

认清C++语言之《成员函数查找》

 
阅读更多

成员函数查找:

1) 调用一个成员函数时,涉及三个步骤:一是编译器查找函数的名字;二是从可用候选者中选择最佳匹配函数;三是检查是否具有访问该匹配函数的权限。例如:

class App

{

public:

//........

void find(double);

};

class ACEApp : public App

{

void find(int);

};

//..........

ACEApp aa;

aa.find(13.4); //容易混淆

上面代码调用的是哪一个成员find?分析如下:

步骤一:查找函数名字。从ACEApp的作用域开始查找并且立即定位到ACEApp::find上。

步骤二:从可用候选者中选择最佳匹配函数。因为我们只有一个候选者,因此会尝试匹配该函数,通过将实参13.4double转换为int做到这一点。

步骤三:检查访问权限。我们可能得到一个错误,因为ACEApp::find是私有成员。

匹配的过程中,编译器一旦在内层作用域中找到一个,它就不会到外层作用域中继续查找该名字。内层作用域的名字会隐藏外层作用域中相同的名字。这一点和Java不同,因为在Java中,内层作用域中的方法名字和外层作用域中同名方法属于重载关系。

实际上,该名字甚至可以不是一个函数的名字:

class HWApp : public ACEApp

{

int f;

};

//..........

HWApp ha;

ha.f(12); //Err

实参相依的查找:

1) 实参相依的查找(Argument Dependent LookupADL):当查找一个函数调用表达式中的函数名字时,编译器也会到“包含函数调用实参的类型”的名字空间中检查。例如:

namespace ACE

{

class Hw {.....};

void find(const Hw &);

Hw operator +(const Hw &, const Hw &);

class String {....};

std::ostream operator <<(std::ostream &, const String &);

};

//......

void AceFunc()

{

ACE::Hw hw;

find(hw); //调用ACE::find

hw = hw + hw; //调用ACE::operator +

}

普通的查找是不会发现函数ACE::find的,因为它嵌套在一个名字空间内,并且对find的使用需要以该名字空间的名字加以限定。然而,由于实参hw的类型被定义于ACE名字空间中,因此,编译器也会到该名字空间中检查候选函数。

2) 实际上,很多程序员广泛地使用了ADL却没意识到这一点,例如:

ACE::String name("hw");

std::out <<"Hello, "<<name;

在这个例子中,对operator<<的第一个使用极可能调用的是类模板std::basic_ostream的一个成员函数,而第二个则是对位于ACE名字空间中重载的operator<<的非成员函数的调用。

操作符函数查找:

1) 有时看上去好像一个成员操作符函数重载了一个非成员的操作符,事实上并非如此,这不是重载,而是不同的查找算法。例如下面的类,它以成员函数的形式重载了一个操作符函数:

class ACEClass

{

public:

ACEClass operator %(const ACEClass &) const;//二元取余操作符

ACEClass memFunc1(const ACEClass &);

void memFunc2();

//.......

};

可以采用中缀或函数调用语法来调用这个重载的操作符函数:

ACEClass aa, ab, ac;

aa = ab % ac; //采用中缀语法调用成员操作符%

aa = ab.operator%(ac); //成员函数调用语法

aa = ab.memFunc1(ac); //另一个成员函数调用

当我们使用函数调用语法时,应用的是普通的函数查找规则,即对ab.operator%(ac)调用的处理方式与memFunc1的相同。而对于中缀操作符调用来说,编译器不仅会考虑成员操作符,也会考虑非成员操作符:

ACEClass operator %(const ACEClass &, int ); //非成员操作符

//.........

void ACEClass::memFunc2()

{

*this % 12; //调用非成员操作符%

operator %(*this, 12); //错误,实参太多

}

对于中缀操作符调用来说,编译器不仅会考虑成员操作符,也会考虑非成员操作符,上面代码中第一个对operator%的中缀调用,将会匹配非成员的那一个。注意:这不是函数重载的实例,而是编译器在两个不同地方查找候选函数。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics