Search code examples
c++inheritanceobject-slicing

Member variables and object slicing


This answer suggests that object slicing in vectors can be overcome with the use of pointers. From my testing, this is true when working with variables shared between the base and derived classes. For example, given these classes:

class Base {
public:
     int x = 1;
};

class Derived : public Base {
public:
    Derived() : Base() { 
        x = 2;
    }
    int y;
};

it can be seen that Derived redefines x to be 2 upon construction. When placed into a vector of the Base class, the x variable behaves as expected.

vector<unique_ptr<Base>> baseVec;               // Vector of pointers to Base objects
unique_ptr<Derived> derivedPtr(new Derived());  // Pointer to Derived object
baseVec.push_back(std::move(derivedPtr));       // Add pointer with std::move()
std::cout << baseVec[0]->x << std::endl;        // Outputs 2

However, attempting to use the y variable belonging only to Derived leads to an error, C2039: 'y' is not a member of 'Base' as seen here:

std::cout << baseVec[0]->y << std::endl;

Is there a way to bypass this issue while preserving member variables unique to derivative classes, and if not is there a superior way to store objects in an ordered container?

Edit

Relevant contents of the previous edit have been moved to my answer below. I do not plan to accept the answer until I can find a way to use smart pointers (or ensure that my raw pointer implementation does not have memory issues).


Solution

  • Changing the classes

    The class definitions for Base and Derived should be changed to:

    class Base {
    public:
        int x = 1;
    
        virtual void f() { }
    };
    
    class Derived : public Base {
    public:
        Derived() : Base() { x = 2; }
        int y = 55;
    
        void f() { }
    };
    

    The notable addition is the virtual function void f() to allow dynamic_cast to handle casting the pointers.

    Using unique_ptr

    Unique pointers can be used to store, retrieve, modify and safely delete pointers to Derived objects residing within a vector of pointers to Base objects. The example below uses heap-allocated objects.

    /* Insertion */
    // Create a vector of unique_ptr to Base and add a unique_ptr to Derived to it
    std::vector<std::unique_ptr<Base>> v;
    std::unique_ptr<Derived> p1(new Derived());
    v.push_back(std::move(p1));
    
    /* Retrieval */
    // Release the pointer at base to place it into p2
    std::unique_ptr<Derived> p2(dynamic_cast<Derived*>(v[0].release()));
    
    /* Modification */
    p2->x = 0xff;    // Modify x (Decimal 255)
    p2->y = 0xffff;  // Modify y (Decimal 65535)
    int y = p2->y;   // Copy the value of the Derived object's y to an int for use later
    
    // Move the pointer to the modified object back to v
    v[0] = std::move(p2); 
    
    /* Output */
    std::cout << v[0]->x << std::endl; // Outputs 255
    std::cout << y << std::endl;       // OUtputs 65535
    
    /* Delete */
    // To safely delete the object, it must be treated as a pointer to a Derived object
    // Thus it must be released from the control of v and deleted via p3, which is cast in the same manner as p2 was
    std::unique_ptr<Derived> p3(dynamic_cast<Derived*>(v[0].release()));
    p3.reset(); // This deletes the object as well, not just the reference to it!
    

    Functions as expected and outputs 255 and 65535.