Why does a purely virtual/abstract class require a constructor, in particular for protected const member variables?
Asked Answered
S

3

5

I have a purely virtual class defined as such:

class BaseClass {
 protected:
  const int var;
 public:
  void somefun() = 0; // what I mean by a purely virtual class
  // stuff...
};

If I don't add a constructor defined as such:

BaseClass(const int & VAR) : var(VAR) {};

that I would have to subsequently use in ever derived class, my derived class can't initialize the const variable var to whichever value it wants to. Now I actually understand what's going on here. Before constructing a derived class, a constructor of the base class is called, at which point const member variables must be initialized. My question is not a "how do I make my code work" kind of question, that's already been done. My question is about why the compiler thinks it's necessary. For a purely virtual class, shouldn't I be allowed to write something like:

class DerivedClass : BaseClass {
 public:
  DerivedClass() : var(SOME_VALUE) {};
}

If the compiler knows that a call to a BaseClass constructor will necessarily be followed by a call to some derived class constructror (since an object of abstract type can never be instantiated) shouldn't it give us a bit more leeway?

Is this all a consequence of how C++ chooses to get around the Diamond problem? Even if that was the case, shouldn't the compiler at least somehow allow for the possibility that const member variable of purely virtual functions will be defined in derived classes? Is that too complicated or does that mess with the C++ solution to the Diamond problem?

Thanks for the help everyone.

Serviceman answered 22/2, 2012 at 16:55 Comment(3)
Probably because the compiler would have to enforce the rule that derived classes must initialize protected members of their base class, which would be a bit convoluted.Argueta
I think it's true that it does that. But is that really necessary in this situation?Serviceman
You can do it like this. , ` DerivedClass() : BaseClass::var {SOME_VALUE} {} ; ` Should note I have not tested this with data members marked const. Specifically what I tested was: ` Base{ private: unsigned var1, var2 ; string var3 ; public: Base(string str) : var1{str.length()}, var2{0}, var3{str} {} }; Derived:Base {private: double dnum ; public: Derived(String dstr) : Base::Base{dstr}, dnum{2.2} {} }; `Concentre
P
6

It's not "purely virtual" (whatever you mean by that) - it contains a data member.

Class members can only be initialised by the initialiser list of a constructor of that class, not of a derived class. That's how object initialisation is specified: all members that are initialised, are initialised before the constructor body begins.

Constant objects must be initialised, since they can't be assigned a value later.

Therefore, a class with a constant data member must initialise it in each constructor.

Putsch answered 22/2, 2012 at 17:3 Comment(5)
What I mean by purely virtual is what you might call an abstract class, containing a function that is not defined except in derived classes i.e. a class that will never be initialised...Serviceman
@ticster: OK, that's known as "abstract". It means the class can't be instantiated except as a base class, but has nothing to do with any data members it might have. They behave in exactly the same way as they would in a non-abstract class.Putsch
But my point is, do they have to? Knowing that an object of that type will never be instantiated, shouldn't the compiler give us some extra leeway?Serviceman
@ticster: That would add a number of complications (diagnosing failure to initialise, preserving initialisation order, and probably others that I can't immediately think of) for no particular benefit other than allowing you to omit a small fragment of code. I would guess that's why it's not allowed (assuming anyone even considered allowing it), but if you really want to know the reasons you'd have to ask the language committee.Putsch
Thanks for the answer. I figured it was just more complications than it merited, but I still wondered if there might not be a broader advantage to allow special treatment of abstract classes that takes into account the fact that the base constructor alone will never be called. I guess not, and the complications you gave must outweigh them.Serviceman
U
3

For a purely virtual class, shouldn't I be allowed to write something like

No, but you can(and in this case should) write something like this:

class DerivedClass : BaseClass {
 public:
  DerivedClass() : BaseClass(SOME_VALUE) {};
};
Utilize answered 22/2, 2012 at 17:4 Comment(3)
Yes, like I said, that's what I've done, using the constructor I defined for BaseClass. I'm just wondering why the language is that way...Serviceman
In the example you gave us, you tried to initialize BaseClass::var from DerivedClass::DerivedClass directly, without using any BaseClass constructor. By calling the parent constructor you can actually initialize the var instance variable. This answers the "how do i make my code work" question.Utilize
Like I said, I've already figured that out and done it. I'm just trying to understand why it's necessary in this case, in particular because BaseClass can never be initialized.Serviceman
S
2

The construction of an object occurs in a specific order. The base class must be fully constructed before the constructor of a derived class is run, so that the derived constructor is working with a fully formed and valid base object. If the initialization of base member variables were put off until the construction of the derived class, this invariant would be broken.

Sheathe answered 22/2, 2012 at 17:21 Comment(1)
Ok, but why does that apply to an abstract class? After all, the compiler KNOWS that a following constructor will necessarily be called since an object of abstract type can never be instantiated.Serviceman

© 2022 - 2024 — McMap. All rights reserved.