0

Question: Are there special precautions needed, when passing around instance member functions?

Scenario: Sporadic crashes when I pass around and call an instance member function.

  1. The member function is called constantly (hundreds of thousands of times) via the function pointer.
  2. The process eventually crashes, but the stack trace is somewhat inconsistent. The only common denominator seems to be that new/malloc fails, even though there's plenty of process memory available.
  3. To the best of my knowledge the instance (CClass in the example below) is still alive.
  4. The member function can have an empty body; simply trying to call that method via the function pointer is enough to corrupt the heap (assuming it's heap corruption).
  5. Single-threaded, single-process.
  6. This is visual studio 6.0 on XP.

Note: To side-step the issue, I made CFunctor a friend of CClass and give it access CClass members; but I'd like to know if my original approach was flawed from the get-go.

A simplified example:

class CFunctor
{
private:
    typedef void (CClass::*Calculator)(args);

    CClass *c;
    Calculator func;

public:
    CFunctor(CClass* c, Calculator func)
    {
        this->c = c;
        this->func = func;
    };
    ~CFunctor()
    {
        this->c = NULL;
        this->func = NULL;
    }

    virtual void calculate(args)
    {
        (c->*func)(args);
    }
};

class CFunctorConsumer
{
public:
    CFunctorConsumer(CFunctor *functor)
    {
        this->functor = functor;
    }
    ~CFunctorConsumer()
    {
        this->functor = NULL;
    }

    virtual void DoStuff(args)
    {
       if (this->functor)
       {
           this->functor->calculate(args);
       }
    }

private:
    CFunctor *functor;
}

class CClass
{
private:
    CFunctorConsumer* testFunctor;

    void CalculationMethod(args)
    {
       // This method can be empty, and still crashes
    }

public:
    CClass()
    {
        this->testFunctor = new CFunctorConsumer(new CFunctor(this, this->CalculationMethod));
    }
    void Test()
    {
       this->testFunctor->DoStuff( args );
    }   
}
hythlodayr
  • 2,377
  • 15
  • 23
  • Have you tried to run it in debugger? It should show you where the program stops. If you're not sure about CClass object existence/consistency then add a destructor and set a trap in it (either debugger's breakpoint or some code to reset a global flag/counter for CClass objects). You may also add some member at the beginning of an object and initialize it with a specific pattern, then check it in `CalculationMethod`. This will not prevent the crash but it can give come clue about the crash reason... – CiaPan Mar 12 '14 at 20:48

2 Answers2

0

I see two CFunctor constructors here, one of which should probably be a destructor but it has no tilde in front of it...

Method CFunctor::calculate is declared virtual, so you probably use some derived class MyFunctor, not the CFunctor itself? Possibly you forgot to pass appropriate data from MyFunctor constructor to the base class CFunctor constructor. And the default constructor initializes base members to NULL, which causes memory access error in calculate(args).

CiaPan
  • 9,381
  • 2
  • 21
  • 35
0

You mention memory issues and in the sample you provide your classes allocate memory without ever destroying it.

You seem to be assigning NULL to your members in the destructor. This is useless. In c++ deallocate what you new with delete, or use smart pointer when possible. Assigning to NULL only changes the pointer but does not free anything.

Additionnally, if you have non-default copy-constructor/destructor/copy-assignment, you most likely need all three.

Community
  • 1
  • 1
François Moisan
  • 790
  • 6
  • 12
  • Fair enough on the deallocation & gratuitous destructor code. So provided I'm managing instance lifetimes correctly (i.e., no bug in my own code), there's nothing inherently unsafe about passing around an instance member function and calling it? I haven't touched C/C++ in a long, long time. – hythlodayr Mar 12 '14 at 00:25