locked
Function Overloading does not work in constructors RRS feed

  • Question

  • Hello everyone,

    I stumbled across some strange behaviour recently. The following code Fragment exits with error about a pure function call

    class Abstract { public: virtual int a() = 0; virtual int b() = 0; }; class PartiallyAbstract : public Abstract { public: PartiallyAbstract() { c(); } virtual int a() { return 1; } virtual int c() { return b(); } }; class Concrete : public PartiallyAbstract { public: Concrete() : PartiallyAbstract(){ } virtual int b() { return 2; } }; int main(int argc, char* argv[]) { Concrete object; object.c(); PartiallyAbstract *part = (PartiallyAbstract*)&object; part->c(); return 0; }

    when calling the constructor of PartiallyAbstract. Function 'c' seems to be uninitialized(0 - set to the error function) withing the

    constructor-context when looking at the virtual function table. When commenting out the function call in the constructor

    the program seems to work as expected, which is strange as when explicitely casting the object-pointer to PartiallyAbstract the same

    pointer that gets passed to the constructor should be obtained I guess.

    Thursday, March 29, 2012 9:01 PM

Answers

  • Idea Hat wrote:

    So this is actually standard behavior for object oriented languages

    Not really. Java and C# call a virtual method of the most derived class,  even though it's not constructed yet, from the base class' constructor.  C++ chose to go the other way. There are pros and cons to either  approach.

    In C++, this is indeed the standard behavior. The runtime type of the  object under construction is that of the constructor currently being  executed.


    Igor Tandetnik

    • Proposed as answer by Helen Zhao Tuesday, April 3, 2012 6:10 AM
    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Friday, March 30, 2012 1:04 AM
  • On 3/30/2012 9:49 AM, Heiko Lewin wrote:

    Must disagree and have other memories about that.

    Which part do you disagree with? I didn't state any opinions, only facts.

    But surely it can be explained why a constructor shouldn't be able to call function overloads whilst other methods can?

    The argument goes like this. When a base class's constructor runs, derived class has not been constructed yet. All its member variables have undefined contents. Formally, the object's lifetime hasn't started yet. It would be risky to call a method on an object in this undefined state. So a design decision was made to disallow such calls.

    In languages like C# or Java, the state of member variables is well-defined even before the constructor runs (they are guaranteed to be initialized to zero or null, or by static initializers if provided). So a decision was made by their designers to allow virtual calls to methods of not-yet-constructed classes.


    Igor Tandetnik

    • Proposed as answer by Helen Zhao Tuesday, April 3, 2012 6:12 AM
    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Friday, March 30, 2012 3:51 PM
  • On 4/6/2012 10:12 PM, Heiko Lewin wrote:

    Does someone
    at know at first glace whether this is a requirement to meet the
    C++-standard or is the behaviour really - to cite the msdn-library -
    undefined?

    I'm not sure I understand the question. "Undefined behavor" is a term-of-art in the C++ standard, not something that MSDN authors made up. Here are relevant portions of C++03 standard:

    1.3.12 undefined behavior
    behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined
    behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)...]

    10.4p6 Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

    12.7p3 Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8).


    Igor Tandetnik

    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Proposed as answer by Helen Zhao Monday, April 9, 2012 2:04 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Saturday, April 7, 2012 2:29 AM

