将函数指针用作类T的成员作为模板类<t>?</t>的函数中的参数的不良做法

时间:2012-08-14 15:14:58

标签: c++ templates function-pointers member-function-pointers

首先,抱歉标题。我无法真正浓缩我想要用一个短语提出的问题:(

我正在阅读this post,它以某种方式让我思考函数指针。具体来说,我想知道为什么将类成员函数作为函数参数传递“坏”(或者至少很少见),然后在该函数中的现有对象上使用该指针。

假设我有一个模板类“Container”,它存储一个T类型的单个变量,并提供一个方法来获取对该变量的const引用。

template<class T>
class Container {
public:
    Container(T anObject) {
        m_data = anObject;
    }
    const T& getData() const {
        return m_data;
    }
private:
    T m_data;
};

现在,我希望能够在m_data上执行T的成员函数,但是我不想让getData()非const,因为这会使得返回引用的各种其他恶作剧成为可能。我的解决方案是向Container添加一个新的公共函数modifyData(...),它将一个函数指针作为参数获取T的成员函数并在m_data上执行;像这样:

// ...
void modifyData( void(typename T::*funcptr)(void) ) {
    (m_data.*fptr)();
}
// ...

按原样,如果T是指针,这将崩溃并刻录。为了测试,我刚刚为Container<T*>创建了一个专门的模板来解决这个问题,但我相信会有更优雅的方式。

一个非常有代表性的例子表明,这似乎按预期工作:

// example class to be used with Container
class Data {
public:
    Data() {m_count = 0; }
    void incrementCount() { m_count++; }
    int getCount() const { return m_count; }
private:
    int m_count;
};

// ... in main.cpp:
Data dat;
Container<Data*> DCont(dat);
std::cout << cl.getData()->getCount() << std::endl; // outputs 0
DCont.modifyData<Data>(&Data::incrementCount);
std::cout << cl.getData()->getCount() << std::endl; // outputs 1

// compiler catches this:
// DCont.modifyData<SomeOtherClass>(&Data::incrementCount); 
// this probably does something bad:
// DCont.modifyData<SomeOtherClass>(&SomeOtherClass::someFunc); 

现在,本能地这似乎是一种可怕的扭曲的做事方式,而且我从未见过这样的代码。但我的问题是,是否存在性能/安全性原因,为什么这样的事情很糟糕,或者它只是被认为是不好的做法?如果这是“只是”不好的做法,那么为什么呢?

我能想到的显而易见的限制是类似的     // DCont.modifyData(&amp; SomeOtherClass :: someFunc); 可能会在运行时崩溃,但我认为可以通过在incrementData()中检查U对T的类型来解决。另外,实际上,modifyData只接受void(*)()函数,但这可能通过可变参数模板解决。

这个例子显然是很好的解释,并没有很好地实现,但我认为(希望?)它足以解释我在说什么。

谢谢!

编辑:关于问题是什么似乎有些混乱。基本上,这就是我正在讨论的场景:你有一些来自某个库的类,你试图存储在容器中,另一个函数生成某些容器;现在,您希望用户能够在这些容器中的对象上调用现有的成员函数,而不是修改实际的对象(比如在使用getter返回非const引用时)。实际的实现可能会使用某种可变参数模板,但我需要在发布示例代码之前再考虑一些。

简而言之,我想将用户对容器成员的访问限制为仅限该成员的成员函数。是否有一种更简单的方法可以做到这一点,或者这种方式不按我想要的方式工作?

1 个答案:

答案 0 :(得分:1)

我的架构没有任何问题 - 我认为这不是一种不好的做法。对我来说,保护数据似乎是一种非常费力的方式,并没有真正帮助你,因为用户可以使用任何void函数来修改所包含的数据,这实际上是一个关于什么能够和不能改变的合同。

我认为这个构造很少见的原因是你的容器类的要求和目标是不寻常的。