类实例生命周期异常及拷贝构造函数正确实现咨询
纹理加载与拷贝构造问题排查与修复
问题场景
需求是从指定目录加载所有图片为纹理并存入列表,实现代码如下:
for (const auto & entry : fs::directory_iterator(path)) { auto x = path + "/" + std::filesystem::path(entry).filename().string(); auto y = x.c_str(); Texture text(y); inGameTextures.push_back(text); }
实际运行时,先加载的图片会被后续加载的覆盖,且纹理完全无法显示。尝试添加拷贝、移动构造函数后问题仍未解决:
Texture(const char* path); Texture(const Texture& other) : texture(other.texture){}; Texture& operator=(const Texture & that) {texture = that.texture;}; Texture(Texture &&) noexcept = default; Texture& operator=(Texture &&) noexcept = default; ~Texture();
核心问题分析
从提供的Texture类实现来看,有两个致命问题:
- 静态成员
textureData误用:类内的data textureData是全局静态变量,所有Texture实例共享同一数据。每次创建新实例时textureData.ID = LoadImage(path)都会覆盖之前的值,这就是先加载图片被覆盖的直接原因。 - 拷贝构造浅拷贝问题:当前拷贝构造仅复制
texture句柄(OpenGL纹理ID),但OpenGL纹理是资源句柄,浅拷贝会导致多个实例指向同一纹理资源,析构时可能重复释放,同时无法解决静态数据覆盖问题。
解决方案
1. 修复静态成员问题
将静态的textureData改为类的非静态成员,让每个Texture实例拥有独立数据:
// 头文件修改 class Texture { private: unsigned int texture = 0; data textureData; // 替换原静态成员 public: // 删除静态声明,改为成员函数 data& GetTextureData(); void SetData(const char* path); // ... 其他成员保持不变 };
// cpp文件修改,删除全局textureData变量 Texture::Texture(const char* path) : texture(0) { textureData.ID = LoadImage(path); SetData(path); } data& Texture::GetTextureData() { return textureData; }
2. 正确实现拷贝构造与赋值重载
OpenGL纹理资源需通过引用计数管理,避免浅拷贝导致的资源冲突,实现如下:
// 头文件添加引用计数成员 class Texture { private: unsigned int texture = 0; data textureData; unsigned int* refCount = nullptr; // 引用计数指针 public: Texture(const char* path); Texture(const Texture& other); Texture& operator=(const Texture& other); Texture(Texture&& other) noexcept; Texture& operator=(Texture&& other) noexcept; ~Texture(); // ... 其他成员 };
// cpp实现 Texture::Texture(const char* path) : texture(0), refCount(new unsigned int(1)) { textureData.ID = LoadImage(path); SetData(path); } Texture::Texture(const Texture& other) : texture(other.texture), textureData(other.textureData), refCount(other.refCount) { (*refCount)++; // 引用计数+1 } Texture& Texture::operator=(const Texture& other) { if (this != &other) { // 释放当前资源 if (--(*refCount) == 0) { glDeleteTextures(1, &texture); delete refCount; } // 拷贝资源与引用计数 texture = other.texture; textureData = other.textureData; refCount = other.refCount; (*refCount)++; } return *this; } // 移动构造 Texture::Texture(Texture&& other) noexcept : texture(other.texture), textureData(std::move(other.textureData)), refCount(other.refCount) { // 置空原对象资源指针,避免重复释放 other.texture = 0; other.refCount = nullptr; } Texture& Texture::operator=(Texture&& other) noexcept { if (this != &other) { // 释放当前资源 if (refCount) { if (--(*refCount) == 0) { glDeleteTextures(1, &texture); delete refCount; } } // 转移资源 texture = other.texture; textureData = std::move(other.textureData); refCount = other.refCount; // 置空原对象 other.texture = 0; other.refCount = nullptr; } return *this; } Texture::~Texture() { if (refCount) { if (--(*refCount) == 0) { glDeleteTextures(1, &texture); delete refCount; } } }
3. 加载逻辑优化
路径拼接可直接使用filesystem的原生方法,避免手动拼接斜杠:
for (const auto & entry : fs::directory_iterator(path)) { const auto& imgPath = entry.path(); Texture text(imgPath.c_str()); inGameTextures.push_back(text); }
额外排查提示
- 检查
LoadImage函数是否每次调用都生成唯一的OpenGL纹理ID,确保不会复用旧纹理。 - 确认析构函数中是否正确调用
glDeleteTextures释放纹理资源,避免内存泄漏或资源冲突。 - 移动构造/赋值后,需确保原对象的纹理ID被置空,防止析构时重复释放资源。
内容的提问来源于stack exchange,提问作者notfynnaf
相关产品推荐
相关产品推荐

