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->push_back(auto_ptr<Vector>(this));
和ObjectList->push_back(shared_ptr<Vector>(this));
。这甚至是编译器吗?也没有必要“使用向量作为类的静态成员来计算类本身及其派生类的所有实例”,只需size_t
就足够了。
【参考方案1】:
考虑当您从向量中删除 ID3OBJ
时会发生什么:
-
向量会破坏
auto_ptr<ID3OBJ>
...
这会试图破坏ID3OBJ
...
谁的析构函数会尝试从向量中删除auto_ptr<ID3OBJ>
...
这会破坏auto_ptr<ID3OBJ>
...,然后我们回到第 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<int> ap0(&a);
问题是智能指针会调用delete &a;
,但&a
不是通过调用new
分配的内存。智能指针不用于存储每个指针,仅用于存储从堆内存中分配的指针(即使用new
。)以上是关于C++ 使用向量作为类实例的静态容器的主要内容,如果未能解决你的问题,请参考以下文章