Actually, the main question is in the cap, but there are a couple more generated by him (quite silly, but I can’t enter them in any way). Who is not lazy, put all the dots for me over i;)

  1. Why can I declare a virtual destructor who can destroy any descendant, but the virtual constructor isn’t? (I read about the envelope-letter technique, but somehow I didn’t reconsider). Hypothetically, I understand that all inherited objects will be different, but, perhaps, just because of this?

  2. Why do we need a virtual destructor at all, if it is considered good practice to write a standalone destructor for all classes (hence, for descendants, as I understand it)?

  3. Why you can declare a pointer to an abstract class, but you cannot create an instance of an abstract class? (I understand that a virtual class is, in fact, a form (interface) of all classes generated by it => there is no sense to declare a simple abstract class, but the situation with the pointer is not clear) .

  4. Does it make any sense to write a constructor in an abstract class? (in personal opinion, no, but in examples of abstract classes, designers met).

  5. What is the point of declaring a virtual function of one class friendly in another?

    3 answers 3

    There is absolutely no reason for all functions to be virtual in an abstract class. For example, there is a classical pattern, when using an abstract class they implement a certain “skeleton” of a general algorithm, and with the help of virtual functions they fill in the particular details of a particular application of this algorithm. The details of the algorithm are implemented by virtual functions, and the algorithm itself is a non-virtual function. An elementary example of such a pattern could be a certain class "Program":

    class Program { protected: virtual void init() {} virtual void run() = 0; virtual void done() {} public: void execute() { init(); run(); done(); } }; 

    In this example, the actual "algorithm" itself is implemented by the execute() function, which has no reason to be virtual. The whole variety of behaviors of different "programs" is implemented by the successor classes through the overlapping of the functions init() , run() and done() , while the "algorithm" itself remains undisguable.

    1. The behavior of virtual functions in C ++ is fundamentally based on the concept of a dynamic object type . A virtual function call is, by definition, a call in which a particular version of a function is selected based on an analysis of the dynamic type of the object used in that call. For this reason, a virtual call is possible only when the dynamic type of the object is already defined (initialized, formed, fixed - choose the term that you like). So the constructor is exactly that special function that forms , initializes the type of the object. Until such time as the constructor worked, the object does not exist yet and the type of the object is not yet determined. Those. The concept of a virtual call to such a “raw” object is not yet applicable. For this reason, in C ++ there can be no virtual constructors.

      In other words, in C ++, the constructor is the same function that initializes the virtual call mechanism. Therefore, it is virtually impossible to call the constructor itself - at this point, the functionality of virtual calls has not yet been initialized.

      Physically, in the ubiquitous implementation of polymorphism, the functionality of virtual functions is provided by the so-called table of virtual functions , the pointer to which is stored in each polymorphic object. So the constructor, among other things, is exactly the same and binds each polymorphic object to its correct virtual function table. Before such a binding is performed, virtuality cannot work.

    2. Hmm ... Absolutely every class has its own destructor. But virtuality is needed precisely in order for the polymorphic deletion of an object to select the correct destructor correctly , in accordance with the dynamic type of the object being deleted. Those. if you have a pointer to the base class Base *p , which actually points to an object of the derived type Derived

       Base *p = new Derived; 

      then when performing delete p (polymorphic deletion) you need to Derived class destructor. For this to happen, the destructor must be virtual.

      If the destructor in such a situation is not virtual, then, as @VladD correctly noted in the comments, the program's behavior is not defined.

    3. It is impossible to create an independent copy of an abstract class because the language specification forbids this. And what is the point in this creation? The abstract class is a class with "nonexistent" (unwritten) virtual functions. Why and who might need objects of such an inferior class? What do you suggest to do if the user tries to call such a non-existent virtual function? To cause undefined behavior? The authors of the language decided that it was wiser to simply prohibit the creation of independent copies of abstract classes.

      At the same time, it can be said that instances of abstract classes can still be created, but only as basic sub - objects of non-abstract classes-heirs, and not as independent objects. Actually, this is the purpose of abstract classes - to serve as the base classes for successor classes.

      Here you can also add that there is still a loophole that allows you to try to make a call to a nonexistent virtual function of an abstract class: it is a call to a virtual function from the constructor (or destructor) of an abstract class. To "deceive" the compiler, such a call usually has to be done via an intermediate function.

       struct S { virtual void foo() = 0; void bar() { foo(); } S() { bar(); } }; struct D : S { virtual void foo() {} }; int main() { D d; } 

      The above code leads to indefinite behavior (practically, to the crash of the program) precisely because during the operation of the class S constructor, an attempt is made to call the nonexistent method S::foo() . One can conditionally say that during that “short time” when the constructor (or destructor) of an abstract class is working, the corresponding object is an independent abstract object with all the ensuing consequences, such as a program crash when trying to call a non-existent virtual function.

    4. I do not understand why this question arises. It makes sense to write a constructor in an abstract class, of course, if there is work for such a constructor in this class. The constructor must initialize something. Do you have something to initialize in your abstract class? If there is - then write the designer.

      Another thing is that usually in the abstract class there is nothing to initialize. Accordingly, the constructor is often not needed.

    5. I do not understand the essence of the question. Friendliness has nothing to do with virtuality. Therefore, declaring a virtual function as a friend has exactly the same meaning as declaring any other (non-virtual) function as a friend.

      Here it is worth repeating again that friendliness does not know anything about virtuality (and virtuality knows about friendship). Friendliness does not extend through the class hierarchy, i.e. The friendliness you declared will not apply to the "same" virtual function in the inheritance classes.

    • Oh, AndreyT himself looked in :) - VladD
    • Thank you, everything fell into place! :) - kvendingoldo
    • And one more small question. Can an abstract class be inherited from something? Suppose from another abstract class. - kvendingoldo
    • @AnT: Deleting by a pointer to a base type in the absence of a virtual destructor will not just cause the wrong destructor, but UB. - VladD
    • @VladD: I don’t say that the wrong destructor will be called. I just say that the call to the correct destructor will not happen . - AnT
    1. Types in C ++ are not first class objects. You cannot write a type to a variable, and call a constructor by this variable. Accordingly, virtual constructors are basically impossible in C ++. Imagine how you could call a constructor in a “virtual” way? When calling new you always need to specify the exact type. A constructor is essentially a static function.

      However, there is an idiom , sometimes called a virtual constructor . This is essentially a virtual factory method in the same class hierarchy (or parallel to it). For example, you can declare a virtual (and therefore non-static ) function Base* createNew() , and implement it in all descendants (the compiler does not check for any overload, so you have to be careful). Here, instead of a class variable, you have an instance of an object of the same class.

    2. A virtual destructor is needed, like any virtual function, so that when called by the pointer to the base class, the function of the derived class is executed. If you have a pointer to Base (this usually happens if you have a collection of pointers to the generated classes), and you delete an object by this pointer, then for the case of a non-virtual destructor, only the base class will be called. If the destructor of the derived class does something nontrivial to you, it will be “skipped” in this case.

      It was just a motivation. Worst of all, such a situation (deleting an object according to a pointer to a base class that does not have a virtual destructor) is generally undefined behavior in the standard. Take here and carefully read §5.3.5 / 3:

      If the static type of an object that is deleted does not match the dynamic type, the static type must be the base class of the dynamic type of the object being deleted, and the static class must have a virtual destructor. Otherwise, the behavior of the program is undefined. ( my translation )

    3. A pointer to an abstract class, like any class, can be declared to point to objects of a given type, as well as derivatives from it. In the case of an abstract class, these are only pointers to objects of derived types, of course.

      You cannot create an instance of an abstract class because it is not ready for use. If there is an abstract method in the class, it cannot be called, but in a real class each method must be called.

      I do not see a contradiction between these two facts. Moreover, if you have several non-abstract classes derived directly from the abstract, you have no other method to define a pointer to any of them.

    4. Yes, it makes sense. An abstract class is essentially just a “blank” class, but even this blank can have its own invariants, which need to be set in the constructor.

      Another thing is if you use an abstract class as an interface : just define which methods will be available. In this case, the constructor is not needed and is even redundant. C ++, unfortunately, does not distinguish between these two cases. More about the difference between the interface and the abstract class here .

    5. In principle, the same as the usual meaning of declaring a friendly function: access control. Friendship is not inherited , so that the descendants of the friendly class will not automatically be friends.

    Regarding the question in the heading: here again, the difference is between whether you use an abstract class as an interface, or as a semifinished product of another class. Both cases are valid. In the first, you should not have a constructor, data, and non-virtual / non-abstract functions. In the second, on the contrary, most likely you will have a non-trivial constructor, data, and just a few abstract methods. (See the link from p. 4.)

    • And one more small question: what's the point in a NOT purely virtual function? - kvendingoldo
    • @kvendingoldo: For the case of “stocking,” the same as in the ordinary, this is almost a ready class. Often used for example interception point: virtual void onchange() {} void change(int newval) { x = newval; onchange(); } virtual void onchange() {} void change(int newval) { x = newval; onchange(); } virtual void onchange() {} void change(int newval) { x = newval; onchange(); } . If the derived class wants it, it can override onchange . And if not, an empty onchange will work. Thus, it is like an abstract function, but it does not need to be defined if it is not necessary. But this does not apply to abstract classes, it is a common technique. - VladD

    Generally should not. If a non-virtual function is declared, then when called to the heirs, it will simply be executed (a function from the base class).

    1. Virtual destructor in general, as I know, it is customary to write always. When a destructor is called for an heir, all destructors in the hierarchy are unwound. And if it is not in the base class, the chain is broken.
    2. Because the pointer to the abstract class is the same as for the heirs and all heirs can be referred to as the base abstract class.
    3. Well, if something is the same for all heirs, then why not.
    4. Same as for situations for non-abstract classes.