Search code examples
c++pointersreferencepolymorphismsmart-pointers

Alternatives for storing a class member as a raw pointer


In the code example shown below - in Container class, it owns (and is responsible fore destroying) two objects c, d, which are subclasses of an abstract class B. Container object can create new ObjectDisplay that takes a kind of B in its constructor. I can pass the abstract type B as a pointer into ObjectDisplay and store it as a RAW pointer. But it's not ideal to store & use a raw pointer and always check if it's a null pointer. If B wasn't an abstract class, I could pass it in ObjectDisplay as a reference (ie. ObjectDisplay (B& b)). But since I can't change B, I wonder what's the aternative of storing B* object as a raw pointer in ObjectDisplay?

// B is abstract
class B
{
public:
    virtual int getDefault() = 0;
};

class C : public B
{
public:
    int getDefault() override { return 1; }
};

class D : public B
{
public:
    int getDefault() override { return 5; }
};

class ObjectDisplay
{
public:
ObjectDisplay (B* b) : object (b) {}

void someFunction()
{
    const auto result = b->getDefault();
    // do something
}

private:
B* object;
};

class Container
{
public:
    void addDisplay()
    {
    displays.push_back (ObjectDisplay (&c));
    displays.push_back (ObjectDisplay (&d));    
    }
private:
    C c;
    D d;
    std::vector<ObjectDisplay> displays;
};

Solution

  • If B wasn't an abstract class, I could pass it in ObjectDisplay as a reference

    No, if B is an abstract class, you can still pass it by reference. B& object can be bound to an instance of B's subclass. It behaves almost the same as pointers.

    As quoted in cppref:

    That is to say, if a derived class is handled using pointer or reference to the base class, a call to an overridden virtual function would invoke the behavior defined in the derived class.

    Declare a member of B& in ObjectDisplay and construct it through a reference.

    class ObjectDisplay
    {
    public:
        ObjectDisplay (B& b) : object (b) {}
    private:
        B& object;
    
    };
    
    class Container
    {
    public:
        void addDisplay()
        {
            displays.push_back (ObjectDisplay (c));
            displays.push_back (ObjectDisplay (d));    
        }
    };
    

    See online demo

    Aside: Since you are passing a temporary ObjectDisplay object directly constructed in push_back, I recommend you to use emplace_back.

        void addDisplay()
        {
            displays.emplace_back (c);
            displays.emplace_back (d);
        }