Is there any automated way to implement post-const

2019-01-18 12:49发布

Due to the well-known issues with calling virtual methods from inside constructors and destructors, I commonly end up with classes that need a final-setup method to be called just after their constructor, and a pre-teardown method to be called just before their destructor, like this:

MyObject * obj = new MyObject;
obj->Initialize();   // virtual method call, required after ctor for (obj) to run properly
[...]
obj->AboutToDelete();  // virtual method call, required before dtor for (obj) to clean up properly
delete obj;

This works, but it carries with it the risk that the caller will forget to call either or both of those methods at the appropriate times.

So the question is: Is there any way in C++ to get those methods to be called automatically, so the caller doesn't have to remember to do call them? (I'm guessing there isn't, but I thought I'd ask anyway just in case there is some clever way to do it)

9条回答
Fickle 薄情
2楼-- · 2019-01-18 13:23

I used a very carefully designed Create() factory method (static member of each class) to call a constructor and initializer pair in the same order as C# initializes types. It returned a shared_ptr to an instance of the type, guaranteeing a heap allocation. It proved reliable and consistent over time.

The trick: I generated my C++ class declarations from XML...

查看更多
相关推荐>>
3楼-- · 2019-01-18 13:23

http://www.research.att.com/~bs/wrapper.pdf This paper from Stroustrup will solve your problem.

I tested this under VS 2008 and on UBUNTU against g++ compiler. It worked fine.

#include <iostream>

using namespace std;

template<class T>

class Wrap
{
    typedef int (T::*Method)();
    T* p;
    Method _m;
public:
    Wrap(T*pp, Method m): p(pp), _m(m)  { (p->*_m)(); }
    ~Wrap() { delete p; }
};

class X
{
public:
    typedef int (*Method)();
    virtual int suffix()
    {
        cout << "X::suffix\n";
        return 1;
    }

    virtual void prefix()
    {
        cout << "X::prefix\n"; 
    }

    X() {  cout << "X created\n"; }

    virtual ~X() { prefix(); cout << "X destroyed\n"; }

};

class Y : public X
{
public:
    Y() : X() { cout << "Y created\n"; }
    ~Y() { prefix(); cout << "Y destroyed\n"; }
    void prefix()
    {
        cout << "Y::prefix\n"; 
    }

    int suffix()
    {
        cout << "Y::suffix\n";
        return  1;
    }
};

int main()
{
    Wrap<X> xx(new X, &X::suffix);
    Wrap<X>yy(new Y, &X::suffix);
}
查看更多
The star\"
4楼-- · 2019-01-18 13:27

Haven't seen the answer yet, but base classes are only one way to add code in a class hierarchy. You can also create classes designed to be added to the other side of the hierarchy:

template<typename Base> 
class Derived : public Base {
    // You'd need C++0x to solve the forwarding problem correctly.
    Derived() : Base() {
        Initialize();
    }
    template<typename T>
    Derived(T const& t): Base(t) {
        Initialize();
    }
    //etc
private:
    Initialize();
};
查看更多
欢心
5楼-- · 2019-01-18 13:28

You can use static function template in the class. With private ctor/dtor. Run on vs2015 community

class A {
    protected:
    A() {}
        virtual ~A() {}
        virtual void onNew() = 0;
        virtual void onDelete() = 0;
    public:

        void destroy() {
            onDelete();
            delete this;
        }

        template <class T> static T* create() {
            static_assert(std::is_base_of<A, T>::value, "T must be a descendant of A");
            T* t = new T();
            t->onNew();
            return t;
        }
   };

class B: public A {
     friend A;

     protected:
          B() {}
          virtual ~B() {}

          virtual void onNew() override {
          }

          virtual void onDelete() override {
          }
};

int main() {
    B* b;
    b = A::create<B>();
    b->destroy();
}
查看更多
劳资没心,怎么记你
6楼-- · 2019-01-18 13:32

The best I can think of is for you to implement your own smart pointer with a static Create method that news up an instance and calls Initialize, and in its destructor calls AboutToDelete and then delete.

查看更多
放荡不羁爱自由
7楼-- · 2019-01-18 13:34

The main problem with adding post-constructors to C++ is that nobody has yet established how to deal with post-post-constructors, post-post-post-constructors, etc.

The underlying theory is that objects have invariants. This invariant is established by the constructor. Once it has been established, methods of that class can be called. With the introduction of designs that would require post-constructors, you are introducing situations in which class invariants do not become established once the constructor has run. Therefore, it would be equally unsafe to allow calls to virtual functions from post-constructors, and you immediately lose the one apparent benefit they seemed to have.

As your example shows (probably without you realizing), they're not needed:

MyObject * obj = new MyObject;
obj->Initialize();   // virtual method call, required after ctor for (obj) to run properly

obj->AboutToDelete();  // virtual method call, required before dtor for (obj) to clean up properly
delete obj;

Let's show why these methods are not needed. These two calls can invoke virtual functions from MyObject or one of its bases. However, MyObject::MyObject() can safely call those functions too. There is nothing that happens after MyObject::MyObject() returns which would make obj->Initialize() safe. So either obj->Initialize() is wrong or its call can be moved to MyObject::MyObject(). The same logic applies in reverse to obj->AboutToDelete(). The most derived destructor will run first and it can still call all virtual functions, including AboutToDelete().

查看更多
登录 后发表回答