The Artima Developer Community
Sponsored Link

Articles Forum
Never Call Virtual Functions during Construction or Destruction

12 replies on 1 page. Most recent reply: Apr 29, 2010 1:25 AM by Claudiu Sav

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 12 replies on 1 page
Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

Never Call Virtual Functions during Construction or Destruction Posted: Jun 6, 2005 9:00 PM
Reply to this message Reply
Advertisement
The third edition of Scott Meyers' popular book, Effective C++, was recently published. In this excerpt from the new edition, Meyers explains why you should never call virtual functions during object construction and destruction.

http://www.artima.com/cppsource/nevercall.html

What do you think of the way in which C++ treats virtual calls in constructors and destructors? Java, by contrast, doesn't do anything special with virtual calls during construction. Although it seems C++ would be safer because of this special treatment, Scott Meyers suggests you avoid virtual calls anyway. What's your opinion?


Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: Never Call Virtual Functions during Construction or Destruction Posted: Jun 8, 2005 12:22 AM
Reply to this message Reply
> What do you think of the way in which C++ treats virtual
> calls in constructors and destructors? Java, by contrast,
> doesn't do anything special with virtual calls during
> construction. Although it seems C++ would be safer because
> of this special treatment, Scott Meyers suggests you avoid
> virtual calls anyway. What's your opinion?

I don't know what Java does, but I think C# does something similar, i.e. calls to a virtual in a constructor call the most derived version.

IMO C++ does the right thing here, C# (and Java?) don't. The danger is in the data members, as Scott highlights.

Taking Scott's example and trivialising it :-)

namespace VirtualConstruction
{
	public abstract class Base
	{
		public Base() 
		{
		}
		public abstract void Log();
	}
 
	public class Derived : Base
	{
		public Derived()
		{
			buf = new StringBuilder();
			Log();
		}
		public override void Log()
		{
			buf.Append( "Derived" );
			Console.WriteLine( buf.ToString() );
		}
		private StringBuilder buf;
	}
 
	public class MoreDerived : Derived
	{
		public MoreDerived()
		{
			buf = new StringBuilder();
			Log();
		}
		public override void Log()
		{
			buf.Append( "MoreDerived" );
			Console.WriteLine( buf.ToString() );
		}
		private StringBuilder buf;
	}
 
	class VirtualTester
	{
		static void Main(string[] args)
		{
			Base d = new Derived();
			Base md = new MoreDerived();
		}
	}
}


This is C#, and it crashes when MoreDerived is created. Note that in all cases, the StringBuilder object is created before Log() is called. Except it isn't because the base class CTR gets called before that, and in turn tries to call the more derived virtual function.

Can anyone indicate what Java does with the equivalent code?

Steve

Harrison Ainsworth

Posts: 57
Nickname: hxa7241
Registered: Apr, 2005

java virtual methods Posted: Jun 8, 2005 5:48 AM
Reply to this message Reply
In Java, virtual methods do just work in their normal way in constructors (as Mr Meyers implied). But any member variables used wouldn't be fully initialized.

Object construction in Java has an extra stage, compared to C++: member variable storage is zeroed before any constructors are executed. So those zero values are what a virtual method would encounter, since its corresponding constructor won't have been called yet.

You could say Java has default construction.

A Java version of Mr Love's code would throw a null-object-reference exception when attempting to do the buf.Append in MoreDerived::Log.

Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: java virtual methods Posted: Jun 8, 2005 5:59 AM
Reply to this message Reply
> In Java, virtual methods do just work in their normal way
> in constructors (as Mr Meyers implied). But any member
> variables used wouldn't be fully initialized.

Which is the problem - in C++, C# and Java, then. This is the heart of why I think C++ is right, C# and Java wrong in this regard.

Of course, we can argue the fine points of whether (possibly) throwing an exception, or calling the "wrong" member is more, er, surprising :-)

> You could say Java has default construction.

Yes C# does too.

> A Java version of Mr Love's code would throw a
> null-object-reference exception when attempting to do the
> buf.Append in MoreDerived::Log.

Which is what the C# code does. The buf variable gets init'ed to null.

I found this in the MSDN C# Language Spec (10.10.1, Constructor Initializers):

<paraphrase>

An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple-name.

</paraphrase>

Surely having a base class constructor (which is part of the initialiser list for the derived class) call a virtual method is exactly the same as an initailiser list referencing "this"?

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: java virtual methods Posted: Jun 11, 2005 6:50 AM
Reply to this message Reply
> > In Java, virtual methods do just work in their normal
> way
> > in constructors (as Mr Meyers implied). But any member
> > variables used wouldn't be fully initialized.
>
> Which is the problem - in C++, C# and Java, then. This is
> the heart of why I think C++ is right, C# and Java wrong
> in this regard.

