Call a virtual function from the constructor

I read Effective C ++ , and there is "Point 9: Never Call Virtual Functions During Construction or Destruction." And I am wondering if my code is good, even if it violates this rule:

using namespace std; class A{ public: A(bool doLog){ if(doLog) log(); } virtual void log(){ cout << "logging A\n"; } }; class B: public A{ public: B(bool doLog) : A(false){ if(doLog) log(); } virtual void log(){ cout << "logging B\n"; } }; int main() { A a(true); B b(true); } 

Is there something wrong with this approach? Can I get in trouble when I do something more complicated?

It seems to me that most of the answers did not get what I did there, and they just explained why you are calling a virtual function from a constructor that is potentially dangerous.

I would like to emphasize that the output of my program is as follows:

 logging A logging B 

This way I get the log when it is constructed, and B is logged when it is created. And this is what I want ! But I ask if you find something wrong (potentially dangerous) with my “hack” to overcome the problem of calling a virtual function in the constructor.

+6
source share
3 answers

And I am wondering if my code is good, even if it violates this rule:

It depends on what you mean by "fine." Your program is well-formed, and its behavior is clearly defined, so it will not cause undefined behavior and the like.

However, when viewing a call to a virtual function, you can expect that the call is resolved by calling the implementation provided by the most derived type that overrides this function.

Except that during construction, the corresponding sub-object has not yet been built, so the most derived subobject is the one that is currently being built. Result: the call is sent as if the function were not virtual.

This is against intuition, and your program should not rely on this behavior. Therefore, as a competent programmer, you should get used to avoiding such a pattern and following the guidance of Scott Meyer.

+11
source

Is there something wrong with this approach?

From Bjarne Stroustrup:

Is it possible to call a virtual function from the constructor?

Yes, but be careful. He may not do what you expect. In the constructor, the virtual call mechanism is disabled because overriding from the derived classes has not yet occurred. Objects are built from the base, "base to derivative." Consider:

  #include<string> #include<iostream> using namespace std; class B { public: B(const string& ss) { cout << "B constructor\n"; f(ss); } virtual void f(const string&) { cout << "B::f\n";} }; class D : public B { public: D(const string & ss) :B(ss) { cout << "D constructor\n";} void f(const string& ss) { cout << "D::f\n"; s = ss; } private: string s; }; int main() { D d("Hello"); } 

the program compiles and produces

 B constructor B::f D constructor 

Note not D :: f. Think about what would happen if the rule was different so that D :: f () was called from B :: B (): since the constructor of D :: D () had not yet been run, D :: f () try to assign its argument uninitialized string s. The result is likely to be an immediate accident. Destruction is performed by the “derived class before the base class”, therefore virtual functions behave the same as in the constructors: only local definitions are used - and calls are not called to redefine the functions so as not to touch the (now destroyed) derived part of the object class.

See D & E 13.2.4.2 or TC ++ PL3 15.4.3 for details.

It has been suggested that this rule is an implementation artifact. This is not true. In fact, it would be much easier to implement the unsafe rule for invoking virtual functions from constructors in the same way as from other functions. However, this means that no virtual function can be written to rely on the invariants established by the base classes. That would be a terrible mess.

+15
source

This is "excellent" in terms of clarity. It may not be “excellent” in the sense of what you expect.

You are invoking an override from the class that is currently being built (or destroyed), and not the final override; since the final derived class has not yet been built (or has already been destroyed) and therefore cannot be obtained. Thus, you may have problems if you want the final override to be called here.

Since this behavior is potentially confusing, it is best to avoid this. I would recommend adding behavior to a class by aggregation rather than a subclass in this situation; class members are built up to the constructor body and stored until the destructor and are therefore available in both of these places.

One thing you should not do is call a virtual function from the constructor or destructor if it is pure virtual in this class; that behavior is undefined.

+4
source

Source: https://habr.com/ru/post/945701/


All Articles