问题
on more than one occasion I felt the need to define class methods that get called in a manner similar to constructors or destructors.
A specific example would be; in a program I had a very complex network of nodes of different types that depended mutually on each other in a very irregular fashion (The network did not resemble a tree at all). When a node needed to be destroyed, it started a complex chain of destructions in the network. Much like a spider web being torn apart, but more complex.
During the execution of this chain, the control came back to the methods of the initiator (or one of the intermediate elements in the chain), so that the actual destruction had to take place when the chain had settled, and that's why I couldn't use destructors for this purpose. However, along the class hierarchy of my nodes, I needed a "destructor like", i.e. a ladder way of calling my non-destructing pre-destruct function (for exactly the same reasons why an actual destructor is also called that way, namely, every step in the class hierarchy needed to contribute to the chain in a different way).
I ended up coding the ladder by hand. Namely, the class nodeBase has a method called "preDestroyNodeBase" which does its job and calls the virtual method "preDestroyNode" and so on until the leaf (I know, this way it looks like a constructor, but it was -comparatively- more elegant that way, since you can just call the "preDestroy" of the most base class).
You can imagine how error prone this approach is, not to mention ugly. Is there a cleaner way of emulating constructor or destructor way of calling methods? Some kind of template magic or even macro magic! Because hand coding it is too error-prone, even for a single programmer, so I cannot imagine exposing this kind of behavior to the clients of a library.
Maybe I'm missing a fundamental programming concept that obsoletes the need for such functions. If that is the case, I'd be glad if you pointed how else that network of nodes example could be handled.
Thanks a lot!
回答1:
When you declare a virtual function it will call to the most-derived handler of the function, and then from there you have each handler call NextMostDerivedClass::preDestroy and the calls will go to the next-most-derived handler of the function, and you can again call NextMostDerivedClass::preDestroy, and so on. This is the same as the path a virtual destructor takes, except that you don't have to call anything manually with destructors, it's automated. If you were to put the cout statements from the below code sample into your destructors, you could see the same output as the sample below provides.
#include <iostream.h>
class Foo
{
public:
virtual void PreDestroy()
{
cout << "Foo preDestroy";
}
}
class Bar : public Foo
{
public:
void PreDestroy()
{
cout << "Bar preDestroy\n\n";
Foo::PreDestroy();
}
}
class MostDerived : public Bar
{
public:
void PreDestroy()
{
cout << "MostDerived preDestroy\n\n";
Bar::PreDestroy();
}
}
int main()
{
MostDerived testObj;
testObj.PreDestroy();
}
Output should be:
MostDerived preDestroy
Bar preDestroy
Foo preDestroy
回答2:
You may make use of the visitor pattern to go through your nodes graph (I assume you are talking about a graph) and let the nodes put themselves into a kind of "to-be-deleted" list (if needed) which is hold by the visitor object. In a second iteration you can go through that list elements and call a kind of "destroy" method on them which in turn does the final cleanup. This approach needs to have all your nodes supporting the visitor pattern and containing that "destroy" method.
回答3:
I'd use a subscription mechanism, where if you want a to hold a pointer to another object, you also have to subscribe to their destruction event; on receiving such an event, cancel their subscription to your own event, and clean up your own structures (which may then lead to further destruction).
i.e. (C++1x syntax for brevity)
struct node {
~node() { for(auto i : subscribers) i->notify(this); }
void subscribe(node *n) { subscribers.push_back(n); }
void notify(node *n) { subscribers.remove(n); /* Handle other node being removed */ }
private:
std::list<node *> subscribers;
}
To get access to the node's data members, you need to make them sub-objects of the node class, either by extending the node class itself, or by using a templated node class that inherits from the class actually containing the data.
来源:https://stackoverflow.com/questions/8927648/calling-overriden-class-methods-as-a-chain-in-c