0

Can we achieve runtime polymorphism(method overriding) without virtual function in C++

I think that since it is runtime it should only be possible through pointers using virtual functions but we can override a function without virtual function and pointers also. Here is the following code.. But will it still be runtime ?

#include <iostream>
using namespace std;
class animal{
public:
    void print(){
        cout<<"I am animal"<<endl;
    }
};
class dog: public animal{
public:
    void print(){
        cout<<"I am dog"<<endl;
    }
};
class cat:public animal{
public:
    void speak(){
        cout<<"meow"<<endl;
    }    
};
int main()
{
    dog obj1;
    obj1.print(); // I am a dog
    cat obj2;
    obj2.print(); // I am animal
    return 0;
}
3
  • 1
    Sure, you can write your own lookup mechanism, but why? Commented Sep 23, 2023 at 19:29
  • 3
    "we can override a function without virtual function" No we cannot. Commented Sep 23, 2023 at 20:41
  • override is a specific keyword used only in conjunction with virtual. But for dynamic(runtime) polymorphism without virtual functions check boost.any's implementation. The real thing we need to avoid is not 'virtual' keyword. We try to keep the data local inside CPU cache; when doing so, the set of used types becomes bounded and known, so we sometimes discard virtual functions to save a few more bytes of memory. Commented Sep 24, 2023 at 10:41

1 Answer 1

0

It's certainly true that you can't override a method without virtual functions, as one of the comments mentioned. To achieve any kind of runtime polymorphism, you will need some kind of lookup table either implemented yourself or by the compiler. A relatively recent alternative to virtual functions is to use variants instead. Another great blog post compares virtual functions to std::variant. To quote:

virtual functions – dynamic dispatch via vtable vs std::variant – dynamic dispatch via internal union tag (discriminant) and compile-time generated function pointer table

Example code

#include <memory>
#include <string>
#include <variant>

namespace Virtual
{
struct Animal
{
    virtual ~Animal() = default;
    virtual std::string speak() const = 0;
};
struct Cat : Animal
{
    std::string speak() const override { return "meow"; }
};
struct Dog : Animal
{
    std::string speak() const override { return "woof"; }
};
} // namespace Virtual

namespace Variant
{
struct Cat
{
    std::string speak() const { return "meow"; }
};
struct Dog
{
    std::string speak() const { return "woof"; }
};
using Animal = std::variant<Cat, Dog>;
} // namespace Variant

{
    using namespace Virtual;
    const std::unique_ptr<const Animal> pCat = std::make_unique<Cat>();
    const std::unique_ptr<const Animal> pDog = std::make_unique<Dog>();
    pCat->speak(); // expect "meow";
    pDog->speak(); // expect "woof";
}

{
    using namespace Variant;
    const auto cat = Animal{Cat{}};
    const auto dog = Animal{Dog{}};
    std::visit([](const auto& animal) { return animal.speak(); }, cat); // "meow";
    std::visit([](const auto& animal) { return animal.speak(); }, dog); // "woof"
}

Also illustrates another quote from the blog post

virtual functions - clients must operate using pointer or reference vs std::variant – clients use value type [i.e., wrapping the underlying object in a variant]

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.