All replies

  • So this is actually standard behavior for object oriented languages, and it makes a weird sort of sense. The base constructor is called before the inherited constructor, so you would not be able to use any of the inherited classes members that are initialized in the constuctor, including the virtual function table! Allowing you to use ANYTHING from the inherited class would break the inheritance model.

    The same is true for destruction. The inherited class is already destroyed when you get to the base class's destructor, so you cannot use anything.

    Unfortunately there is no real good way around this, so you'll just have to follow the initialize the base totally than initialize the inherited member totally.

    Thursday, March 29, 2012 9:11 PM
  • Sure about that? Setting up the virtual function tables and allocating memory for the object is something done by the compiler, which already has all the information needed at compile time or is unable to compile it. The sizes of the partial objects are known as well as the layout of the function tables.

    Thursday, March 29, 2012 9:20 PM
  • Idea Hat wrote:

    So this is actually standard behavior for object oriented languages

    Not really. Java and C# call a virtual method of the most derived class,  even though it's not constructed yet, from the base class' constructor.  C++ chose to go the other way. There are pros and cons to either  approach.

    In C++, this is indeed the standard behavior. The runtime type of the  object under construction is that of the constructor currently being  executed.


    Igor Tandetnik

    • Proposed as answer by Helen Zhao Tuesday, April 3, 2012 6:10 AM
    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Friday, March 30, 2012 1:04 AM
  • Heiko Lewin wrote:

    Sure about that? Setting up the virtual function tables and allocating  memory for the object is something done by the compiler,
    which already has all the information needed at compile time or is  unable to compile it.

    It's not done this way becase the compiler lacks the information to do  it some other way. As you say, the compiler has all the information it  needs. No, it's done this way intentionally, by design. The compiler in  fact needs to do a bit of extra work to ensure that a virtual function  call from a constructor ends up calling a method from the class being  constructed, not from any derived class.


    Igor Tandetnik

    Friday, March 30, 2012 1:14 AM
  • Must disagree and have other memories about that. But surely it can be explained why a constructor shouldn't be able to call function overloads whilst other methods can? Invoking a function-overload from a specific class is done with the Class::Method syntax. But sure enough there ain't no reason to discuss that - here.

    Friday, March 30, 2012 1:49 PM
  • Igor is right about the derived class in C# and Java (my bad!)

    Here is a C# example of why it is prevented in C++:

        abstract class A
        {
            int i;
            protected abstract int CalculateI(int j);
            public A(int j)
            {
                i = CalculateI(j);
            }
        }
        class B : A
        {
            int val;
            public B(int k)
                : base(k)
            {
                val = 2 * k;
            }
            protected override int CalculateI(int j)
            {
                return val;
            }
        }

    You can thus used val before it is filled in by the constructor, keeping it always at 0. If this was not an int (say an object or a pointer in c++), the memory could simply be unallocated, or this would be a null or bad ptr. This makes a weird and difficult to debug design model.

    I remembered a few ways around this..both are dependent on static functions (and thus are technically independent of the constructor)

    1) If the values can be calculated independently of the state of the partially constructed object, the best way is to evaluate the value with a static function and pass it as an argument.

    2) If you are really desperate to use the state of the object, you can pass in a function pointer to a static function that takes in the state (either the entire object for public info or just the necessary private/protected info) and returns the value for use in the constructor. While this seems like you are doing the work for the compiler to build the virtual function table, what you are really doing is explicitly stating that the function is independent of the state of the instance.

    Friday, March 30, 2012 2:58 PM
  • On 3/30/2012 9:49 AM, Heiko Lewin wrote:

    Must disagree and have other memories about that.

    Which part do you disagree with? I didn't state any opinions, only facts.

    But surely it can be explained why a constructor shouldn't be able to call function overloads whilst other methods can?

    The argument goes like this. When a base class's constructor runs, derived class has not been constructed yet. All its member variables have undefined contents. Formally, the object's lifetime hasn't started yet. It would be risky to call a method on an object in this undefined state. So a design decision was made to disallow such calls.

    In languages like C# or Java, the state of member variables is well-defined even before the constructor runs (they are guaranteed to be initialized to zero or null, or by static initializers if provided). So a decision was made by their designers to allow virtual calls to methods of not-yet-constructed classes.


    Igor Tandetnik

    • Proposed as answer by Helen Zhao Tuesday, April 3, 2012 6:12 AM
    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Friday, March 30, 2012 3:51 PM
  • The argument regarding uninitialized values sounds plausible at first, but the decision to make such a function call result in undefined behaviour instead of catching it either at compile time (with an error message or a warning) or just assuming that the programmer knows what he is doing could be doubted - at least it wouldn't have wasted the time of ppl reading my posts. Does someone at know at first glace whether this is a requirement to meet the C++-standard or is the behaviour really - to cite the msdn-library - undefined?

    Saturday, April 7, 2012 2:12 AM
  • On 4/6/2012 10:12 PM, Heiko Lewin wrote:

    Does someone
    at know at first glace whether this is a requirement to meet the
    C++-standard or is the behaviour really - to cite the msdn-library -
    undefined?

    I'm not sure I understand the question. "Undefined behavor" is a term-of-art in the C++ standard, not something that MSDN authors made up. Here are relevant portions of C++03 standard:

    1.3.12 undefined behavior
    behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined
    behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)...]

    10.4p6 Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

    12.7p3 Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8).


    Igor Tandetnik

    • Marked as answer by Helen Zhao Monday, April 9, 2012 1:46 AM
    • Unmarked as answer by Helen Zhao Monday, April 9, 2012 1:47 AM
    • Proposed as answer by Helen Zhao Monday, April 9, 2012 2:04 AM
    • Marked as answer by Helen Zhao Tuesday, April 10, 2012 1:45 AM
    Saturday, April 7, 2012 2:29 AM
  • The question aimed in the direction whether there can be compilers called standard-compatible where that works - and you answered it.

    Thank you


    • Edited by Heiko Lewin Saturday, April 7, 2012 2:42 AM
    Saturday, April 7, 2012 2:41 AM