Virtual function: Difference between revisions

Content deleted Content added
+Derived class constructor calls virtual function
Line 110:
 
== Behavior during construction and destruction ==
Languages differ in their behavior while the [[Constructor (computer science)|constructor]] or [[Destructor (computer science)|destructor]] of an object is running. For this reason, calling virtual functions in base class constructors is fatal in C++ (and compilers or linker would not issue error nor warning according to the scope of the language), therefore being generally discouraged (softly speaking).<syntaxhighlight lang="c++">
 
</syntaxhighlight>In C++, the "base" function is called. Specifically, the most derived function that is not more derived than the current constructor's class is called.<ref>{{cite web |title=Never Call Virtual Functions during Construction or Destruction |last=Meyers|first=Scott|date=June 6, 2005 |url=http://www.artima.com/cppsource/nevercall.html}}</ref> If that function is a pure function, then undefined behavior occurs.
 
In Java and C#, the derived implementation is called, but some fields are not yet initialized by the derived constructor (although they are initialized to their default zero values).<ref>{{cite web |title=Joy of Programming: Calling Virtual Functions from Constructors |last=Ganesh|first=S.G.|date=August 1, 2011 |url=https://www.opensourceforu.com/2011/08/joy-of-programming-calling-virtual-functions-from-constructors/}}</ref> Some [[Design pattern (computer science)|design patterns]], such as the [[Abstract Factory Pattern]], actively promote this usage in languages supporting this ability.
 
== Virtual destructors ==
Object-oriented languages typically manage memory allocation and de-allocation automatically when objects are created and destroyed. However, some object-oriented languages allow a custom destructor method to be implemented, if desired. If the language in question uses automatic memory management, the custom destructor (generally called a finalizer in this context) that is called is certain to be the appropriate one for the object in question. For example, if an object of type Wolf that inherits Animal is created, and both have custom destructors, the one called will be the one declared in Wolf.
 
In manual memory management contexts, the situation can be more complex, particularly in relation to static dispatch. If an object of type Wolf is created but pointed to by an Animal pointer, and it is this Animal pointer type that is deleted, the destructor called may actually be the one defined for Animal and not the one for Wolf, unless the destructor is virtual. This is particularly the case with C++, where the behaviourbehavior is a common source of programming errors if destructors are not virtual.
 
== Derived class constructor calls virtual function ==
- Calling virtual base function within derived constructor, if function is not defined within derived class, are captured by the linker as being unresolved symbol - which is as expected.
 
- Although '''calling virtual base function''' '''through a base wrapper''', would not be captured by compiler nor linker, and '''is fatal in C++ at runtime''' (some runtimes will issue pure virtual zero function call error)
 
This is is due to way code is compiled/linked. Within the scope of class definition, calling base wrapper function is fully resolved symbol, whether same or separate translation unit, but the base function calling unresolved virtual never known until runtime (at least compiler does not see that far).
 
 
These things should not happen when code is written at least within same solution, compilers do loop unrolls and various variable optimizations, it should also do class construction tree check especially for virtual functions.
 
Example below shows this in action (virtual function test1 is defined within Derived class two levels apart)<syntaxhighlight lang="c++" line="1">
struct Base {
virtual void test1() = 0;
void base_functest1_wrap() { // wrapper
test1();
}
virtual ~Base() {} // warning C5204, care about virtual destructors? - read below
 
// warning C5204, care about virtual destructors? - read below
virtual ~Base() {
}
};
 
struct Mid : Base {
virtual void test2() = 0;
 
Mid() {
//test1(); // linker LNK2019, unresolved symbol
base_functest1_wrap(); // compiles all good - fatal runtime (on construct)
}
};
 
struct Derived : Mid {
void test1() {}
void test2() {}
}
void test2() {
}
};
 
int main() {
Derived d;
}
</syntaxhighlight>
</syntaxhighlight>In C++, the "base" function is called. Specifically, the most derived function that is not more derived than the current constructor's class is called.<ref>{{cite web |title=Never Call Virtual Functions during Construction or Destruction |last=Meyers|first=Scott|date=June 6, 2005 |url=http://www.artima.com/cppsource/nevercall.html}}</ref> If that function is a pure function, then undefined behavior occurs.
 
In Java and C#, the derived implementation is called, but some fields are not yet initialized by the derived constructor (although they are initialized to their default zero values).<ref>{{cite web |title=Joy of Programming: Calling Virtual Functions from Constructors |last=Ganesh|first=S.G.|date=August 1, 2011 |url=https://www.opensourceforu.com/2011/08/joy-of-programming-calling-virtual-functions-from-constructors/}}</ref> Some [[Design pattern (computer science)|design patterns]], such as the [[Abstract Factory Pattern]], actively promote this usage in languages supporting this ability.
 
== Virtual destructors ==
Object-oriented languages typically manage memory allocation and de-allocation automatically when objects are created and destroyed. However, some object-oriented languages allow a custom destructor method to be implemented, if desired. If the language in question uses automatic memory management, the custom destructor (generally called a finalizer in this context) that is called is certain to be the appropriate one for the object in question. For example, if an object of type Wolf that inherits Animal is created, and both have custom destructors, the one called will be the one declared in Wolf.
 
In manual memory management contexts, the situation can be more complex, particularly in relation to static dispatch. If an object of type Wolf is created but pointed to by an Animal pointer, and it is this Animal pointer type that is deleted, the destructor called may actually be the one defined for Animal and not the one for Wolf, unless the destructor is virtual. This is particularly the case with C++, where the behaviour is a common source of programming errors if destructors are not virtual.
 
== See also ==
* [[Abstract method]]
* [[Inheritance (computer science)|Inheritance]]