欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Effective_C++:25、避免对指针型别和数值型别进行重载

程序员文章站 2022-07-15 12:24:04
...

25、避免对指针型别和数值型别进行重载

1、指针型别和数值型别进行重载的问题

指针保存的是其所指对象的地址,解析时将其变为数值类型。尤其是有时我们把一个空指针定义为0,则当传入一个值为0的参数,若对指针型别和数值型别都进行重载的函数,应当出现编译错误,因为此时编译器不知道0是指针型别还是数值型别。然而,实际上,编译器对于入参为0的情况,会认为其为数值型别,会调用参数为数值型别的函数,并不会出现编译错误。

void f(int x);
void f(string *ps);

2、尝试解决问题

为此,我们尝试定义一个常量,使其可以转换为指针型别和数值型别,则当函数参数为此常量时,即可表现出二义性。

1.如下,NULL可以转换为指针型别,然而需要显示类型转换,即调用类型转换操作符实现。而且调用数值型别的重载函数失败。

void * const NULL = 0;//void*指针可以转换为各类型指针,其本身定义为0
f(NULL);//错误,型别不符
f(static_cast<string *>(NULL);//成功,调用f(string *);

2.尝试用宏定义。在调用指针型别的重载函数时仍需要显示类型转换。

#define NULL 0
#define NULL ((void*) 0)
f(NULL);//成功,调用f(int);
f(static_cast<string *>(NULL));//成功,调用f(string *);

3.一个临时的解决办法。long int 0转换为int与null指针是相同的,不分优先、好坏。此时似乎可以实现让编译器编译错误。然而,当有一个f(long int)与f(string *)重载函数时,此方法就失效了。

#define NULL 0L
f(NULL);//错误,二义性

4.为此,我们希望定义这样一个类,其可以实现将对象隐式转换为所有类型指针,则当调用函数f传入该类对象时,可以将对象隐式转换为任意类型的指针,即解决了上述1、2中需要进行显示类型转换的问题。此时,引入成员函数模板,产生该class的一些成员函数。

class NullClass {
public:
    template<class T>
    operator T*() const { return 0; }
};
const NullClass NULL;

优化一下,没必要给出class名称,不需要一个以上的类对象;添加可使其隐式转换为指向类成员的指针的成员函数模板;最后,NULL是指针值,不允许取地址。

const
class {
public:
    template<class T>
    operator T*() const
    { return 0; }
    template<class C, class T>
    operator T C::*() const
    { return 0; }
private:
    void operator&() const;
} NULL;

3、避免对指针型别和数值型别进行重载

以上,我们定义了NULL来实现传入NULL时隐式类型转换为指针型别,来让编译器实现入参的二义性而编译错误。然而,回到最初的问题,若传入0,编译器仍然认为他是数值型别。

因此,最好的办法就是避免对指针型别和数值型别进行重载。