In C++, a pure virtual function is a function declaration that has no implementation in the class where it is declared, but is rather left to the child classes to override and define the function. This means that the parent class cannot be instantiated. In Java this is known as an abstract class or interface.
Chat with our AI personalities
We use virtual functions whenever we create a class which we expect to be used as a base class. Virtual functions are simply functions that we expect to be overridden by those classes. Whenever we implicitly invoke a virtual method, the most-derived override of that method is automatically invoked, even if we do not know the specific type of that derivative.
Let us consider the following minimal example in C++ which requires some means of differentiating between different types of the same class. For this we will use a type field:
#include
#include
enum g_type {a, b, c};
class bad {
g_type m_type;
public:
bad (g_type type): m_type (type) {}
std::string type() const;
};
std::string bad::type () const {
switch (m_type)
{
case a: return "A";
case b: return "B";
case c: return "C";
}
}
void print_type (const bad& object) { std::cout << object.type() << std::endl;
}
int main () {
bad x (a);
bad y (b);
bad z (c);
print_type (x);
print_type (y);
print_type (z);
}
Output:
A
B
C
This class isn't particularly useful but it is capable of determining it's own type from the type field, which is the only part the really interests us. Other methods that depend upon that type field can obviously make use of a similar switch/case structure to the one found in the bad::type() method, and thus provide implementations that are specific to each of those types. However, this is bad design for several reasons:
All these problems can be address through virtual functions. Consider the following alternative:
#include
#include
class good {
public:
virtual std::string type() const {return "base class";}
};
class a : public good {
public:
virtual std::string type () const override {return "A";}
};
class b : public good {
public:
virtual std::string type () const override {return "B";}
};
class c : public good {
public:
virtual std::string type () const override {return "C";}
};
void print_type (const good& object) {
std::cout << object.type() << std::endl;
}
int main () {
a x;
b y;
c z;
print_type (x);
print_type (y);
print_type (z);
}
Output:
A
B
C
[It should be noted that C++ methods are non-virtual by default while Java methods are virtual by default. While it could be argued that it makes more sense for all methods to be virtual by default, C++ inherits from C and since there are no virtual methods in C, they are non-virtual by default in order to maintain compatibility with C.]
Obviously this example has more classes, but that is entirely the point. Each class (a, b and c) "knows" its own type, so the base class doesn't have to specifically cater for them. The classes a, b and c are simply more specialised versions of their base class, and each caters only for its own type. You will note that although the print_type function expects a "good" object and invokes its type() function, the output is not "base class" as you might expect. Derived objects behave according to their most-derived type, so when you invoke a virtual method, the most-derived override is invoked automatically. This is known as polymorphism, where derived object's behave according to their actual type. This works even when the function knows nothing about the derivatives. However, it only works when we pass objects by reference (or by pointer). If we pass a derived object and the function expects a base class by value rather than by reference, the base class will be copied without the derived portion of the object. This is known as "object slicing" and is usually undesirable.
The implementation greatly reduces the cost of maintenance because if we need a new type we simply derive a new class and that new class caters specifically for that new type and nothing else. We do not have to modify the base class, nor do we have to modify any other classes, we simply override the virtual methods provided by the base class. End-users can also create their own types much more easily, even if they have no access to the source code. All they need to know can be found in the declaration of the "good" base class (which can be found in the class header which must be distributed with the class library). Classes a, b and c can actually be defined in completely separate files (or libraries) in order to maintain modularity.
Moreover, our programs no longer need to include code that will never be invoked, only the classes we physically instantiate need to be compiled, even if we include classes we never actually use. All the code that pertains to a specific class is encapsulated by that class. The "good" class essentially provides the interface to all its derivatives, even if the code that operates upon "good" classes doesn't know anything about those derivatives. And that's just as it should be because when you ship a base class you have no way of knowing what derivatives other programmers may derive from your base class.
Note that all base classes must have a virtual destructor. This is how you determine if a class can be used as a base class or not. If it does not have a virtual destructor and does not inherit from any other classes then it is not intended to be used as a base class. However, if it has one or more virtual methods, it must also have a virtual destructor.
The reason we need a virtual destructor is because derived objects must be destroyed in the reverse order they were constructed. Construction always begins from the least-derived class because a derived object cannot exist until its base class has been fully constructed. But during destruction, the derived class must be destroyed before its base class can be destroyed. If it weren't (if the destructor were not virtual), code that isn't aware of the derived class may destroy the base class, leaving an incomplete object in memory. We can see this working in the following minimal example:
#include
class base {
public:
base () {std::cout<<"creating base"<
~base () {std::cout<<"destroying base"<
};
class derived : public base {
public:
derived () {std::cout<<"creating derived"<
~derived () {std::cout<<"destroying derived"<
};
int main () {
base* object = new derived;
delete object;
object = nullptr;
}
Output:
creating base
creating derived
destroying base
As you can see, we constructed a derived object but we didn't destroy it. It's still in memory, but it's no longer valid because its base class no longer exists. Note that the "object" variable is a pointer to the base class of the derived class. This is valid because a derived class is said to be a "kind of" base and is really no different to our print_type function in the previous example which accepted a reference to a "good" object, even though we actually passed references to a, b and c objects.
To fix this, we need a virtual destructor:
class base { public:
base () {std::cout<<"creating base"<
virtual ~base () {std::cout<<"destroying base"<
};
Now we get the expected behaviour and the objects are both created and destroyed in the correct order:
creating base
creating derived
destroying derived
destroying base
You declare a pure virtual function whenever you wish your class to be an abstract data type and require all derivatives of the class to provide an implementation for the pure virtual function. Unlike a non-pure virtual function which needn't be overridden by derivatives, pure virtual functions must be overridden. Once a derivative provides an implementation, that function becomes non-pure virtual, and can then be inherited by derivatives of that derivative.
You declare a pure-virtual method in base classes that are intended to provide a common interface to two or more derived classes. Any class that declares a method as pure-virtual becomes an abstract base class (ABC), also known as an abstract data type (ADT).
You cannot instantiate abstract classes, but that's fine because the intent is to derive classes from the abstract class and define specialised implementations for the pure-virtual methods in each of the derived classes. The base class exists purely to provide a common interface to all its derivatives.
Virtual functions must be implemented in the base class, but they need not be implemented in their derived classes. Derived classes will automatically inherit the base class implementations of all virtual functions, but can override those functions by providing their own implementations. The base class methods can still be called explicitly to provide common functionality. A function that is declared virtual is expected to be implemented by derived classes, but it is not a requirement.
Pure-virtual functions need not be implemented in the base class, but they must be implemented in the derived classes. Any class that declares a pure-virtual function is automatically rendered an ADT (abstract data type) and cannot be instantiated other than through derivation. Derived classes do not inherit any of the abstract base class implementations, but they can call them explicitly. Derived classes that do not implement all the pure-virtual functions inherited from their abstract base classes are automatically rendered abstract themselves, however any implementations they do provide are subsequently treated as virtual methods (and are therefore inherited by derivatives). A concrete class is a class that inherits or implements the sum total of all pure-virtual methods inherited from all abstract base classes. Only concrete classes can be instantiated without being derived from.
[Edit: original answer was incomplete and misleading.]
Virtual functions are expected to be overridden by derived classes. Pure-virtual functions must be overridden by derived classes.
A pure virtual function can not be instanced directly, only in a derived class. A normal virtual function has no such requirement.
All virtual functions (including pure-virtual functions) are represented in italics. All non-virtual functions are represented normally. There is no differentiation between pure and non-pure virtual functions, however some people append "=0" to distinguish the pure-virtual functions.
Virtual Functions and Pure Virtual Functions are relevant in the context of class inheritance.Unlike Virtual Functions, Pure Virtual Functions do not require a body. This implies that when a base class defining such a function is inherited, the derived class must implement that function. Furthermore, the base class becomes abstract; meaning you cannot create an instance of the base class even if a body is implemented for the function. You are expected to derive from abstract classes; only the derived classes that implement all the inherited Pure Virtual functions can be instantiated.Here are some examples of Virtual and Pure Virtual function signatures:- Virtual Function: E.g. virtual void myFunction();- Pure Virtual Function: E.g. virtual void myFunction() = 0;
No, inlining is done at compile time whereas virtual functions are resolved at run time(late binding). So, virtual functions can't be inlined. Both properties are orthogonal.Inlining is a mere suggestion the compiler may ignore it if it is declared with virtual function.
A pure-virtual function is a function that must be overridden in derived classes. You simply add "=0" to the end of the function declaration. class AbstractClass { public: virtual void DoSomething()=0; // Pure-virtual. };
We make a virtual function pure whenever we wish to make our class an abstract base class (an abstract data type). Unlike a virtual function, pure virtual functions must be overridden by a derived class or by one of its derivatives (the function remains pure virtual until it is overridden, at which point it becomes virtual). Derived classes that do not provide a complete implementation for all the pure virtual functions it inherits become abstract themselves. You cannot instantiate an abstract base class other than through derivation.