You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

类实例生命周期异常及拷贝构造函数正确实现咨询

纹理加载与拷贝构造问题排查与修复

问题场景

需求是从指定目录加载所有图片为纹理并存入列表,实现代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:35:31