简体   繁体   中英

Are there any advantages to pure virtual members (except the human error that they might prevent)?

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));
}

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM