C++ primer 摘要《函数重载》20090120

来源:互联网 发布:怎样写好钢笔字 知乎 编辑:程序博客网 时间:2024/06/04 18:54

===9.1.1 为什么要重载一个函数名===
我们可能希望定义一组函数它们执行同样的一般性动作,但是应用在***不同的参数类型***上(注意是参数)
===9.1.2 怎样重载一个函数名===
1、如果两个函数的参数表中参数的个数或类型不同,则认为这两个函数是重载的
2、当一个参数类型是const 或volatile 时,在识别函数声明是否相同时,并不考虑const 和volatile 修饰符
// 声明了同一函数,编译报错
void f( int );
void f( const int );

如果把const 或volatile 应用在指针或引用参数指向的类型上,则在判断函数声明是否相同时,就要考虑const 和volatile 修饰符
// 声明了不同的函数
void f( int* );
void f( const int* );
// 也声明了不同的函数
void f( int& );
void f( const int& );

===9.1.4 重载与域===
重载函数集合中的全部函数都应在***同一个域***中声明
每个类都维持着自己的一个域
每个名字空间也都维持着自己的一个域,作为不同名字空间成员的函数不能相互重载

#include <string>
void print( const string & );
void print( double ); // overloads print()
void fooBar( int ival )
{
    // 独立的域隐藏print()的两个实例
    extern void print( int );
    // 错误: print( const string & )在这个域中被隐藏
    print( "Value : " );
    print( ival ); // ok: print( int ) 可见
}
using 声明和using 指示符可以使一个名字空间的成员在另一个中可见,这些机制对于重载函数的声明有一些影响
记住:using 声明只是一个声明,由using 声明引入的函数就好像在该声明出现的地方被声明一样。因此由using 声明引入的函数重载了在该声明所出现的域中同名函数的其他声明
#include <string>
namespace libs_R_us {
    extern void print( int );
    extern void print( double );
}
extern void print( const string & );
// libs_R_us::print( int ) 和 libs_R_us::print( double )
// 重载 print( const string & )
using libs_R_us::print;

void fooBar( int ival )
{
    print( "Value: " ); // 调用全局 print( const string & )
    print( ival ); // 调用 libs_R_us::print( int )
}

//如果using 声明向一个域中引入了一个函数,而该域中已经有同名函数且具有相同的参数表,则该using 声明就是错误的
namespace libs_R_us {
    void print( int );
    void print( double );
}
void print( int );
using libs_R_us::print; // 错误: print(int) 的重复声明

===9.1.5 extern "c" 和重载函数===
链接指示符只能指定重载函数集中的一个函数(原因我想是因为C不支持重载)

===9.1.7 类型安全链接===
每个函数名及其相关参数表都被作为一个惟一的内部名编码,一般的做法是把参数的个数和类型都进行编码然后再将其附在函数名后面
因为这种编码帮助链接阶段区分程序中的重载函数,所以我们把它称作类型安全链接type-safe linkage(应该是在编译阶段实现,这样链接时就可以根据编码后的不同函数名来区分重载函数)

这种特殊编码不适用于用链接指示符extern "C"声明的函数

===9.2重载解析的三个步骤===
1、确定函数调用考虑的重载函数的集合,确定函数调用中实参表的属性(确定候选函数即重载函数集、实参的数目和类型)
2、从重载函数集合中选择函数,该函数可以在给出实参个数和类型的情况下用调用中指定的实参进行调用(根据实参的数目和类型==>可行函数)(可行函数能够调用实参)
3、选择与调用最匹配的函数(根据参数转换等级来确定)

===9.3.3 标准转换的细节===
1 整值类型转换 : 从任何整值类型或枚举类型向其他整值类型的转换(不包括前面提升部分中列出的转换)
2 浮点转换     : 从任何浮点类型到其他浮点类型的转换(不包括前面提升部分中列出的转换)
3 浮点—整值转换: 从任何浮点类型到任何整值类型或从任何整值类型到任何浮点类型的转换
4 指针转换     : 整数值0 到指针类型的转换和任何类型的指针到类型void*的转换
5 bool 转换    : 从任何整值类型浮点类型枚举类型或指针类型到bool 型的转换
类别1 2 和3 中的转换是有潜在危险的转换

所有的标准转换都被视为是等价的,优先级相同,类型之间的接近程度不被考虑
从而:
extern void farith( unsigned int );
extern void farith( float );
int main() {
    // 每个调用都是二义的
    farith( 'a' ); // 实参类型为 char
    farith( 0 ); // 实参类型为 int
    farith( 2uL ); // 实参类型为 unsigned long
    farith( 3.14159 ); // 实参类型为 double
    farith( true ); // 实参类型为 bool
    return 0;
}

类型转换的等级划分如下:精确匹配好于提升,提升好于标准转换

 一个转换序列潜在地由下列转换以下列顺序构成
左值转换——提升或者标准转换——限定修饰转换

===9.3.4 引用===
int obj;
void frd( double & );
int main()
{
    frd( obj ); // 错误: 参数必须是 const double &
    return 0;
}
对frd()的调用是错误的.实参类型是int 必须被转换成double 以匹配引用参数的类型,
该转换的结果是个临时值,因为这种引用不是const 型的,所以临时值不能被用来初始化该引用

关于临时变量不能用来初始化非const引用参数的说明参见http://blog.csdn.net/kongying168/archive/2009/02/05/3864756.aspx

void print( int );
void print(int&);
int iobj;
int &ri = iobj;
int main()
{
    print( iobj ); // 错误: 二义,对象iobj 是与两个函数print()都精确匹配的实参
    print( ri ); // 错误: 二义,引用ri 指向一个对象它是两个函数的精确匹配
    print( 86 ); // ok: 调用 print( int )
    return 0;
}            

原创粉丝点击