2

I have a stack of classes with pure virtual members, it'll be populated by derived non-abstract classes. I'm getting the error:

Error   C2259   'ABC': cannot instantiate abstract class    TEMP    c:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.16.27023\include\xmemory0   879 

This error can be solved by using stack<ABC*>, however heap memory access is slower than stack memory, so I'm thinking about rewriting the base class ABC with no pure virtual members at all.

Would there be any drawbacks from this (except the possible human error of whoever might use this code)?

Is there a way I can create a stack of classes with pure virtual members on the stack? Or, maybe, am I too paranoid about using the heap? The class (in the original code) would be accessed very frequently.

Simplified version of the code below:

#include <iostream>
#include <stack>

class ABC {
public:
    ABC(int& a) : m_a(a) {}
    ~ABC() {}
    virtual void do_something() = 0;
    int m_a;
};

class DEF : public ABC {
public:
    DEF(int& a) : ABC(a) {}
    void do_something() override;
    ~DEF() {}
};

void DEF::do_something() {
    std::cout << "Hi!\n";
}

int main(int argc, char* argv[]) {
    int x = 123;
    std::stack<ABC> s;

    s.push(DEF(x));
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Eris
  • 35
  • 3
  • 2
    you confuse virtual dispatch with stack/heap. Pure virtual function does not require heap allocations. But anyhow `std::stack` does store its elements on the heap, so you are worrying about the wrong thing – 463035818_is_not_an_ai Jul 24 '22 at 18:28
  • 1
    Even without pure `virtual`s, your current approach is going to lead to object slicing. See https://stackoverflow.com/questions/274626/what-is-object-slicing – Stephen Newell Jul 24 '22 at 18:30
  • Fyi, this code [slices](https://stackoverflow.com/questions/274626/what-is-object-slicing) like a razor. – WhozCraig Jul 24 '22 at 18:30
  • `std::stack s;` -- This internally uses a `std::deque`, which uses the heap. – PaulMcKenzie Jul 24 '22 at 18:30
  • virtual functions are often said to have overhead, though that overhead is only compared to no runtime polymorphism. Once you go for runtime polymorphism, there is nothing wrong with (pure) virtual functions. – 463035818_is_not_an_ai Jul 24 '22 at 18:32
  • `std::stack s;` -- Yes, this looks like using another language (Java / Python) way of doing things in a C++ program. You are aware that the stack can only store `ABC` objects fully? You cannot store anything derived from `ABC` without the type being sliced away. – PaulMcKenzie Jul 24 '22 at 18:34
  • *"This error can be solved by using `stack`"* - it can also (arguably better) be solved by using `std::stack>` and inserting using `s.push(std::make_unique(x));` , and as a bonus you get RAII cleanup. – WhozCraig Jul 24 '22 at 18:40
  • The absolute best power of pure virtual functions is that you can use them to declare an interface (abstract base class). And interface are key to building large (unit testable) systems. And are used in dependency injection. Most of my designs start there... With interfaces – Pepijn Kramer Jul 24 '22 at 19:09
  • You're creating a monolithic design - which make lots of things depend on each other unnecessarily and becomes harder to maintain as the design evolves. Virtual functions (pure or otherwise) are design tools to ease such problems by separating interface (usually relatively stable) from implementation (needs to be evolve frequently). So, yes, you're too paranoid about using the heap. Focus on getting your design *working correctly* first, then use testing/profiling to find/fix any performance concerns. You'll find that your worry about speed of memory access is usually unjustified in practice. – Peter Jul 24 '22 at 20:09
  • Re: “heap memory access is slower than stack memory” — this is not true. – Pete Becker Jul 24 '22 at 23:17

2 Answers2

1

In this call

s.push(DEF(x));

the temporary object of the type DEF is implicitly converted to an object of the type ABC. So if to call the virtual function then the virtual function of the class ABC will be called.

You can use the polymorphism when you use pointers or references.

Here is your updated program.

#include <iostream>
#include <stack>

class ABC {
public:
    ABC(int& a) : m_a(a) {}
    ~ABC() {}
    virtual void do_something() 
    {
        std::cout << "Bye!\n";
    };
    int m_a;
};

class DEF : public ABC {
public:
    DEF(int& a) : ABC(a) {}
    void do_something() override;
    ~DEF() {}
};

void DEF::do_something() {
    std::cout << "Hi!\n";
}

int main()
{
    int x = 123;
    std::stack<ABC> s;

    s.push(DEF(x));

    s.top().do_something();
}

The program output is

Bye!

As you can see there is object slicing.

Pay attention to that you need to make the destructor virtual. You could just write in the base class

virtual ~ABC() = default;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

You can't have a container (of which stack is an example) of objects with virtual members at all (pure or not) and maintain the dynamic behavior. You can have a container of pointers to such objects and maintain the dynamic behavior, but not such objects themselves.

The time to use pure virtual is when the base class doesn't have a reasonable implementation available at all. Think of standard types like the stream buffers, the base simply defines an interface but there really isn't anything for the base to do for many operations. Only a derived type knows what it is supposed to do.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23