怀疑c ++中类中的typedef函数

时间:2014-09-20 20:27:53

标签: c++ function visual-c++ typedef

我需要定义一个返回类对象的函数。

假设我有一个名为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函数的地方。还有其他地方可以定义这个功能吗?如果没有这个问题的解决方案,或者至少是它的替代方案。

由于

3 个答案:

答案 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.

请注意,我(不需要)更改了回调的签名,以接受constDog的引用,而不是指针。这通常被认为是更好的风格,但如果你需要指针,无论出于什么原因我看不到,你也可以这样做。

注意:如果您决定从析构函数中调用回调,请注意有一些不寻常的事情需要注意:

  • 在析构函数中尽早调用回调。如果您的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)

哟走错路。每只狗都需要存储对经理的引用,并且应该将它的死亡报告给狗的析构函数中的经理。当经理本身被摧毁并且一些狗仍然存在时,问题就出现了,因此引用变得无效。这可以使用静态管理器实例或单线程序(在创建第一个狗时自动创建的唯一DogManager实例)来解决。