Runtime polymorphism calls an overridden virtual function through a base reference or pointer. It supports substitutable implementations, but only when the base contract defines valid inputs, results, ownership, and destruction. The override keyword asks the compiler to verify that a derived method truly overrides.
A base value contains only the base subobject. Passing a derived object by value slices it, so virtual calls made on the copy cannot recover the discarded derived state.
#include <iostream>
#include <memory>
#include <vector>
struct Notice { virtual ~Notice() = default; virtual void send() const = 0; };
struct Email : Notice { void send() const override { std::cout << "email\n"; } };
struct Sms : Notice { void send() const override { std::cout << "sms\n"; } };
int main() {
std::vector<std::unique_ptr<Notice>> notices;
notices.push_back(std::make_unique<Email>());
notices.push_back(std::make_unique<Sms>());
for (const auto& notice : notices) notice->send();
}
email
sms
A derived declaration with the same function name can hide all base overloads. Bring the base set into scope with using Base::name when that is the intended interface.
A polymorphic base normally needs a virtual destructor so deleting through a base pointer destroys the complete derived object. Store owning polymorphic objects in std::unique_ptr<Base> by default; use shared ownership only when the lifetime really has multiple independent owners.
Avoid calling virtual functions from constructors and destructors as a customization mechanism: dispatch is limited to the part currently being constructed or destroyed. Prevent slicing by passing polymorphic objects through references or pointers instead of copying them into base values.
A virtual destructor preserves derived cleanup when an object is owned through a base pointer.
#include <iostream>
#include <memory>
struct Task {
virtual ~Task() = default;
virtual void run() const = 0;
};
struct EmailTask final : Task {
void run() const override { std::cout << "email\n"; }
};
int main() {
std::unique_ptr<Task> task = std::make_unique<EmailTask>();
task->run();
}
email
Try this next
0 of 2 completed
It occurs when a virtual function is called through a base reference or pointer. Direct calls on a known object do not need runtime dispatch.
No. The compiler chooses an overload from the argument types; virtual overriding is selected at runtime.
Copying a derived object into a base object discards the derived portion. Use references or smart pointers for polymorphic objects.
Explore 500+ free tutorials across 20+ languages and frameworks.