C++ 成员函数的抽象指针:安全性和替代性
在这个问题中,假设我们以一种友好、谨慎的方式处理了所有指针——为了防止问题膨胀,我不想在这里包含我的清理代码 假设我们有两个类,C++ 成员函数的抽象指针:安全性和替代性,c++,c++11,member-function-pointers,C++,C++11,Member Function Pointers,在这个问题中,假设我们以一种友好、谨慎的方式处理了所有指针——为了防止问题膨胀,我不想在这里包含我的清理代码 假设我们有两个类,Foo和Bar,类定义如下: class Foo { public: Foo(); void fooFn(); }; class Bar { public: Bar(); void barFn(); }; 假设Foo和Bar没有继承关系是必要的,但是我们需要调用fooFn和barFn,以
Foo
和Bar
,类定义如下:
class Foo
{
public:
Foo();
void fooFn();
};
class Bar
{
public:
Bar();
void barFn();
};
假设Foo
和Bar
没有继承关系是必要的,但是我们需要调用fooFn
和barFn
,以响应某些刺激。我们可以创建一个带有容器的控制器类,从中调用fooFn
和barFn
,对Foo
和Bar
的特定实例进行调用,例如静态std::vector
,但我们遇到了一个问题:指向Foo
和Bar
实例的成员函数的指针是不同的类型
通过在控制器类中使用静态向量
,我们可以找到一个解决方法Foo
和Bar
实例可以有一个函数,该函数通过lambda函数向向量添加指针,lambda函数捕获此
:
void Foo::registerFnPointer()
{
ControllerClass::function_vector.push_back( new [this](){ return this->fooFn(); } );
}
我已经测试了这个方法,它似乎没有任何问题。这就是说,我担心的问题,可能会造成规避类型差异之前提到。。。我什么都不担心吗?有没有更好的方法来实现等效功能?我看到的唯一问题实际上与函子无关,而是与对象生命周期有关。也就是说:我不知道如何确保每当Foo或Bar实例被销毁时,总是取消注册向ControllerClass注册的函子 然而,您提到您进行了适当的内存管理 在我看来,您不需要存储指向
函数的指针
,只需将函数存储为值(即具有向量
)
在C++11和lambdas之前,为了达到相同的效果,您还将使用(boost)函数,但您将使用boost::bind,其中包含fooFn的地址和绑定到指向Foo对象实例的指针(或引用)的第一个参数
这将创建一个函数实例,该实例保存在给定对象上调用fooFn方法所需的所有信息。然后,您可以将实例存储在一个向量中,以便稍后调用它(同样的问题是确保绑定到已销毁对象的noboost::function
保持注册状态)
编辑:
为完整起见,指向特定于绑定成员的Boost bind文档的链接:
您所做的实际上非常类似,只是现在使用lambda捕获对象指针并定义要调用的函数
因此,我认为您所做的没有问题(除了我已经提到的那一个)。您可以使用适配器类。这对你正在做的事情来说可能是过分的,但它可能会起作用 这样做的好处是:
void Foo::registernPointer()
很难看std::vector
struct Foo
{
void fooFn () {
std::cout << "Foo::fooFn ()" "\n" ;
}
};
struct Bar
{
void barFn () {
std::cout << "Bar::barFn ()" "\n" ;
}
};
struct Adapter_Base
{
virtual ~Adapter_Base () {} ;
virtual void adapterFn () = 0 ;
};
template <typename T>
struct Adapter : Adapter_Base
{
T tVal ;
Adapter (const T &tVal) : tVal (tVal) {}
void adapterFn () ;
};
template <>
void Adapter <Foo>::adapterFn ()
{
tVal.fooFn () ;
}
template <>
void Adapter <Bar>::adapterFn ()
{
tVal.barFn () ;
}
int main ()
{
std::vector <std::unique_ptr <Adapter_Base> > v1 ;
std::unique_ptr <Adapter_Base> u1 (new Adapter <Foo> (Foo ())) ;
std::unique_ptr <Adapter_Base> u2 (new Adapter <Bar> (Bar ())) ;
v1.push_back (std::move (u1)) ;
v1.push_back (std::move (u2)) ;
for (auto &adapter : v1) {
adapter->adapterFn () ;
}
return 0 ;
}
你可以这样使用它:
struct Foo
{
void fooFn () {
std::cout << "Foo::fooFn ()" "\n" ;
}
};
struct Bar
{
void barFn () {
std::cout << "Bar::barFn ()" "\n" ;
}
};
struct Adapter_Base
{
virtual ~Adapter_Base () {} ;
virtual void adapterFn () = 0 ;
};
template <typename T>
struct Adapter : Adapter_Base
{
T tVal ;
Adapter (const T &tVal) : tVal (tVal) {}
void adapterFn () ;
};
template <>
void Adapter <Foo>::adapterFn ()
{
tVal.fooFn () ;
}
template <>
void Adapter <Bar>::adapterFn ()
{
tVal.barFn () ;
}
int main ()
{
std::vector <std::unique_ptr <Adapter_Base> > v1 ;
std::unique_ptr <Adapter_Base> u1 (new Adapter <Foo> (Foo ())) ;
std::unique_ptr <Adapter_Base> u2 (new Adapter <Bar> (Bar ())) ;
v1.push_back (std::move (u1)) ;
v1.push_back (std::move (u2)) ;
for (auto &adapter : v1) {
adapter->adapterFn () ;
}
return 0 ;
}
int main()
{
std::向量v1;
std::unique_ptr u1(新适配器(Foo());
std::unique_ptr u2(新适配器(Bar());
v1.push_back(标准::move(u1));
v1.push_back(标准::移动(u2));
用于(自动和适配器:v1){
适配器->适配器fn();
}
返回0;
}
这些都不是。为什么vector
而不是简单的vector
和ControllerClass::function\u vector.push\u back([this]{return fooFn();})代码>?@您称之为类声明。转发声明将是类Foo代码>。我想你可以说你用正向声明成员函数来声明类,但是对于函数来说更常见的是声明和定义。你也可以用std::bind
。他现在所做的一切还可以。函数实际上执行的是适配器所执行的操作(它保留要调用的实例和函数)。代码要少得多。他实际上是在问他所做的是否可以,是否有更好的方法来完成同样的事情。我不认为你的建议是更好的解决方案。@ds27680在OP的帖子中,他在每个类中添加了一个新的成员函数。这样做,您不必更改原始类。您也不必处理使用静态向量或函数指针的问题,但是适配器并没有解决新成员函数的“问题”。您只需将他在RegisternPointer中编写的代码移动到main。他选择用一种新的课堂方法进行注册(即使有争议),这与他的问题没有多大关系。他本可以编写与您在main中编写的代码相同的代码(我指的是等效代码)。解决静态向量问题也是如此。您将向量移到了main…这只是std::function的一个糟糕的重新实现。我看不出答案中C++11之前部分的要点。问题被标记为C++11,但在我看来,这里唯一的C++11细节是std::function现在是STL和lambda的一部分。他的问题实际上是C++11独立的。。。我还想说,他正在做的事情不是以前做不到的。同时要说的是:旧的做事方式很好(没有问题)。新的方式(你正在做的)实际上几乎是一样的->而且很好。。。同时也描述了实现同样目标的另一种方法。谁知道呢,如果有人不能切换到最新版本的支持C++11的编译器,他可能会在某个时候对此表示感谢。