【问题标题】:Bad practice to use function pointers to members of a class T as parameters in functions of a template class<T>?使用指向类 T 成员的函数指针作为模板类<T> 的函数中的参数的坏习惯?
【发布时间】:2012-08-14 15:14:58
【问题描述】:

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

我正在阅读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&lt;T*&gt; 创建了一个专门的模板来解决这个问题,但我相信会有更优雅的方式。

一个非常容易理解的示例表明这似乎按预期工作:

// 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() 中根据 T 检查 U 的类型来解决这个问题。同样,modifyData 只接受 void (*)() 函数,但这可能可以通过可变参数模板来解决。

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

谢谢!

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

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

【问题讨论】:

  • "modifyData 的模板是必需的,因为编译器抱怨 T::*funcptr 因为 T 不是类或命名空间。" - 这不是必需的,你只需要使用“typename T::*funcptr”而不是“T::*funcptr”。
  • 另外,你能详细说明这里的问题是什么吗?比如,“有没有比专门针对 T* 的容器更好的方法?”或者“有没有比制作那个模板更好的方法?”,我在上面提到过?
  • 不能让用户使用成员 void f() 函数创建自己的类,将其转换为您的类类型,然后他的函数将使用您的 this 指针运行?
  • 感谢格里维斯!我将编辑问题以反映这一点。我也会尽量让问题更准确,见上文。
  • 我看到的主要“问题”(如果你愿意的话)是你将基本上开放对包含对象的公共函数接口的访问。 (至少对于那些无效的函数;但这会一直如此吗?)唯一的好处是防止访问 data 的公共成员,这些成员一开始就不应该存在(在你的例子中它们不存在) .在许多情况下,批发非常量访问的差异并不大,并且可能无法证明这种情况。

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


【解决方案1】:

我对您的架构没有任何问题 - 我不认为这是一种不好的做法。对我来说,保护数据似乎是一种相当费力的方式,并且对您没有太大帮助,因为用户可以使用任何 void 函数来修改包含的数据;这实际上并不是关于什么可以更改和不可以更改的合同。

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

【讨论】:

  • “因为用户可以使用任何 void 函数来修改包含的数据”:这正是我试图通过强制传递的函数是 T 的成员来解决的问题。例如,假设我有一堆来自我试图存储在容器中的库中的类,以及生成某些容器的另一个函数;我希望用户能够在这些容器内的对象上调用现有的成员函数,但不能修改实际的对象。就像我说的那样,实际的实现可能会使用某种可变参数模板来有用,但我需要一些......
  • ... 是时候提出一个合理的例子了。 (该死,600 个字符并不多。)您认为保护数据的更好方法是什么?
猜你喜欢
  • 2010-09-13
  • 1970-01-01
  • 2011-12-09
  • 2012-07-05
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
相关资源
最近更新 更多