C++ using vector as static container for class instances

0

I use a vector as static member of a class to count all the instance of the class itself and its derived classes. However When trying to resize the container I get a stack-overflow thrown from the vector itself.

 // initialize static values:
    auto_ptr<HandleManager> ID3OBJ::HM = auto_ptr<HandleManager>(new HandleManager());
    auto_ptr<vector<shared_ptr<ID3OBJ>>> ID3OBJ::ObjectList = auto_ptr<vector<shared_ptr<ID3OBJ>>>(new vector<shared_ptr<ID3OBJ>>{});

I initialize the static member as empty as shown above.

// constructors:
        ID3OBJ::ID3OBJ(double x, double y, double z) : X(x), Y(y), Z(z), Handle(this->HM->addHandle()) { ObjectList->push_back(auto_ptr<ID3OBJ>(this));}
        ID3OBJ::ID3OBJ() : X(0), Y(0), Z(0), Handle(this->HM->addHandle()) { ObjectList->push_back(shared_ptr<ID3OBJ>(this));}

        Vector::Vector(double x, double y, double z) { X = x; Y = y; Z = z;
        ObjectList->push_back(auto_ptr<Vector>(this));}
        Vector::Vector() {
            X = 0; Y = 0; Z = 0;
            ObjectList->push_back(shared_ptr<Vector>(this));}

The constructors add any new instance to the instancelist, which is called ObjectList. This is working as intended.

// deconstructors:
    ID3OBJ::~ID3OBJ()
    {
        string tempH = this->Handle;
        auto iter = ObjectList->end();
        if (ObjectList->size() == HM->assignedHandles())
        {
            iter = remove_if(ObjectList->begin(), ObjectList->end(), [&](shared_ptr<ID3OBJ> ptr) {return ptr->getHandle() == tempH; });
        }
        ObjectList->erase(iter, ObjectList->end());
        this->HM->removeHandle(this->Handle);
    }

    Vector::~Vector()
    {
        string tempH = this->Handle;
        auto iter = ObjectList->end();
        if (ObjectList->size() == HM->assignedHandles())
        {
            iter=remove_if(ObjectList->begin(), ObjectList->end(), [&](shared_ptr<ID3OBJ> ptr) {return ptr->getHandle() == tempH; });
        }
        ObjectList->erase(iter, ObjectList->end());
    }

As far as I understand remove_if replaces any occurances whose pred returns true with the element after the occurance. Means if the pred with vec[3] as argument returns true, vec[2] points to vec[4] instead of vec[3]. Hence the erase function is needed to shorten the container length, however as soon as I implement this shortening an error occurs.

Header File:

// class-name:      ID3OBJ
    // Date:            30.01.2017
    // Version:         1.0
    // Description:     The class works as base class for all used 3D-Objects, and defines the operations all 3D-Objects have, namely the Direction in case of a vector, or origion in all other cases
    //

    class ID3OBJ
    {
    public:
        double X;
        double Y;
        double Z;
        static auto_ptr<vector<shared_ptr<ID3OBJ>>> ObjectList;

        ID3OBJ(double x, double y, double z);
        ID3OBJ();
        ~ID3OBJ();
        const string getHandle();

    protected:
        string Handle;
        static auto_ptr<HandleManager> HM;
    };

    // class-name:      I3DM
    // Date:            23.03.2017
    // Version:         1.0
    // Description:     The class works as Interface for classes which can do Vector-operations 
    //
    template <class T> class I3DM : public virtual ID3OBJ
    {
    public:
        using ID3OBJ::X;
        using ID3OBJ::Y;
        using ID3OBJ::Z;

        static auto_ptr<vector<shared_ptr<T>>> ObjectList;
    protected:
        using ID3OBJ::Handle;
        using ID3OBJ::HM;
    };
    // class-name:      Vector
    // Date:            30.01.2017
    // Version:         1.0
    // Description:     The class works as vector, it provides an interface to acces and modify vectors, aswell as most of the vector operations
    //
    class Vector : public virtual I3DM<Vector>
    { 
    public:
        using I3DM<Vector>::X;
        using I3DM<Vector>::Y;
        using I3DM<Vector>::Z;
        using I3DM<Vector>::ObjectList;
        Vector(double x, double y, double z);
        Vector();
        ~Vector();
    //I'm not sure if the protected members have to be provided aswell in the header file
    protected:
        using ID3OBJ::Handle;
        using ID3OBJ::HM;
    };

