Size of derived class in virtual base class function

Viewed 96

Consider the following code

class A {
    int x, y;
public:
    A(){}
    virtual void PrintSize(){ cout << sizeof(typeof(*this)) << endl; }
};

class B : public A {
    int a, b, c;
public:
    B(){}
};

int main() {
    A obja;
    B objb;

    obja.PrintSize();
    objb.PrintSize();
}

The intent of "PrintSize()" is to get the size of the current class where we are calling it from. What happens is that this-keyword refers to class A even though we are calling it from B. We don't want this since we need this function to be general for child classes.

We could obviously redefine the function verbatim to every class. The code would become harder to hande since there's so many unnesessary lines. Not to mention that re-writing the function to every class would defeat the purpose of deriving it in the first place.

Here's my temporary fix:

class A {
public:
    virtual void PrintSize(){ cout << sizeof(typeof(*this)) << endl; }
};

class B : public A {
public:
    virtual void PrintSize(){ cout << sizeof(typeof(*this)) << endl; }
};

class C : public A {
public:
    virtual void PrintSize(){ cout << sizeof(typeof(*this)) << endl; }
};

class D : public A {
public:
    virtual void PrintSize(){ cout << sizeof(typeof(*this)) << endl; }
};
2 Answers

While the accepted answer may have solved the immediate problem, you suddenly have no common base class for B and C. They inherit from two unrelated classes, namely A<B> and A<C>.

An alternative is to create a common base that defines an interface (called Interface below) and to add the CRTP class template between the derived classes and the interface. This lets you keep pointers and references to Interface and call the virtual member functions using those.

Here's an example of storing pointers to the common base class in a vector:

#include <iostream>
#include <memory>
#include <vector>

struct Interface {
    virtual ~Interface() = default;

    virtual void PrintSize() const = 0;
    virtual void do_stuff() const = 0;
};

template<typename T>
struct Printer : public Interface {
    void PrintSize() const override {
        std::cout << sizeof(T) << '\n';
    }
};

class B : public Printer<B> {
    int a{};
public:
    void do_stuff() const override { std::cout << "B doing stuff\n"; }
};

class C : public Printer<C> {
    int a{}, b{}, c{};
public:
    void do_stuff() const override { std::cout << "C doing stuff\n"; }    
};

int main() {
    std::vector<std::unique_ptr<Interface>> objs;

    objs.emplace_back(std::make_unique<B>());
    objs.emplace_back(std::make_unique<C>());

    for(auto& ptr : objs) {
        ptr->do_stuff();
        ptr->PrintSize();
    }
}

Possible output:

B doing stuff
16
C doing stuff
24

You can use the CRTP idiom to do this. https://eli.thegreenplace.net/2011/05/17/the-curiously-recurring-template-pattern-in-c The idea is the parent class is a template, so you can have access to the type of the child class directly in it. With that, you'll be able to remove all "PrintSize" from child class.

Example :

template <typename Derived>
class A {
    int x, y;
public:
    A() {}
    void PrintSize() { cout << sizeof(Derived) << endl; }
};

class B : public A<B> {
    int a, b, c;
public:
    B() {}
};

class C : public A<C> {
public:
    C() {}
};

int main() {
    C objc;
    B objb;

    objc.PrintSize();
    objb.PrintSize();
}

The output is :

8

20

Related