Pure Virtual Function With Implementation

Pure virtual function with implementation

A pure virtual function must be implemented in a derived type that will be directly instantiated, however the base type can still define an implementation. A derived class can explicitly call the base class implementation (if access permissions allow it) by using a fully-scoped name (by calling A::f() in your example - if A::f() were public or protected). Something like:

class B : public A {

virtual void f() {
// class B doesn't have anything special to do for f()
// so we'll call A's

// note that A's declaration of f() would have to be public
// or protected to avoid a compile time problem

A::f();
}

};

The use case I can think of off the top of my head is when there's a more-or-less reasonable default behavior, but the class designer wants that sort-of-default behavior be invoked only explicitly. It can also be the case what you want derived classes to always perform their own work but also be able to call a common set of functionality.

Note that even though it's permitted by the language, it's not something that I see commonly used (and the fact that it can be done seems to surprise most C++ programmers, even experienced ones).

Why does C++ support pure virtual functions with an implementation?

C++ Supports pure virtual functions with an implementation so class designers can force derived classes to override the function to add specific details , but still provide a useful default implementation that they can use as a common base.

Classic example:

class PersonBase
{
private:
string name;
public:
PersonBase(string nameIn) : name(nameIn) {}

virtual void printDetails() = 0
{
std::cout << "Person name " << name << endl;
}
};

class Student : public PersonBase
{
private:
int studentId;
public:
Student(string nameIn, int idIn) : PersonBase(nameIn), studentId(idIn) { }
virtual void printDetails()
{
PersonBase::printDetails(); // call base class function to prevent duplication
std::cout << "StudentID " << studentId << endl;
}
};

Implement pure virtual function with using

Node and Graph are unrelated classes. Which makes Node::Visit and Graph::Visit distinct unrelated member functions.

Neither can override the other. You seem to think Graph::Visit is somehow "better" than Node::Visit on account of not being pure virtual, and should therefore override it. But there is no objective reason for it. For starters, pure virtual functions can have a definition:

struct A {
virtual void thing() = 0;
};

inline void A::thing() {}

And secondly, a function can be made pure virtual when overriden:

struct A {
virtual void thing() {}
};

struct B : A {
void thing() override = 0;
};

So really, there is no reason for Node::Visit and Graph::Visit to interact as overriders of each other. That's why you need to be explicit and define a function yourslef that will serve as an overrider*.


* One shortcoming of C++ is that you in fact override both Node::Visit and Graph::Visit in GraphNode3. IMO it's a quirk of the language (really, it should be possible to keep them unrelated). MSVC has an extension that lets you choose what to override in a more fine-grained manner.

Class with implementation of pure virtual function

Pure virtual functions are the virtual functions whose implementation must be provided by the derived class. This is one of the reason to make your virtual function pure, so that derived class must have it's implementation.

virtual void function()=0 is pure virtual function where =0 indicates it's purity.

