65.9K
CodeProject 正在变化。 阅读更多。
Home

仿函数和成员函数指针

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (7投票s)

2010年1月12日

CPOL

2分钟阅读

viewsIcon

27473

downloadIcon

142

一篇关于使用 C++ 模板函子和成员函数指针的文章。

问题

假设你需要使用一个以 COM 风格设计的遗留 C++ 类。该类的一些方法返回 HRESULT,并接受一个参数是指针,该指针可以指向一个 int,一个 short,一个 std::string,一个枚举类型,等等。例如

HRESULT GetName(std::string* name);

解决方案 1

调用这些方法的显而易见的方式是

std::string name;
if (SUCCEEDED(obj->GetName(&name)))
    m_name = name;
else
    m_name = "Untitled";

如果你一遍又一遍地使用这种方式调用这些方法,你会产生相当多的重复代码。

解决方案 2

一种解决方法是为具有相同参数类型的方法提供一个辅助函数。例如,对于接受 int* 作为参数的方法,你可以编写一个辅助函数,如下所示

int GetInt(MyClass* obj, HRESULT (MyClass::*func)(int*), int defaultValue)
{
    int i = defaultValue;

    if (SUCCEEDED(obj->*func(&i))) // call the member method
        return i;
    else
        return defaultValue;
}

obj 参数指向一个 MyClass 对象,成员方法将在其上被调用;func 参数指向 MyClass 的一个成员方法,将被调用;defaultValue 参数是在成员方法调用失败时要返回的值。辅助函数隐藏了检查成员方法返回的 HRESULT 的细节。

现在,当你需要调用 MyClass 对象上的一个方法时,你可以使用辅助函数,例如:

int length = GetInt(obj, &MyClass::GetLength, 0);

调用 GetName() 方法的代码可以简化为

m_name = GetString(obj, &MyClass::GetName, "Untitled");

代码变得更加简洁。但是,存在一个问题:你需要为每个类中具有相同参数类型的方法组编写一个辅助函数。假设你有五个类,并且它们都具有使用 10 种不同参数类型的方法,那么你必须创建 50 个辅助函数,这相当多的(看似重复的)代码。

更好的解决方案

有没有更好的解决方案?是的,通过结合模板和成员函数指针。我们可以定义一个模板函子类 PropertyGetter

///////////////////////////////////////////////////////////////////////////////
// A Functor for calling member methods that returns HRESULT and takes a
// pointer as the parameter, e.g. HRESULT MyClass::GetLength(int* length)
//
// T represents a class whose method is to be called.
// ARG represents the type of the parameter of the method to be called.
///////////////////////////////////////////////////////////////////////////////
template<typename T, typename ARG>
class PropertyGetter
{
    T* m_object; // The object on which the method is to be called
    HRESULT (T::*m_func)(ARG*); // The member method to be called

public:
    PropertyGetter(T* obj, HRESULT (T::*func)(ARG*))
      : m_object(obj), m_func(func)
    {
    }

    ARG operator()(ARG defaultValue)
    {
        ARG a = defaultValue;
        if (SUCCEEDED((m_object->*m_func)(&a))) // call the member method
        {
            return a;
        }
        else
        {
            return defaultValue;
        }
    }
};

模板参数 T 表示你想要调用其方法的类,而 ARG 表示方法参数的类型。构造函数接受两个参数:一个类实例的指针和一个成员方法的指针。operator() 在类实例上调用目标方法。如果调用成功,它将返回方法返回的值;否则,它将返回传入的 defaultValue

有了这个模板类,你可以调用任何类的任何方法,只要该方法返回 HRESULT 并接受一个指针作为参数。现在可以这样调用 GetName()GetLength() 方法:

m_name = PropertyGetter<MyClass, std::string>(obj,
    &MyClass::GetName)("Untitled");

int length = PropertyGetter<MyClass, int>(obj, &MyClass::GetLength)(0);

这可以概念性地转换为

{
    PropertyGetter<MyClass, std::string> temp1(obj, &MyClass::GetName);
    m_name = temp1("Untitled");
}

int length;
{
    PropertyGetter<MyClass, int> temp2(obj, &MyClass:GetLength);
    length = temp2(0);
}

如你在此示例中所见,如果使用得当,模板和函数指针的结合可以帮助你使代码更简洁。

© . All rights reserved.