最近写代码遇到一个问题,在一个类中实现了函数重载,参数分别为int型和string*字符指针类型,当传递数值0时,始终调用第一个函数,当参数传递为NULL时同样调用的是第一个函数,而我原本以为会调用第二个函数。发现自己基础比较薄弱,后来查了下,发现NULL是这么定义的
#define NULL 0
或者
#define NULL ((void*)0)
发现问题得不到解决,后来想起《Effective C++》上有介绍,所以专门查看了一下。书上是这么介绍的:
条款25: 避免对指针和数字类型重载
两个函数:
第一个void f(int)
第二个void f(string*)
当这样调用时 f( 0 ) 必然调用的是第一个函数。
而显示转换后:
f(static_cast<string*>(0))调用的是第二个函数。
如果定义一个常量void* const null = 0,然后调用f(null)会怎样?答案是编译器给出error cannot convert parameter 1 from 'void *const ' to 'int'。
有点挫败感,怎样才能传0的时候调用的是第二个函数呢???
前面已经提到了通过显示转换是可以实现的。那么有什么办法可以得到static_cast<string*>(0)这样的效果呢?可以想到的就是模板。什么样的模板才可以呢?这个模板可以将数值0显示转换成任意类型的指针。相信不少同学都使用或是见到过这样的用法operator char() const { ... }等等之类的。那如果我们定义一个下面这样的成员函数
operator T*() const
{
<pre name="code" class="cpp"> <span style="font-family: Arial, Helvetica, sans-serif;">return 0;</span>
}
相信大家看到这里就明白了。可以做一个类来实现这个功能,比如
class NULLClass
{
public:
template<class T>
operator T*() const //区分开整数和指针
{
return 0; //每个函数返回一个null指针
}
};
《Effective C++》上还考虑到了函数指针的情况,比如一个函数指针是这样的:
typedef void (TestClass::*fun)(std::string*);
当函数void f(fun pfn){...}时会触发的情况。书中还提到了将这个类做成无具名类。
贴出我的测试代码
#include <string>
#include <iostream>
using namespace std;
class NULLClass
{
public:
template<class T>
operator T*() const //区分开整数和指针
{
return 0; //每个函数返回一个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;
} TESTNULL;
class TestEF22;
typedef void (TestEF22::*fun)(std::string*);
class TestEF25
{
public:
TestEF25();
~TestEF25();
void f(int x)
{
std::cout << "f(int)" << std::endl;
}
void f(string *ps)
{
std::cout << "f(string*)" << std::endl;
}
void test(fun ps)
{
std::cout << "test(fun*)" << std::endl;
}
private:
};
void* test_null = 0;
#define NULL_1 0L
const NULLClass NULL_2;
TestEF25::TestEF25()
{
// f(0); //f(int)
// f(static_cast<string*>(test_null)); //f(string*)
// f(static_cast<string*>(0)); //f(string*)
// f(test_null); //error cannot convert parameter 1 from 'void *const ' to 'int'把调用“错误”转成了编译错误
f(NULL_1);
f(NULL_2);
test(TESTNULL);
f(NULL);
}
TestEF25::~TestEF25()
{
}
其实,书中该条款的名称就已经说明我们在设计的时候要避免对指针和数字的重载,因为,你无法避免别人怎样使用你的代码。