Horje
Why Can't We Declare a std::vector<AbstractClass> in C++?

In C++, Standard Template Library (STL) we have a container called std::vector, which is used for managing collections of objects. However, we might encounter difficulties when trying to declare a std::vector of an abstract class in C++.

In this article, we will learn why we cannot declare a std::vector<AbstractClass> and discuss some practical alternatives to do similar a task in some other ways.

Why Can’t We Declare a std::vector<AbstractClass>?

There are several reasons why we cannot declare a std::vector<AbstractClass>, some of which are as follows:

1. Abstract Classes Cannot Be Instantiated

Since abstract classes cannot be instantiated, we cannot create objects of an abstract class. std::vector requires creating instances of the objects it stores, which is not possible with an abstract class.

2. Copy and Assignment Operations

std::vector relies on copy and assignment operations to manage its elements. Abstract classes typically do not implement these operations, making it impossible for std::vector to manage them correctly.

3. Slicing Problem

If we try to store objects of derived classes in a std::vector<AbstractClass>, we would encounter the slicing problem that occurs when an object of a derived class is assigned to a variable of the base class type, leading to the loss of the derived class-specific attributes and methods.

Alternatives to std::vector<AbstractClass> in C++

To achieve the functionality similar to std::vector<AbstractClass>, we can use several alternatives that allow us to store and manage collections of abstract class objects effectively.

1. Using std::vector<std::unique_ptr<AbstractClass>>

Using std::unique_ptr allows us to store pointers to objects of the derived classes while maintaining ownership and avoiding memory leaks. This approach allows smart pointers to manage the lifecycle of the objects.

Example:

C++
// C++ program to use
// std::vector<std::unique_ptr<AbstractClass>>

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class AbstractClass
{
public:
    // Pure virtual function making this class abstract
    virtual void pureVirtualFunction() = 0;

    // Virtual destructor to ensure proper cleanup of
    // derived classes
    virtual ~AbstractClass() = default;
};

class DerivedClass : public AbstractClass {
public:
    // Override the pure virtual function with specific
    // implementation
    void pureVirtualFunction() override
    {
        cout << "DerivedClass implementation" << endl;
    }
};

int main()
{
    // Declare a vector of unique_ptr to AbstractClass
    vector<unique_ptr<AbstractClass> > vec;

    // Create a unique_ptr to DerivedClass and add it to the
    // vector
    vec.push_back(make_unique<DerivedClass>());

    // Iterate over the vector and call the pure virtual
    // function
    for (const auto& item : vec) {
        item->pureVirtualFunction();
    }

    return 0;
}

Output
DerivedClass implementation

2. Using std::vector<std::shared_ptr<AbstractClass>>

If we need shared ownership of the objects, we can use std::shared_ptr. This approach is useful when multiple parts of our program need to share and manage the same objects.

Example:

C++
// C++ program to use
// std::vector<std::shared_ptr<AbstractClass>>

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

using namespace std;

// Abstract base class with a pure virtual function
class AbstractClass {
public:
    // Pure virtual function
    virtual void pureVirtualFunction() = 0;
    // Virtual destructor
    virtual ~AbstractClass() = default;
};

// Derived class implementing the pure virtual function
class DerivedClass : public AbstractClass {
public:
    void pureVirtualFunction() override
    {
        // Output from DerivedClass
        cout << "DerivedClass implementation" << endl;
    }
};

int main()
{
    // Creating a vector of shared_ptr to AbstractClass
    vector<shared_ptr<AbstractClass> > vec;

    // Adding an instance of DerivedClass to the vector
    vec.push_back(make_shared<DerivedClass>());

    // Iterating over the vector and calling the pure
    // virtual function
    for (const auto& item : vec) {
        // Calling the implemented function
        item->pureVirtualFunction();
    }

    return 0;
}

Output
DerivedClass implementation

3. Using std::vector<AbstractClass*>

If we prefer manual memory management, we can use raw pointers. However, this approach requires careful handling of memory allocation and deallocation to avoid memory leaks and dangling pointers.

Example:

C++
// C++ program to use std::vector with pointers to
// AbstractClass

#include <iostream>
#include <vector>

using namespace std;

// Abstract base class with a pure virtual function
class AbstractClass {
public:
    // Pure virtual function
    virtual void pureVirtualFunction() = 0;
    // Virtual destructor
};
virtual ~AbstractClass() = default;

// Derived class implementing the pure virtual function
class DerivedClass : public AbstractClass {
public:
    void pureVirtualFunction() override
    {
        // Output from DerivedClass
        cout << "DerivedClass implementation" << endl;
    }
};

int main()
{
    // Creating a vector of pointers to AbstractClass
    vector<AbstractClass*> vec;

    // Adding an instance of DerivedClass to the vector
    vec.push_back(new DerivedClass());

    // Iterating over the vector and calling the pure
    // virtual function
    for (const auto& item : vec) {
        // Calling the implemented function
        item->pureVirtualFunction();
    }

    // Clean up memory to avoid memory leaks
    for (auto& item : vec) {
        // Deleting dynamically allocated objects
        delete item;
    }

    return 0;
}


Output

DerivedClass implementation

Conclusion

In conclusion, while we cannot declare a std::vector<AbstractClass> due to the limitations of abstract classes, using smart pointers such as std::unique_ptr or std::shared_ptr provides effective alternatives. These approaches avoid the slicing problem, ensure proper memory management, and maintain the flexibility and functionality of STL containers. By using these techniques, we can effectively manage collections of abstract class objects in your C++ programs.




Reffered: https://www.geeksforgeeks.org


C++

Related
What Requirements Must std::map Key Classes Meet to be Valid Keys? What Requirements Must std::map Key Classes Meet to be Valid Keys?
C++ 03 Standard C++ 03 Standard
Toggling Bits in C++ Toggling Bits in C++
Alternative to vector&lt;bool&gt; Alternative to vector&lt;bool&gt;
How to Remove an Item from a STL Vector with a Certain Value? How to Remove an Item from a STL Vector with a Certain Value?

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
17