析构函数误删元素:Texture实例#5被意外删除的技术问题
Hey there, let's break down exactly what's causing your texture ID #5 to get deleted unexpectedly, and walk through the solutions to fix it.
The Root Problem: Shallow Copy + Resource Double-Free
Your Texture class manages an OpenGL texture resource (via GLuint m_texture), but you haven't defined copy constructors or copy assignment operators. C++ automatically generates default versions of these, which do a shallow copy—they just copy the value of m_texture (the texture ID) between objects, not create a new OpenGL texture or track how many objects are using the same ID.
Here's what happens in your failing case:
- You create a temporary
Texture(0, 102, 153)object, which generates texture ID #5. - You pass this temporary to
SetTexture(Texture texture)—since it's pass-by-value, a copy of the temporary is made (both temp and parameter hold ID #5). m_texture = texturedoes another shallow copy, so nowm_texturealso holds ID #5.- The function ends: the parameter
textureis destroyed, calling~Texture()which deletes ID #5 viaglDeleteTextures. - Now
m_textureholds a stale, deleted texture ID—any use of it will fail, and whenm_textureis destroyed later, it will try to delete ID #5 again (undefined behavior).
The "working" case with a named Texture texture(...) only seems to work because the named object is still alive after SetTexture returns—ID #5 isn't deleted until that named object is destroyed, but you still have a hidden double-free waiting for when both m_texture and the named object are destroyed.
Solutions to Fix This
Option 1: Disable Copying (If You Don't Need Shared Textures)
If your Texture objects shouldn't be copied at all, explicitly disable copy operations to prevent accidental shallow copies:
class Texture { public: Texture(const std::string& fileName); Texture(int red, int green, int blue); void Bind(); virtual ~Texture(); // Disable copy constructor and assignment Texture(const Texture&) = delete; Texture& operator=(const Texture&) = delete; private: void Init(unsigned char* imageData, int width, int height); GLuint m_texture; };
Then update SetTexture to take a reference or move semantics:
// Pass by const reference (no copy made) inline void SetTexture(const Texture& texture) { // Adjust your design to hold a reference/pointer (ensure texture outlives m_texture) } // Or use move semantics (transfer ownership of the texture) inline void SetTexture(Texture&& texture) { m_texture = std::move(texture); }
Option 2: Implement Reference Counting (For Shared Textures)
If you need multiple Texture objects to share the same OpenGL texture, add a reference counter to track how many objects are using the texture ID. Only delete the texture when the last object is destroyed.
Update your Texture class:
#include <memory> // For std::shared_ptr class Texture { public: Texture(const std::string& fileName); Texture(int red, int green, int blue); Texture(const Texture& other); Texture& operator=(const Texture& other); void Bind(); virtual ~Texture(); private: void Init(unsigned char* imageData, int width, int height); GLuint m_texture; std::shared_ptr<int> m_refCount; // Tracks number of owners };
Implement the copy operations and update constructors/destructor:
// Constructor initialization Texture::Texture(const std::string& fileName) : m_refCount(std::make_shared<int>(1)) { int width, height, numComponents; unsigned char* imageData = stbi_load((fileName).c_str(), &width, &height, &numComponents, 4); if (imageData == NULL) std::cerr << "Texture loading failed for texture: " << fileName << std::endl; Init(imageData, width, height); stbi_image_free(imageData); } Texture::Texture(int red, int green, int blue) : m_refCount(std::make_shared<int>(1)) { unsigned char imageData[] = { static_cast<unsigned char>(red), static_cast<unsigned char>(green), static_cast<unsigned char>(blue) }; Init(imageData, 1, 1); } // Copy constructor Texture::Texture(const Texture& other) : m_texture(other.m_texture), m_refCount(other.m_refCount) { (*m_refCount)++; // Increment reference count } // Copy assignment operator Texture& Texture::operator=(const Texture& other) { if (this != &other) { // Decrement count for current texture; delete if no owners left (*m_refCount)--; if (*m_refCount == 0) { glDeleteTextures(1, &m_texture); } // Take ownership of the other texture's ID and reference count m_texture = other.m_texture; m_refCount = other.m_refCount; (*m_refCount)++; } return *this; } // Destructor Texture::~Texture() { (*m_refCount)--; if (*m_refCount == 0) { glDeleteTextures(1, &m_texture); } }
Option 3: Use Move Semantics (Transfer Texture Ownership)
If you want to transfer ownership of a texture from one object to another (instead of sharing), implement move constructors and move assignment operators. This lets temporary objects pass their texture ID to m_texture without copying, and the temporary object won't delete the texture.
Update your Texture class:
class Texture { public: Texture(const std::string& fileName); Texture(int red, int green, int blue); Texture(Texture&& other) noexcept; // Move constructor Texture& operator=(Texture&& other) noexcept; // Move assignment void Bind(); virtual ~Texture(); // Disable copying if ownership transfer is your only use case Texture(const Texture&) = delete; Texture& operator=(const Texture&) = delete; private: void Init(unsigned char* imageData, int width, int height); GLuint m_texture; };
Implement move operations and update the destructor:
// Move constructor Texture::Texture(Texture&& other) noexcept : m_texture(other.m_texture) { other.m_texture = 0; // Reset source object so it doesn't delete the texture } // Move assignment operator Texture& Texture::operator=(Texture&& other) noexcept { if (this != &other) { // Delete current texture if it exists if (m_texture != 0) { glDeleteTextures(1, &m_texture); } // Transfer ownership m_texture = other.m_texture; other.m_texture = 0; } return *this; } // Destructor: only delete if texture ID is valid Texture::~Texture() { if (m_texture != 0) { glDeleteTextures(1, &m_texture); } }
Now your original SetTexture will work correctly with temporary objects—std::move will transfer ownership from the temporary to m_texture:
inline void SetTexture(Texture texture) { m_texture = std::move(texture); }
Final Recommendation
- Use move semantics if you just need to pass textures around without sharing (most efficient, cleanest for temporary objects).
- Use reference counting if you need multiple objects to share the same texture.
- Use disable copying if textures should never be copied, and adjust your code to use references/pointers instead.
内容的提问来源于stack exchange,提问作者Freddy C.




