C++ 内存访问冲突仅发生在列表中,而不是相同操作的向量
C++ Memory Access Violation occurring with List only, not Vectors for Same Operations
我一直在创建一个 C++ Vulkan 程序,它使用子父 class 系统来管理资源并自动以正确的顺序释放它们。例如,考虑下面的代码 -
/**
* Forward declaration of parent class
*/
class ParentResource;
/**
* Represents a single child resource, all child resources must inherit from this class
*/
class ChildResource {
protected:
/**
* Creates an uninitialized resource, if this constructor is used then
* the init method MUST be called if the derived object is ever initialized
*/
ChildResource() {}
/**
* Adds a child resource to the list of child resources to be managed
* @param parent The parent resource managing this resource
*/
ChildResource(ParentResource& parent) : parentPtr(&parent) {
parent.resources.push_back(this);
}
/**
* Adds a child resource to the list of child resources to be managed
* @param parent The parent resource managing this resource
*/
virtual void init(ParentResource& parent) {
parentPtr = &parent;
if (std::find(parent.resources.begin(), parent.resources.end(), this) == parent.resources.end())
parent.resources.push_back(this);
}
/**
* Removes the child resource from the list of child resources to be managed
*/
~ChildResource() {
if (parentPtr == nullptr) return;
ParentResource& parent = *parentPtr;
parent.resources.erase(std::remove(parent.resources.begin(), parent.resources.end(), this),
parent.resources.end());
}
/**
* The pointer to the parent
*/
ParentResource* parentPtr = nullptr;
friend class ParentResource;
public:
/**
* Frees resources if not already freed
*/
virtual void freeResources() {
if (parentPtr == nullptr) return;
ParentResource& parent = *parentPtr;
parent.resources.erase(std::remove(parent.resources.begin(), parent.resources.end(), this),
parent.resources.end());
}
};
/**
* Forward declaration of child resource
*/
class ChildResource;
/**
* Represents a single parent resource which manages several child resources
*/
class ParentResource {
protected:
/**
* All child resources being managed by this parent resource
*/
std::vector<ChildResource*> resources;
friend class ChildResource;
public:
/**
* Frees all children resources
*/
virtual ~ParentResource() {
freeResources();
}
/**
* Frees all children resources
*/
virtual void freeResources() {
for (ChildResource* resource : resources) { // Memory Access Violation when using std::vector
try {
resource->freeResources();
}
catch (const std::exception& e) {
console.error(e.what());
}
}
resources.clear();
}
};
此设置用于自动释放依赖于某些 vulkan 对象的资源,例如设备 class 将从父级 class 继承,以及依赖设备的东西(交换链、图形管线、着色器等)从父级设置为设备的子级继承。
以上大部分只是背景,这个问题并不是真正关于 Vulkan,而是 C++ list
stl,因此我没有添加 Vulkan 标签。我的问题出现的地方是我在父 class.
的析构函数中不断遇到内存访问冲突
在调试时我发现列表中不知何故出现了一个 nullptr,这让我很困惑。更让我困惑的是,当我添加以下行时,崩溃仍然继续 -
if (resource == nullptr) continue;
然后将 Parent
class 子列表从 std::list
更改为 std::vector
,代码运行没有任何问题。
我不喜欢使用只是“神奇地工作”并且未能理解为什么这有效以及为什么列表没有的代码的概念。我相信链表是这种特定情况下的高级数据结构,并希望继续使用它们。
有人可以解释为什么 std::list 会导致内存访问冲突而 std::vector 不会吗?提前致谢!
TLDR - Lists don't support removing items during iterations but somehow vectors do?
正如 Andy Newman 在 中所述,问题是在循环时修改容器。我不确定它为什么用 std::vector
编译,但我能够在 MSVC 上复制相同的行为(原始 Post 使用 Apple Clang)。解决方案是简单地向 freeResources()
函数添加一个布尔参数,只有父 class 的 freeResources()
会设置为 false。如果设置为 false,此参数将停止修改列表。下面的代码示例 -
// Parent Class
virtual void freeResources() {
for (ChildResource* resource : resources) { // Memory Access Violation when using std::vector
try {
resource->freeResources(false);
}
catch (const std::exception& e) {
console.error(e.what());
}
}
resources.clear();
}
// Child Class
/**
* Frees resources if not already freed
* @param remove Whether or not to remove this from the list
*/
virtual void freeResources(bool remove = true) {
if (!remove || parentPtr == nullptr) return;
parentPtr->resources.remove(this);
}
不需要显示额外的代码,这可能不是一个完全可重现的例子,对此我深表歉意(并且将来会避免),但它肯定足以发现问题,因为 Andy Newman 能够做到正在显示。
感谢所有尝试 help/took 花时间阅读本文的人!
我一直在创建一个 C++ Vulkan 程序,它使用子父 class 系统来管理资源并自动以正确的顺序释放它们。例如,考虑下面的代码 -
/**
* Forward declaration of parent class
*/
class ParentResource;
/**
* Represents a single child resource, all child resources must inherit from this class
*/
class ChildResource {
protected:
/**
* Creates an uninitialized resource, if this constructor is used then
* the init method MUST be called if the derived object is ever initialized
*/
ChildResource() {}
/**
* Adds a child resource to the list of child resources to be managed
* @param parent The parent resource managing this resource
*/
ChildResource(ParentResource& parent) : parentPtr(&parent) {
parent.resources.push_back(this);
}
/**
* Adds a child resource to the list of child resources to be managed
* @param parent The parent resource managing this resource
*/
virtual void init(ParentResource& parent) {
parentPtr = &parent;
if (std::find(parent.resources.begin(), parent.resources.end(), this) == parent.resources.end())
parent.resources.push_back(this);
}
/**
* Removes the child resource from the list of child resources to be managed
*/
~ChildResource() {
if (parentPtr == nullptr) return;
ParentResource& parent = *parentPtr;
parent.resources.erase(std::remove(parent.resources.begin(), parent.resources.end(), this),
parent.resources.end());
}
/**
* The pointer to the parent
*/
ParentResource* parentPtr = nullptr;
friend class ParentResource;
public:
/**
* Frees resources if not already freed
*/
virtual void freeResources() {
if (parentPtr == nullptr) return;
ParentResource& parent = *parentPtr;
parent.resources.erase(std::remove(parent.resources.begin(), parent.resources.end(), this),
parent.resources.end());
}
};
/**
* Forward declaration of child resource
*/
class ChildResource;
/**
* Represents a single parent resource which manages several child resources
*/
class ParentResource {
protected:
/**
* All child resources being managed by this parent resource
*/
std::vector<ChildResource*> resources;
friend class ChildResource;
public:
/**
* Frees all children resources
*/
virtual ~ParentResource() {
freeResources();
}
/**
* Frees all children resources
*/
virtual void freeResources() {
for (ChildResource* resource : resources) { // Memory Access Violation when using std::vector
try {
resource->freeResources();
}
catch (const std::exception& e) {
console.error(e.what());
}
}
resources.clear();
}
};
此设置用于自动释放依赖于某些 vulkan 对象的资源,例如设备 class 将从父级 class 继承,以及依赖设备的东西(交换链、图形管线、着色器等)从父级设置为设备的子级继承。
以上大部分只是背景,这个问题并不是真正关于 Vulkan,而是 C++ list
stl,因此我没有添加 Vulkan 标签。我的问题出现的地方是我在父 class.
在调试时我发现列表中不知何故出现了一个 nullptr,这让我很困惑。更让我困惑的是,当我添加以下行时,崩溃仍然继续 -
if (resource == nullptr) continue;
然后将 Parent
class 子列表从 std::list
更改为 std::vector
,代码运行没有任何问题。
我不喜欢使用只是“神奇地工作”并且未能理解为什么这有效以及为什么列表没有的代码的概念。我相信链表是这种特定情况下的高级数据结构,并希望继续使用它们。
有人可以解释为什么 std::list 会导致内存访问冲突而 std::vector 不会吗?提前致谢!
TLDR - Lists don't support removing items during iterations but somehow vectors do?
正如 Andy Newman 在 std::vector
编译,但我能够在 MSVC 上复制相同的行为(原始 Post 使用 Apple Clang)。解决方案是简单地向 freeResources()
函数添加一个布尔参数,只有父 class 的 freeResources()
会设置为 false。如果设置为 false,此参数将停止修改列表。下面的代码示例 -
// Parent Class
virtual void freeResources() {
for (ChildResource* resource : resources) { // Memory Access Violation when using std::vector
try {
resource->freeResources(false);
}
catch (const std::exception& e) {
console.error(e.what());
}
}
resources.clear();
}
// Child Class
/**
* Frees resources if not already freed
* @param remove Whether or not to remove this from the list
*/
virtual void freeResources(bool remove = true) {
if (!remove || parentPtr == nullptr) return;
parentPtr->resources.remove(this);
}
不需要显示额外的代码,这可能不是一个完全可重现的例子,对此我深表歉意(并且将来会避免),但它肯定足以发现问题,因为 Andy Newman 能够做到正在显示。
感谢所有尝试 help/took 花时间阅读本文的人!