سؤال

I have trouble thinking outside the box with the following issue. I have a class hierarchy:

[BaseClass] --> [Win32Class]

[BaseClass] --> [LinuxClass]

[BaseClass] --> [VxWorksClass]

The implementation classes will make calls to API-level functions. These are done inside pure virtual functions from the base class.

Now, when the user creates, uses and is finished with the object, he should call the Done function (from base class), which will do cleanup and resource de-allocation. Part of this cleanup is to call a number of API-level functions -- these naturally are pure virtuals in the base class and implemented in the derived classes. So far so good.

Here's the problem. If the user does not explicitly call Done there will be various memory leaks due to resources not being properly freed. So, I figured I'd make it easy and call Done from ~BaseClass() - automatic cleanup on destruction, i thought. Well, not so much. Since Done makes calls to these pure virtuals all hell breaks loose.

Any thoughts on how to re-design this to avoid this issue?

example code

class BaseClass{ 
  virtual ~BaseClass(){
    Done();
  }
  void Done(){
    // A bunch of OS-independent clean-up logic
    Cleanup();
    // some more OS-independent clean-up logic
  }
  virtual void Cleanup() = 0;
};
class Win32Class : public BaseClass{
  virtual void Cleanup(){
    // call some Win32-specific cleanup code
  }
};
class LinuxClass : public BaseClass{
  virtual void Cleanup(){
    // call some Linux-specific cleanup code
  }
};

==========================================
Here's my solution. Use a wrapper class. Don;t call Done in the destructor of Win32Class or BaseClass

class Win32Wrapper{
public:
  Win32Class* object_;
public:
  Win32Wrapper(){
    this->object_ = new Win32Class;
  }
  ~Win32Wrapper(){
    this->object_->Done();
    delete this->object_;
  }
};

== How to Call Pure Virtuals in a destructor ==

class Base{
public:
  Base(){
  }
  virtual ~Base(){
    Done();
  }
  void Done(){
    Clean();
  }
  virtual void Clean() = 0;
};

class Derived : public Base{
public:  
  Derived(){
  }
  ~Derived(){
  }
  virtual void Clean(){
  }
};

The user thinks that the program will work, because the compiler doesn't complain about the call to a PFV in Base::Done().

هل كانت مفيدة؟

المحلول

You can put the clean-up code in the derived destructors:

struct Base
{
    virtual ~Base() { }

    void CleanUp() { /* ... */ }
};

struct Derived : Base
{
    virtual ~Derived()
    {
        CleanUp();
    }
};

If you want, you can even put calls to further virtuals into the non-virtual Base::CleanUp() function to allow for derived-specific behaviour (though by your description it sounds like you don't need that).

نصائح أخرى

I think the RAII idiom is what you are looking for.

You can find a quite comprehensive description of this here: http://www.hackcraft.net/raii/

You have two options.

  1. The user of BaseClass has to call Done(), and any bugs due to not doing it are his responsibility.
  2. Each of the derived classes performs whatever cleanup is needed in their destroctors. They can use BaseClass functions in their cleanup, and the BaseClass functions can even call functions that are overridden by the derived class.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top