Is there an effect of using performance / memory in non-polymorphic inheritance?

I'm curious about the effect of inheritance in non-polymorphic classes. In particular, I write two classes of smart pointers, neither with virtual methods, nor with both for very unique purposes. Since the main operator overloads and some standard functions are identical, and they only need one member variable, I decided that I could use the base class to reuse the code. Here is a simple layout of what I mean:

Base class:

template <class T>
class Pointer_Impl
{
public:
    T & operator*() { return this->*m_pointer; }
    // etc.
protected:
    T *m_pointer;
    //protected to prevent instantiation without using = delete
    Pointer_Impl() {}
    Pointer_Impl(T *) {}
    //other constructors, assignment and move operators, destructor here
};

And then:

template <class T>
class PointerA : public Pointer_Impl<T>
{
public:
    PointerA() { m_pointer = nullptr; }
    PointerA(T * obj) { m_pointer = obj; }
    // other constructors, assignment and move operators, destructor, and any other class-specific functions here
};

Question: Is there any overhead in memory or performance when getting a base class without any virtual methods? Since this is a smart pointer implementation, accurate and medium is what I'm looking for.

"", , .

+5
3

. virtual ( , ) . .

. virtual , , . , virtual ( ).

- , ; . , .

+8

, m_object, .

- , , , , ++. , . , , , , , .

, , , , , , , , . , , .

+5

No runtime performance limitations (if optimized). Obviously, a debug build can store some inheritance data).

As evidence, observe the output of the following programs

prog1:

template <class T>
class PointerA
{
public:
    PointerA(T * obj) : m_pointer(obj) {}
    T & operator*() { return *this->m_pointer; }
    T *m_pointer;
};

PROG2:

template <class T>
class Pointer_Impl
{
public:
    T & operator*() { return *this->m_pointer; }
protected:
    T *m_pointer;
    Pointer_Impl(T *) {}
};

template <class T>
class PointerA : public Pointer_Impl<T>
{
public:
    PointerA(T * obj) : Pointer_Impl<T>(obj) {}
};

general core:

int main() {
    PointerA<int> p(new int);
    volatile int i = 42;
    *p = i;
    i = *p;
}

Both produce the same assembly:

main:
        subq    $24, %rsp
        movl    $4, %edi
        call    operator new(unsigned long)
        movl    $42, 12(%rsp)
        movl    12(%rsp), %eax
        movl    %eax, 12(%rsp)
        movl    $0, %eax
        addq    $24, %rsp
        ret
+4
source

All Articles