In Java, there is the notation, "<ClassName>.this." that can be used to qualify an inner class reference to an outer class instance.

Unfortunately, this notation is not defined for class hierarchy references related to inheritance. If it was, then you'd be able to code
class Derived extends Base {
     public Derived() {
         buf = new StringBuffer();
         Derived.this.Log();
     }
     public void Log() {
         buf.append( "Derived");
         System.out.println(buf);
     }
}

and get the desired behavior. The flexibility of explicitly being able to designate the desired behavior would be a plus for me.

However, once you know the rules, Java's and C#'s behaviors are not nearly destructive as C++ would be if it acted like Java and C#. C++'s behavior is an acknowledgement of the executing environment and the hazards of a NULL reference where there is no language protection from the side effects of such, as Java and C# provide.

Daniel Heck

Posts: 1
Nickname: boone
Registered: Jun, 2005

Re: Never Call Virtual Functions during Construction or Destruction Posted: Jun 22, 2005 12:56 AM
Reply to this message Reply
Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.

I usually phrase this as Virtual functions aren't virtual during construction and destruction which is much easier to understand and memorize.

Loren Meck

Posts: 1
Nickname: freon
Registered: May, 2006

Re: Never Call Virtual Functions during Construction or Destruction Posted: May 24, 2006 1:43 PM
Reply to this message Reply
The C++ language design decision to make virtual functions non-virtual when called from ctors/dtors is a mistake that simply leads to incorrect program behavior that is difficult to debug. It would be better for C++ to prohibit calls to virtual functions from ctors/dtors and enforce this behavior with a runtime error. The programmer can easily find the source of the error and correct it by creating a non-virtual function that is called from both the virtual function and the ctor/dtor, if the intent is to achieve the existing behavior. I think that the current C++ design was simply easier to implement; my proposal would probably require initializing the vtable to zeros before construction and not adding any function addresses until after all ctors have been called.

Damian Powell

Posts: 1
Nickname: emq
Registered: Jun, 2006

Re: Never Call Virtual Functions during Construction or Destruction Posted: Jun 15, 2006 5:04 AM
Reply to this message Reply
All of these languages implement the virtual functions from the constructor issue in the correct way given the context of the language. It all depends on how each language initialises its data members.

For C# and Java, an object's data members are always initialised - either explicitly by the user or implicitly (to zero) by the runtime. Therefore, calling virtual methods will always have predictable results even if they're not exactly what the programmer expects in some cases.

On the other hand, the data members in a C++ object are *not necessarily* initialised until *all* of the constructors in the class hierarchy have returned. Calling virtual methods that access data members from derived classes will therefore have unpredictable results and that is why it is not supported in C++.

Rexford Gibbs

Posts: 1
Nickname: herk
Registered: Aug, 2006

Re: Never Call Virtual Functions during Construction or Destruction Posted: Aug 12, 2006 9:49 AM
Reply to this message Reply
Calling virtual functions from constructors works for me.
(Accessing data is a separate issue!)

// Re: http://www.artima.com/cppsource/nevercall.html
// "Never call virtual functions during construction / destruction"
//

#include <iostream>

using std::cout;
using std::endl;

class Base1
{
public:
Base1(){ cout << "Constructor: Base1 calling virtual Base1::vf1()..." <<endl; vf1(); }
virtual ~Base1(){ cout << "Destructor : Base1" << endl;}
virtual void vf1 () { cout << "virtual Base1::vf1 called!" << endl; }
virtual void vf2 () = 0;
};
class Derived1 : public Base1
{
public:
Derived1() { cout << "Constructor: Derived1 calling virtual Derived1::vf1()..." <<endl; vf1();
cout << "Constructor: Derived1 calling virtual Derived1::vf2()..." <<endl; vf2(); }
~Derived1(){ cout << "Destructor : Derived1" << endl;}
virtual void vf1 () {cout << "virtual Derived1::vf1 called!" << endl;}
virtual void vf2 () {cout << "virtual Derived1::vf2 called!" << endl;}
};

void main ()
{
bool use_base_class_ptr = false;

if (use_base_class_ptr)
{
Base1 *pb1 = new Derived1;
delete pb1;
/** Produces the following output: **/
// Constructor: Base1 calling virtual Base1::vf2()...
// virtual Base1::vf2 called!
// Constructor: Derived1 calling virtual Derived1::vf1()...
// virtual Derived1::vf1 called!
// Constructor: Derived1 calling virtual Derived1::vf2()...
// virtual Derived1::vf2 called!
// Destructor : Derived1
// Destructor : Base1
// Press any key to continue
}
else if (!use_base_class_ptr)
{
Derived1 d1;
/** Produces the following output: **/
// Constructor: Base1 calling virtual Base1::vf1()...
// virtual Base1::vf1 called!
// Constructor: Derived1 calling virtual Derived1::vf1()...
// virtual Derived1::vf1 called!
// Constructor: Derived1 calling virtual Derived1::vf2()...
// virtual Derived1::vf2 called!
// Destructor : Derived1
// Destructor : Base1
// Press any key to continue
}
}

Anjaline Roby

Posts: 1
Nickname: ajlegz
Registered: Oct, 2006

Re: Never Call Virtual Functions during Construction or Destruction Posted: Oct 22, 2006 10:05 PM
Reply to this message Reply
Hi Herk. I googled your name to find you because our family has lost track of you! Barry and Marianne Roby, their daughters Anjaline and Sharlae are wondering where you are!! Anjaline is getting married, is 24, and Sharlae, 21 is having a baby. We'd like you to share our news!! I, Anjaline, would like for you to attend my wedding! I hope you are doing well. This is the only way I could think of to find you. Please email me if you get this. aj_legz@yahoo.com We hope to hear from you!!

Rodolfo Federico Gamarra

Posts: 7
Nickname: rgamarra79
Registered: Sep, 2008

Boost’s shared_ptr trick and copying. Posted: Jul 12, 2009 10:59 AM
Reply to this message Reply
Hi,

I have been understanding that nice trick and trying to use judiciously. Some doubts came to me while thinking about the copiability of the (root) objects implementing it.

To make this post short, I'll write directly the following observations (hope I'm clear and not making any mistakes). To keep this clear I'll use the typenames Scott used formerly; tough I fear confusion because, I think, that some of the issues are solved with the specific use of a class like Shared_ptr (ie a reference counting pointer)!

- In Shared_ptr there's a pointer that needs to be managed (ie deleted in due time).
- At first, it seems reasonable to have Shared_ptr own it.
- So, on destruction Shared_ptr should delete the pointer accordingly.

So, what if we want to copy these Shared_ptr objects?

- In the copy constructor the actual pointed-to type isn't available: so we cannot just allocate another sp_counted_impl_p (or _pd)

What I did was to add another virtual function in sp_counted_base:


virtual sp_counter_base* Clone() const = 0;


and so as to make a call to this function while copy-constructing.

Is this tha-way-to-go?

At last, my original concern came from this:
- I created my Shared_ptr-like objects with a factory function and returned them by-value.
- Due to Named Return Value Optimization hardly copies will be made.
- Nevertheless, one cannot just "privatize" the copy constructor. So copies will be still allowed to be made, so I thought about providing a good copy; I mean, I ruled out the choice of zeroing the pointer in the source object of a copy.

Thanks a lot for your comments.

Rodolfo.

bug not

Posts: 41
Nickname: bugmenot
Registered: Jul, 2004

Re: Never Call Virtual Functions during Construction or Destruction Posted: Oct 21, 2009 4:02 PM
Reply to this message Reply
If you want to call virtual functions in a constructor in C++, it might be useful to explicitly scope the function call with the class name. e.g.:
class A
{
  public:
    A()
    {
        A::func();
    }
    virtual void func() { }
};

Even though the code will run the same with or without the scope, this will help future readers of the code to see that A's version of func will always be called, regardless of the type of end-object being constructed.

Claudiu Sav

Posts: 1
Nickname: claws
Registered: Apr, 2010

Re: Never Call Virtual Functions during Construction or Destruction Posted: Apr 29, 2010 1:25 AM
Reply to this message Reply
I have some problems with the Scott Meyers's example. The example (if my interpretation below is correct) does not compile at all!!! In MS VS 2005 I have the following link error:
error LNK2001: unresolved external symbol "public: virtual void __thiscall Transaction::logTransaction(void)const " (?logTransaction@Transaction@@UBEXXZ)

If I didn't make any mistake it turns aout that somebody forgot the first lesson is teaching: Test your code!

And why he didn't call the log from the derive class constructor in order to have the expected result?

class Transaction {
public:
Transaction();
virtual ~Transaction(void);
virtual void logTransaction() const = 0;
};

Transaction::Transaction()
{
logTransaction();
}

Transaction::~Transaction()
{
}

class BuyTransaction: public Transaction {
public:
virtual void logTransaction() const;
};

void BuyTransaction::logTransaction() const
{
printf("BuyTransaction::logTransaction");
}


class SellTransaction: public Transaction {
public:
virtual void logTransaction() const;
};

void SellTransaction::logTransaction() const
{
printf("SellTransaction::logTransaction");
}

Flat View: This topic has 12 replies on 1 page
Topic: First Steps to Scala Previous Topic   Next Topic Topic: States and Components in Flex 4

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use