Can a destructor be a pure virtual function in C + +?
It is well known that in the process of implementing polymorphism, the destructor of the base class is generally set virtual
so that it delete
can be called in a multi- state chain . Then can the destructor be set to pure virtual?
class CBase{ public: CBase() { printf("CBase()\n"); } virtual0// 析构函数是纯虚函数};
The answer is yes, so what is the purpose of this implementation? Avoid instantiation , of course.
However, because a derived class cannot implement a destructor for a base class , the base class destructor can be marked as pure virtual, but the destructor must still be implemented, otherwise the derived class cannot inherit or compile through .
Discussed in detail below:
Virtual destructor
We know that in order to be able to correctly invoke the destructor of an object, it is generally required that a top-level class with a hierarchy define its destructor as a virtual function. Because in delete
an abstract class pointer, you have to find the real destructor through the virtual function.
Such as:
class Base { public: Base(){} virtual ~Base(){} }; public Base { public: Derived(){}; ~Derived(){}; } void foo() { Base *pb; new Derived; delete pb;
This is the correct usage that will occur when dynamic binding , which will call Derived
the destructor first, and then Base
the destructor
If the destructor is not added virtual
, delete pb
only Base
the destructor is executed, not the actual Derived
destructor.
Because it is not virtual
a function, the function that is called depends on pointing to the static type , that is Base
.
Pure virtual destructor
The problem now is that we want to Base
make an abstract class that cannot directly construct an object and need to define a pure virtual function in it. If there are no other suitable functions, the destructor can be defined as pure virtual, and the preceding definition will be CObject
changed to:
class Base { public: Base(){} virtual0;
However, this code cannot be compiled, usually an link
error, and cannot be found ~Base()
by the reference ( gcc
error report).
error"public: virtual __thiscall Base::~Base(void)" (??1Base@@UAE@XZin"public: virtual __thiscall Derived::~Derived(void)" (??1Derived@@UAE@XZ)1error1 unresolved externals
This is because destructors, constructors, and other intrinsic functions are different, and when called, the compiler needs to produce a call chain . That is, the destructor that is Derived
implicitly called Base
in destructors. The missing function body in the code just now ~Base()
will, of course, be wrong.
There is a misunderstanding, some people think that virtual f()=0
this pure virtual function syntax is not defined by the semantics of the body.
In fact, this is not right. This syntax simply indicates that the function is a pure virtual function , so this class becomes an abstract class and cannot produce an object . We can definitely specify the function body for pure virtual functions. The usual pure virtual function does not require the function body, because we generally do not invoke this function of the abstract class, only the corresponding function of the derived class is called. Thus, we have a pure virtual destructor function body, the above code needs to be changed to:
class Base { public: Base(){} virtual0//pure virtual }; Base::~Base()//function body { }
From a syntactic point of view, the above destructors cannot be written directly to the class declaration (the method of inline functions). This may be a non-orthogonal place. But it does seem a little cumbersome.
This problem seems to be somewhat scholarly, because generally we can Base
find a more suitable function in, by defining it as a pure virtual function without an implementation body, and defining an entire class as an abstract class. But this technique also has some applications, such as this example:
classBase//abstract class{ Public:Virtual~base () {};//virtual, not pure Virtual voidHiberarchy ()Const=0;//pure Virtual};voidBase::hiberarchy ()Const //pure Virtual also can has function body{STD::cout<<"Base::hiberarchy"; }classDerived: PublicBase { Public: Derived () {}Virtual voidHiberarchy ()Const{Base::hiberarchy ();STD::cout<<"Derived::hiberarchy"; }Virtual voidFoo () {}};intMain () {base* pb=NewDerived (); Pb->hiberarchy (); Pb->base::hiberarchy ();return 0; }
In this example, we try to print out the inheritance relationship of the class. Virtual functions are defined in the base class Hiberarchy
and are then overloaded in each derived class. Once again we see that because we want to make Base
an abstract class, and no other suitable method member in this class can be defined as pure virtual, we still have to Hiberarchy
define it as pure virtual. (Of course, the function can be defined entirely ~Base
, as discussed above.) ^_^)
In addition, you can see that main
there are two methods of invocation, the first is the normal way, dynamic link, execute virtual function, get the result, the "Derived::Hiberarchy"
second is to specify the way of the class, no longer perform the virtual function dynamic link process, the result is "Base::Hiberarchy"
.
As you can see from the above analysis, the real purpose of defining pure virtual functions is to define abstract classes , not the function itself. In contrast, in java
, the syntax for defining abstract classes is abstract class
to specify at the class level (of course, the virtual function or the abstract
keyword). Isn't this the better way? In the Stroustrup
design and evolution of the C + + language, I found this passage:
"I chose to describe individual functions as pure virtual, not in the form of defining a complete class declaration as abstract, because the concept of pure virtual functions is more flexible. I value the ability to define classes in stages, that is to say, I find it useful to define some pure virtual functions in advance, and to define other classes for further derivation. "
I have not fully understood the latter sentence, and I would like to elaborate on this concept from another perspective. That is, in a multi-layered complex class structure, the intermediate-level classes should materialize some abstract functions, but probably not all of them. The middle class does not need to know whether to materialize all the virtual functions, and what functions their ancestors have materialized, as long as they focus on their duties . In other words, the middle class does not need to know whether it is a real abstract class, and the designer does not have to consider whether or not to add a similar description at the class level of the intermediate class abstract
.
Of course, the design of a language has a variety of factors, good or bad are all aspects. It's just an explanation.
Finally, some common questions about virtual functions are summarized:
virtual functions are dynamically bound , that is, pointers and references that use virtual functions can correctly locate the corresponding function of the actual class, rather than executing the function that defines the class. This is the basic function of the virtual function, it is no longer explained.
A constructor cannot be a virtual function . Furthermore, the virtual function is called in the constructor, and the corresponding function of the parent class is actually executed, because the polymorphism is not well constructed disable
.
Destructors can be virtual functions, and in a complex class structure, this is often necessary .
defining a function as a pure virtual function is actually defining the class as an abstract class and cannot instantiate an object .
a pure virtual function usually has no body defined, but it can be owned and even displayed.
Destructors can be purely virtual, but pure virtual destructors must have a defined body, because the invocation of the destructor is implicit in the subclass .
A non-pure virtual function must have a defined body, or it is an error.
The virtual function definition of a derived class override
must be exactly the same as the parent class ( c++11
used in the override
compiler Check). In addition to a special case, if the return value in the parent class is a pointer or reference, the child class override
can return the derivation of the pointer (or reference). For example, in the example above, defined in, Base
virtual Base* clone()
Derived
can be defined as in virtual Derived* clone()
. As you can see, this relaxation Clone
is very useful for patterns (that is, it override
does not check the return value type).
Virtual destructor and pure virtual destructor in C + +