我需要定义一个返回类对象的函数。
假设我有一个名为dog的类(.h中的类声明和.cpp中的实现),当狗死了,我想调用一个函数让狗经理知道。在dog`s SetFunc中,我将收到要调用的函数:
Dog.h
class Dog
{
public:
typedef void (*OndogDie)(Dog * dog);
OndogDie function;
void SetFunc(OndogDie func);
void Die();
};
Dog.cpp
void Dog::SetFunc(Dog::OndogDie func)
{
function = func;
}
void Dog::Die()
{
function(this);
}
如果我把typedef放在Dog类中,everthing工作,除了Manager类,它不能作为参数传递给dog类的合适回调:
Manager.h
#include "Dog.h"
class Manager
{
public:
Manager();
void CbdogDie(Dog * dog);
};
Manager.cpp
void Manager::CbdogDie(Dog * d)
{
//stuff
}
Manager::Manager()
{
Dog d = Dog();
d.SetFunc(CbdogDie); (Error: argument of type void(Manager::*)(Dog *d) is incompatible with parameter of type "Dog::OndogDie")
}
我已经查看了许多教程,并回答了问题,但没有一个能帮助我找出我的代码有什么问题。我也考虑过使用委托类型,但它只适用于clr项目。
我认为问题可能出在我放置typedef函数的地方。还有其他地方可以定义这个功能吗?如果没有这个问题的解决方案,或者至少是它的替代方案。
由于
答案 0 :(得分:2)
为方便更多读者而重复我的评论:CbdogDie
(在您的代码中)是一个函数成员(非静态)并且需要调用Manager
的实例,因此其类型不兼容回调typedef
。
与您的错误无关,但仍值得考虑:您是否不应该将调用回调的逻辑移动到Dog
的析构函数中?在C ++中(例如,与Java不同),我们知道对象何时被破坏。
不确定您是否可以将其应用于您的实际问题,但以下是一些有用的示例。
#include <iostream>
#include <string>
class Dog
{
public:
typedef void (* on_dog_die_callback_t)(const Dog&);
Dog(const std::string& name, on_dog_die_callback_t callback) :
name_(name), callback_(callback)
{
std::clog << "A new dog named " << this->getName() << " is born.\n";
}
~Dog()
{
std::clog << "A dog named " << this->getName() << " has died.\n";
if (this->callback_) // allow NULL pointer for no-op
this->callback_(*this);
}
std::string
getName() const
{
return this->name_;
}
private:
std::string name_;
on_dog_die_callback_t callback_;
};
// Note: This is a free function, not a class member.
void
friendly_callback(const Dog& dog)
{
std::clog << "We are so sorry that " << dog.getName() << " died.\n";
}
struct Manager
{
// Note: This is a static function.
static void
unfriendly_callback(const Dog& dog)
{
std::clog << "Haha, " << dog.getName() << " died.\n";
}
};
int
main()
{
Dog fido("Fido", friendly_callback);
Dog leika("Leika", Manager::unfriendly_callback);
Dog waldo("Waldo", 0);
}
运行此代码的输出是:
A new dog named Fido is born.
A new dog named Leika is born.
A new dog named Waldo is born.
A dog named Waldo has died.
A dog named Leika has died.
Haha, Leika died.
A dog named Fido has died.
We are so sorry that Fido died.
请注意,我(不需要)更改了回调的签名,以接受const
对Dog
的引用,而不是指针。这通常被认为是更好的风格,但如果你需要指针,无论出于什么原因我看不到,你也可以这样做。
注意:如果您决定从析构函数中调用回调,请注意有一些不寻常的事情需要注意:
Dog
类拥有在调用回调之前在destuructor中释放的资源,您将传递一个指向部分被破坏对象的this
指针,并且调用其上的任何成员函数可能会调用undefined行为。virtual
的任何Dog
函数成员。请注意,Dog
的析构函数只会在派生类(如果有)的析构函数之后被称为,因此它可能会处理已经部分被破坏的对象。只要回调只对Dog
基类感兴趣,这很好,但如果它试图访问派生类的任何成员(通过virtual
函数调用),将导致未定义的行为。< / LI>
好消息:如果你的课很简单(没有virtual
函数,没有在析构函数中发布明确的资源),你就可以了。
答案 1 :(得分:1)
您已将function
定义为(非成员)函数的指针。如果在任何类之外创建一个虚函数,则可以轻松验证这一点:
void gondie(Dog * dog) {}
如果您将Manager()
更改为:
d.SetFunc(gondie); // compile
但是Manager::CbdogDie()
是类管理器的成员函数。要调用它,你需要知道要使用哪个管理器,而不是Dog:Die():你只知道函数,而不是对象。
修改:其他建议:
你已经为回调方法提供了一个非常好的解决方案,但对于记录,我建议你另外一个设计稍有变化,并在狗中引用它的经理。
由于在Dog标头中尚未知道Manager,我们将使用适配器类DogManager
,其唯一目标是提供垂死的回调函数:
class Dog
{
public:
class DogManager { // adapter
public:
void CbdogDie(Dog *d) {} // empty function to call back. Will be overloaded later
};
private:
DogManager *mymanager; // Who is the master of the dog ?
public:
... // remaining of your dog definition
void SetManager(DogManager *m);
};
Dog的实现会略有改变:
void Dog::Die()
{
mymanager->CbdogDie(this); // call directly manager function instead of callback
}
最后要做的是让你的Manager
班级继承DogManager
:
class Manager : Dog::DogManager
...
当然会改变其实施方式,通过将d.SetFunc(...)
替换为d.SetManager(this)
来告诉狗;
答案 2 :(得分:0)