C++ 使用向量作为类实例的静态容器

Posted

技术标签:

【中文标题】C++ 使用向量作为类实例的静态容器【英文标题】:C++ using vector as static container for class instances 【发布时间】:2017-09-17 12:39:51 【问题描述】:

我使用向量作为类的静态成员来计算类本身及其派生类的所有实例。但是,当尝试调整容器大小时,我会从向量本身抛出堆栈溢出。

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

如上所示,我将静态成员初始化为空。

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

构造函数将任何新实例添加到名为 ObjectList 的实例列表中。这正在按预期工作。

// 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());
    

据我了解 remove_if 用发生后的元素替换 pred 返回 true 的任何发生。表示如果以 vec[3] 作为参数的 pred 返回 true,则 vec[2] 指向 vec[4] 而不是 vec[3]。 因此,需要擦除功能来缩短容器长度,但是一旦我实施这种缩短,就会发生错误。

头文件:

// 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-标头:

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

错误信息:

RVE.exe 中 0x00C01899 处的未处理异常:0xC00000FD:堆栈溢出(参数:0x00000001、0x01002F48)。发生了

参数 0x00000001 很可能是一个句柄,有没有办法搜索给定内存地址的对象? (0x01002F48)

【问题讨论】:

从不鼓励使用std::auto_ptr。自 C++11 标准以来,它也已被弃用,并在 C++17 标准中被删除.. 好的,所以我将不得不使用常规指针并在使用后清理它,但我怀疑这与我的错误有关 @Y.S 或者改用std::unique_ptr minimal reproducible example 的“最小”方面可能需要一些工作。 抛出异常时不应该得到堆栈跟踪吗?此外,您似乎将不同类型的智能指针存储到同一个容器中:ObjectList-&gt;push_back(auto_ptr&lt;Vector&gt;(this));ObjectList-&gt;push_back(shared_ptr&lt;Vector&gt;(this));。这甚至是编译器吗?也没有必要“使用向量作为类的静态成员来计算类本身及其派生类的所有实例”,只需size_t 就足够了。 【参考方案1】:

考虑当您从向量中删除 ID3OBJ 时会发生什么:

    向量会破坏auto_ptr&lt;ID3OBJ&gt;... 这会试图破坏ID3OBJ... 谁的析构函数会尝试从向量中删除auto_ptr&lt;ID3OBJ&gt;... 这会破坏auto_ptr&lt;ID3OBJ&gt;...,然后我们回到第 1 步。

此过程将递归,直到堆栈溢出。 Vector 向量也是如此。 (罗杰,罗杰。维克多,你的向量是什么?)

标准库vector 并非设计为可重入的;如果vector 的成员最终调用自己,则行为未定义。在您的情况下,vector::erase() 通过您的析构函数间接调用自己。

因此,您的程序的行为是未定义的。

【讨论】:

感谢您解释这种行为,稍后我会在我的问题中添加一个解决方案来解决这个问题:)【参考方案2】:

auto_ptr<..>(this)、shared_ptr<..>(this) 或 unique_ptr<..>(this) 永远不会正确,并且等待发生的错误。智能指针从 已分配 内存中获取值,this 是指向对象的指针,您不知道它是如何产生的。您正在有效地执行以下操作,

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

或者同样糟糕,

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

很难理解您要达到的目标。如果您只是想跟踪实例,您的向量绝对应该是原始指针。如果您的目的是内存管理,那么我无法从您的代码中看到它是如何工作的。

【讨论】:

我正在使用 auto_ptr 因为如果不再使用它会自动解构,因此我不必担心其中的向量的清理。使用 new 启动向量以确保有足够的内存可用;)我必须添加this,因为我想要一个指向新生成对象的指针,并且我需要一个共享指针,因为这些指针将被各种其他对象使用。你能解释一下为什么你的榜样会导致一定的死亡吗?我的示例确实在传递对象之前启动了它,所以它是:int a = 5; shared_ptr&lt;int&gt; ap0(&amp;a); 问题是智能指针会调用delete &amp;a;,但&amp;a不是通过调用new分配的内存。智能指针不用于存储每个指针,仅用于存储从堆内存中分配的指针(即使用new。)

以上是关于C++ 使用向量作为类实例的静态容器的主要内容,如果未能解决你的问题,请参考以下文章

将 C# 数组传递给 C++ 向量

如何将对象添加到类中的静态向量

c++ 使用友好类的类型作为模板参数声明 stl 向量

C++ priority_queue 底层向量容器容量调整大小

c++向量构造函数实例化冲突

C++ 固定数量的向量大小作为类成员