在模板类 T> 的函数中使用指向类 T 成员的函数指针作为参数的不良做法



首先,对不起标题。我无法真正将我想问的问题浓缩成一句话:(

我正在阅读这篇文章,它以某种方式让我想到了函数指针。具体来说,我想知道为什么将类成员函数作为函数参数传递,然后在该函数中的现有对象上使用该指针是"糟糕的"(或者至少很少见)。

假设我有一个模板类"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() 成为非常量,因为这会使返回的引用出现各种其他恶作剧。我的解决方案是将一个新的公共函数 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(&SomeOtherClass::someFunc);可能会在运行时崩溃,但我认为可以通过在 incrementData() 中检查 U 的类型与 T 来解决。此外,实际上,modifyData 只接受 void (*)() 函数,但这可以用可变参数模板来解决。

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

谢谢!

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

简而言之,我想将用户对容器成员的访问权限限制为仅该成员的成员函数。有没有更简单的方法可以做到这一点,或者这种方法不符合我的意图?

我对你的架构没有任何问题 - 我不认为这是不好的做法。对我来说,这似乎是一种相当费力的保护数据的方法,并且并没有真正帮助您,因为用户可以使用任何 void 函数来修改包含的数据,这些数据并不是真正可以更改和不能更改的合同。

我认为这种结构如此罕见的原因是您对容器类的要求和目标是不寻常的。

最新更新