尝试在 unordered_map 中擦除时双重释放或损坏
double free or corruption when trying to erase in unordered_map
我有一个 class 块继承自 class 案例:
class Case {
public:
Case(sf::Vector2f const& pos, sf::IntRect const& textureRect, int type = 1);
protected:
int type_;
sf::Vector2f pos_;
sf::FloatRect hitBox_;
sf::IntRect textureRect_;
};
class Block : public Case {
public:
Block(sf::Texture* const& texture, sf::Vector2f const& pos, sf::IntRect const& textureRect, int const& type = 2);
sf::Sprite& getSprite();
private:
std::shared_ptr<sf::Texture> texture_;
sf::Sprite sprite_;
};
(两个构造函数都很基础,我没有在任何地方使用任何新的构造函数)
我有一张 unordered_map 的无序地图来储存我的积木:
std::unordered_map<int, std::unordered_map<int, Block>> blocks_;
但是当我尝试删除一个时:
if(blocks_[i%lenght].find((i-i%lenght)/lenght) != blocks_[i%lenght].end())
blocks_[i%lenght].erase((i-i%lenght)/lenght);
我收到那些错误:
double free or corruption (out)
我尝试打印析构函数,但在出现此错误之前只调用了 Block 中的析构函数。
我已经找了大约 2 个小时的解决方案所以我终于在这里问了,谢谢!
你的问题出在构造函数上:
Block::Block(sf::Texture *const &texture, sf::Vector2f const &pos,
sf::IntRect const &textureRect, int const &type)
: Case(pos, textureRect, 2), texture_(texture),
sprite_(*texture_, textureRect_) {}
虽然这样写不一定是错的,但如果将同一个Texture传递给多个Blocks就错了:
sf::Texture *tex = new sf::Texture(/* ... params ... */);
Block b1(tex, /* ... remaining params ... */);
Block b2(tex, /* ... remaining params ... */);
现在两个独立的 shared_ptr
认为他们是 tex
的唯一所有者,所以一旦其中一个块被删除,纹理也会被删除,所以如果两个块都被删除那么你有一个双倍免费。
从那时起,这被认为是一种反模式。一般来说,如果您使用智能指针,那么您应该将原始指针视为不拥有指针,并且您永远不应该从不拥有指针构造智能指针。
(但是,这个规则有一个例外,如果你使用不使用智能指针的库,那么你需要检查它们 return 或接收的原始指针是否被视为欠指针如果是这样,将该原始指针转换为智能指针可能是有效的。)
您的代码应该是这样的:
Block::Block(const std::shared_ptr<sf::Texture> &texture, sf::Vector2f const &pos,
sf::IntRect const &textureRect, int const &type)
: Case(pos, textureRect, 2), texture_(texture),
sprite_(*texture_, textureRect_) {}
你应该像这样构造你的Blocks
:
std::shared_ptr<Texture> tex = std::make_shared<Texture>(/* ... params ... */);
Block b1(tex, /* ... remaining params ... */);
Block b2(tex, /* ... remaining params ... */);
这并不意味着您永远不应该使用原始指针。如果你例如会有一个将纹理绘制到屏幕上的函数,那么原始指针就完全没问题了:
void draw_texture( sf::Texture * const tex) {
// do some drawing but don't store tex somewhere
// so tex is only used within that draw_texture call
}
然后你会这样称呼它:
std::shared_ptr<Texture> tex = std::make_shared<Texture>(/* ... params ... */);
draw_texture(tex.get());
我有一个 class 块继承自 class 案例:
class Case {
public:
Case(sf::Vector2f const& pos, sf::IntRect const& textureRect, int type = 1);
protected:
int type_;
sf::Vector2f pos_;
sf::FloatRect hitBox_;
sf::IntRect textureRect_;
};
class Block : public Case {
public:
Block(sf::Texture* const& texture, sf::Vector2f const& pos, sf::IntRect const& textureRect, int const& type = 2);
sf::Sprite& getSprite();
private:
std::shared_ptr<sf::Texture> texture_;
sf::Sprite sprite_;
};
(两个构造函数都很基础,我没有在任何地方使用任何新的构造函数)
我有一张 unordered_map 的无序地图来储存我的积木:
std::unordered_map<int, std::unordered_map<int, Block>> blocks_;
但是当我尝试删除一个时:
if(blocks_[i%lenght].find((i-i%lenght)/lenght) != blocks_[i%lenght].end())
blocks_[i%lenght].erase((i-i%lenght)/lenght);
我收到那些错误:
double free or corruption (out)
我尝试打印析构函数,但在出现此错误之前只调用了 Block 中的析构函数。
我已经找了大约 2 个小时的解决方案所以我终于在这里问了,谢谢!
你的问题出在构造函数上:
Block::Block(sf::Texture *const &texture, sf::Vector2f const &pos,
sf::IntRect const &textureRect, int const &type)
: Case(pos, textureRect, 2), texture_(texture),
sprite_(*texture_, textureRect_) {}
虽然这样写不一定是错的,但如果将同一个Texture传递给多个Blocks就错了:
sf::Texture *tex = new sf::Texture(/* ... params ... */);
Block b1(tex, /* ... remaining params ... */);
Block b2(tex, /* ... remaining params ... */);
现在两个独立的 shared_ptr
认为他们是 tex
的唯一所有者,所以一旦其中一个块被删除,纹理也会被删除,所以如果两个块都被删除那么你有一个双倍免费。
从那时起,这被认为是一种反模式。一般来说,如果您使用智能指针,那么您应该将原始指针视为不拥有指针,并且您永远不应该从不拥有指针构造智能指针。 (但是,这个规则有一个例外,如果你使用不使用智能指针的库,那么你需要检查它们 return 或接收的原始指针是否被视为欠指针如果是这样,将该原始指针转换为智能指针可能是有效的。)
您的代码应该是这样的:
Block::Block(const std::shared_ptr<sf::Texture> &texture, sf::Vector2f const &pos,
sf::IntRect const &textureRect, int const &type)
: Case(pos, textureRect, 2), texture_(texture),
sprite_(*texture_, textureRect_) {}
你应该像这样构造你的Blocks
:
std::shared_ptr<Texture> tex = std::make_shared<Texture>(/* ... params ... */);
Block b1(tex, /* ... remaining params ... */);
Block b2(tex, /* ... remaining params ... */);
这并不意味着您永远不应该使用原始指针。如果你例如会有一个将纹理绘制到屏幕上的函数,那么原始指针就完全没问题了:
void draw_texture( sf::Texture * const tex) {
// do some drawing but don't store tex somewhere
// so tex is only used within that draw_texture call
}
然后你会这样称呼它:
std::shared_ptr<Texture> tex = std::make_shared<Texture>(/* ... params ... */);
draw_texture(tex.get());