When you have a pure virtual function in a class, you are not suppose to create a instance of class. (Otherwise making function pure virtual doesn't make much sense). Such a class is abstract class.

Now, there might be some cases where you want to have your own implementation of pure virtual function so that derived class may directly use it. Hence we provide body of pure virtual function in some cases.

If you want to create an instance of that class, don't make your virtual function pure.

pure virtual function with implementation in C++

This is the behaviour the C++ standard defines for virtual functions: call the version of the most derived type available.

Sure, for normal objects, the most derived type is the one of the object itself:

B b;
b.f1(); // of course calls B's version

The interesting part is if you have pointers or references:

B b;
A& ar = b;
A* ap = &b;

// now both times, B's version will be called
ar.f1();
ap->f1();

The same occurs inside f1, actually, you do implicitly:

this->f2(); // 'this' is a POINTER of type A* (or A const* in const functions).

There's a phenomenon when this does not occur (the example below requires a copy constructor):

B b;
A a = b; // notice: not a pointer or reference!
A.f1(); // now calls A's version

What actually happens here is that only the A part of b is copied into a and the B part is dropped, so a actually is a true, non-derived A object. This is called 'object slicing' and is the reason for that you cannot use base objects in e. g. a std::vector to store polymorphic objects, but need pointers or references instead.


Back to virtual functions: If you are interested in the technical details, this is solved via virtual function tables, short vtables. Be aware that this is only a de-facto standard, C++ does not require implementation via vtables (and actually, other languages supporting polymorphism/inheritance, such as Java or Python, implement vtables, too).

For each virtual function in a class there's an entry in its corresponding vtable.

Normal functions are called directly (i. e. an unconditional branch to the function's address is executed). For virtual function calls, in contrast, we first need to lookup the address in the vtable and only then we can jump to the address stored there.

Derived classes now copy the vtables of their base classes (so initially these contain the same addresses than the base class tables), but replace the appropriate addresses as soon as you override a function.

By the way: You can tell the compiler not to use the vtable, but explicitly call a specific variant:

B b;
A& a = b;
a.A::f1(); // calls A's version inspite of being virtual,
// because you explicitly told so
b.A::f1(); // alike, works even on derived type

Implementing pure virtual function from abstract base class: does override specifier have any meaning?

I'm not a big fan of override, but, assuming it's something that you find useful in general, then, yes, putting override on a virtual function that overrides a pure virtual functions is useful. Consider this rather contrived example:

struct Base {
virtual void f() = 0;
};

struct Derived : Base {
virtual void f();
virtual void f(int);
};

Now suppose that the maintainer of Base (perhaps even your future self) changes Base to look like this:

struct Base {
virtual void f(int) = 0;
};

Now the behavior of Derived has quietly changed. With override the compiler would report an error.

Do ALL virtual functions need to be implemented in derived classes?

Derived classes do not have to implement all virtual functions themselves. They only need to implement the pure ones.1 That means the Derived class in the question is correct. It inherits the bar implementation from its ancestor class, Abstract. (This assumes that Abstract::bar is implemented somewhere. The code in the question declares the method, but doesn't define it. You can define it inline as Trenki's answer shows, or you can define it separately.)


1 And even then, only if the derived class is going to be instantiated. If a derived class is not instantiated directly, but only exists as a base class of more derived classes, then it's those classes that are responsible for having all their pure virtual methods implemented. The "middle" class in the hierarchy is allowed to leave some pure virtual methods unimplemented, just like the base class. If the "middle" class does implement a pure virtual method, then its descendants will inherit that implementation, so they don't have to re-implement it themselves.

Enforce pure virtual function implementation, perhaps with different argument type

Your premise doesn't make a whole lot of sense. Let's imagine it were possible:

class Base {
virtual void step(/* something */);
};
class DerivedString : public Base {
void step(std::string) override;
};
class DerivedInt : public Base {
void step(int) override;
};

What would you have this do?

std::unique_ptr<Base> bs = std::make_unique<DerivedString>();
bs->step(1); // compiler error? runtime error?

std::unique_ptr<Base> bi = std::make_unique<DerivedInt>();
bi->step("one"); // compiler error? runtime error?

If your answer is "compiler error", then you should drop the virtual functions and the base class, as they're not providing any value.

If your answer is "runtime error" and you have C++17, you can use std::any:

class Base {
virtual void step(std::any);
};
class DerivedString : public Base {
void step(std::any v) override {
step(std::any_cast<std::string>(v));
}
void step(std::string s);
};
class DerivedInt : public Base {
void step(std::any v) override {
step(std::any_cast<int>(v));
}
void step(int i);
};

This will cause std::bad_any_cast to be thrown above.


If you don't have C++17, and you know the set of types in advance, then you can predeclare every overload of the function:

class Base {
virtual void step(std::string) { throw runtime_error(); }
virtual void step(int) { throw runtime_error(); }
};
// only subclass from this
template<typename T>
class BaseHelper : public Base {
void step(T) override = 0; // replace with pure-virtual
}
class DerivedString : public BaseHelper<std::string> {
void step(std::string s) override; // compiler error when instantiated if forgotten
};
class DerivedInt : public BaseHelper<int> {
void step(int i) override;
};

Overriding a virtual function but not a pure virtual function?

I attempted to write using B::<function> in order to override a pure
virtual function in A with the implementation in B.

You misunderstood. It is not possible to override a function this way. All that using B::h; does: it introduces B::h into the scope of struct C, so that X.h(); in main() becomes equivalent to X.B::h();. So, A::h() remains pure virtual, thus you cannot instantiate struct C.



Related Topics



Leave a reply



Submit