HM-header:

class HandleManager
    {
    public:
        HandleManager();
        const int assignedHandles();
        const string addHandle();
        void removeHandle(string hexstring);
    protected:
        int AssignedHandles;
        forward_list<int> FreeHandles;
        bool FreeHandlesAvailable;
    };

CPP:

const int string_to_hex(string s)
{
    int returnvalue;
    stringstream stream;
    stream << hex << s;
    stream >> returnvalue;
    return returnvalue;
}
HandleManager::HandleManager()
{
    this->FreeHandlesAvailable = false;
    this->AssignedHandles = 0;
}
const int HandleManager::assignedHandles()
{
    return this->AssignedHandles;
}
const string HandleManager::addHandle()
{
    string returnValue;
    if (this->FreeHandlesAvailable)
    {
        returnValue = int_to_hex(this->FreeHandles.front());
        this->FreeHandles.pop_front();
        this->AssignedHandles++;
        if (this->FreeHandles.empty()) { this->FreeHandlesAvailable = false; }
    }
    else
    {
        returnValue = int_to_hex(this->AssignedHandles);
        this->AssignedHandles++;
        if (this->AssignedHandles == 1) { returnValue = int_to_hex((int)0); }
        }
    return returnValue;
}
void HandleManager::removeHandle(string hexstring)
{
    this->FreeHandlesAvailable = true;
    this->FreeHandles.push_front(string_to_hex(hexstring));
    this->AssignedHandles--;
}

error message:

Unhandled exception at 0x00C01899 in RVE.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x01002F48). occurred

The parameter 0x00000001 is most likely a handle, is there any way to search for the object which has the memory adress given? (0x01002F48)

c++
vector
asked on Stack Overflow Sep 17, 2017 by Y.S

2 Answers

2

Consider what happens when you remove an ID3OBJ from the vector:

  1. The vector will destroy the auto_ptr<ID3OBJ>...
  2. Which will try to destroy the ID3OBJ...
  3. Whose destructor will try to remove the auto_ptr<ID3OBJ> from the vector...
  4. Which will destroy the auto_ptr<ID3OBJ>... and we go back to step 1.

This process will recurse until the stack is overflowed. The same is true for the Vector vector too. (Roger, Roger. What's your vector, Victor?)

The standard library vector is not designed to be re-entrant; if a member of vector winds up calling itself, the behavior is undefined. In your case, vector::erase() indirectly calls itself through your destructors.

Therefore, your program's behavior is undefined.

answered on Stack Overflow Sep 17, 2017 by cdhowie
1

auto_ptr<..>(this), shared_ptr<..>(this) or unique_ptr<..>(this) is never correct and a bug waiting to happen. Smart pointers take values from allocated memory, this is pointer to an object and you do not know how it came about. You are effectively doing something like the following,

int a;
auto_ptr< int > ap0( &a );   // certain death.
shared_ptr< int > ap1( &a ); // also certain death.

or equally bad,

auto_ptr< int > ap( new int );
auto_ptr< int > ap0( ap.get() );   // certain death.
shared_ptr< int > ap1( ap.get() ); // also certain death.

It is hard to understand what you are trying to achieve. If you simply want to track instances your vector should definitely be raw pointers. If your purpose is memory management, then I can't see how this could work from your code.

answered on Stack Overflow Sep 18, 2017 by Justin Finnerty

User contributions licensed under CC BY-SA 3.0