Home > other >  Alternatives for storing a class member as a raw pointer
Alternatives for storing a class member as a raw pointer

Time:04-29

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

CodePudding user response:

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

CodePudding user response:

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?

Just because B is an abstract class does not mean you are required to pass it around and store it as a pointer. You CAN pass it around and store it as a reference as well. Polymorphism works with pointers AND references. And using a reference would indeed solve your nullptr issue, eg:

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

    void someFunction()
    {
        const auto result = object.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;
};

Online Demo

As long as c and d outlive the ObjectDisplay objects in displays, you will be just fine, whether you use pointers or references.

  • Related