复制包含 unique_ptrs 的结构向量
Copying a vector of structs containing unique_ptrs
我正在使用 C++ 和 Allegro 开发一个简单的游戏。我 运行 遇到 Access violation
运行时错误,关于 structs
的 vector
包含 unique_ptrs
到 ALLEGRO_BITMAPs
.
这是我的结构声明。
struct Skin {
std::unique_ptr<ALLEGRO_BITMAP> img;
Skin();
Skin(ALLEGRO_BITMAP*);
Skin& operator=(const Skin& s);
Skin(const Skin& s);
};
这里是另一个文件中构造函数的定义。
Skin::Skin() {
img.reset();
}
Skin::Skin(ALLEGRO_BITMAP* bitmap) {
img.reset(bitmap);
}
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
return *this;
}
这是在我的访问冲突之前调用的代码。
generateBase(world, display.get());
调用这个函数。
void generateBase(World& world, ALLEGRO_DISPLAY* display) {
int x = TILESIZE - WIDTH;
int y = HEIGHT - TILESIZE;
int groundWidth = 3 * WIDTH - 2 * TILESIZE;
Point min{ x, y };
Point max{ x + groundWidth, y + (int)TILESIZE };
ALLEGRO_BITMAP* black = al_create_bitmap(groundWidth, TILESIZE);
ALLEGRO_BITMAP* white = al_create_bitmap(groundWidth, TILESIZE);
al_set_target_bitmap(black);
al_clear_to_color(al_map_rgb(0, 0, 0));
al_set_target_bitmap(white);
al_clear_to_color(al_map_rgb(255, 255, 255));
al_set_target_bitmap(al_get_backbuffer(display));
std::cout << "Errors incoming!" << endl;
createPlayer(world, x, y, 0, 0, 5, vector < AABB > { AABB(min, max) }, vector < Skin > { Skin(black), Skin(white) });
std::cout << "Did we make it?" << endl;
}
依次调用这个函数。
unsigned int createPlayer(World& world, int x, int y, float dx, float dy, float speed, vector<AABB>& mesh, vector<Skin>& imgs) {
unsigned int entity = newEntityIndex(world);
world.masks[entity].set(COMPONENT_TYPE);
world.masks[entity].set(COMPONENT_POINT);
world.masks[entity].set(COMPONENT_UNITVECTOR);
world.masks[entity].set(COMPONENT_SPEED);
world.masks[entity].set(COMPONENT_COLLISIONMESH);
world.masks[entity].set(COMPONENT_SKINLIST);
world.types[entity] = TYPE_PLAYER;
world.points[entity] = Point(x, y);
world.unitVectors[entity] = UnitVector(dx, dy);
world.speeds[entity] = Speed(speed);
world.collisionMeshes[entity].mesh = mesh;
cout << "Starting vector copy" << endl;
for (auto skin : imgs) {
world.skinLists[entity].imgs.push_back(move(skin));
}
cout << "Ending vector copy" << endl;
return entity;
}
这是我为 unique_ptr 删除的。
namespace std {
template<>
class default_delete < ALLEGRO_BITMAP > {
public:
void operator()(ALLEGRO_BITMAP* ptr) {
cout << ptr << endl;
al_destroy_bitmap(ptr);
}
};
}
这是输出。
Errors incoming!
Starting vector copy
00AF9468
00AF9468
当我通过删除 Skin(white)
修改 generateBase
中的 createPlayer
调用时,输出更改为。
Errors incoming!
Starting vector copy
00799468
Ending vector copy
00799468
输出的变化让我有点困惑,但我最大的问题是我需要改变我如何复制 vector
of structs
of unique_ptrs
以便我不会尝试删除同一个指针两次。
提前致谢!
首先要了解的是,您只能有一个 std::unique_ptr
包含指向特定对象的指针的对象。你的 Skin(const Skin& s)
构造函数违反了这个原则,导致了一个 unique_ptr
的两个副本。如果您的对象包含 unique_ptr
个成员,则需要执行以下操作之一:
- 没有复制构造函数或赋值运算符。
- 在复制构造函数and/or赋值运算符中,分配底层资源的新副本。这需要调用
al_clone_bitmap
来复制资源。
其次,当您在 unique_ptr
中持有资源时,您希望在创建资源的同一位置初始化 unique_ptr
。例如,不要创建局部变量 ALLEGRO_BITMAP* black
,而是使用以下内容:
std::unique_ptr<ALLEGRO_BITMAP> black(al_create_bitmap(groundWidth, TILESIZE));
由于此代码直接从 al_create_bitmap
的结果创建 unique_ptr
,您需要 删除 Skin
构造函数需要 ALLEGRO_BITMAP*
并将其替换为:
Skin::Skin(std::unique_ptr<ALLEGRO_BITMAP>&& bitmap)
: img(bitmap)
{
}
然后您可以通过将 unique_ptr
移动到其中来创建 Skin
:
Skin(std::move(black))
将以上内容放在一起,一个有效的复制构造函数可能如下所示。效率不是特别高,但是很安全
Skin::Skin(const Skin& s)
: img(al_clone_bitmap(s.img.get()))
{
}
问题出在这里:
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
您正在从一个 unique_ptr 窃取原始指针并将其分配给另一个。现在 unique_ptr 属于 RAII 类别。只要他们还活着,他们就希望拥有一个对象的生命周期。
当你这样做时
img.reset(s.img.get());
你从一个unique_ptr中取出了指针,交给了另一个unique_ptr。现在 unique_ptr1 认为它拥有底层对象,却不知道还有另一个 unique_ptr2 相信相同的对象。因此,当它们死掉时,它们会很高兴地释放分配给 _Ptr 的内存。所以你的代码必然会结束 accessing/freeing 内存,它已经被第一个 unique_ptr 释放了。
您必须移动 unique_ptr(从而放弃所有权)或在 s.img
上显式调用 release
我正在使用 C++ 和 Allegro 开发一个简单的游戏。我 运行 遇到 Access violation
运行时错误,关于 structs
的 vector
包含 unique_ptrs
到 ALLEGRO_BITMAPs
.
这是我的结构声明。
struct Skin {
std::unique_ptr<ALLEGRO_BITMAP> img;
Skin();
Skin(ALLEGRO_BITMAP*);
Skin& operator=(const Skin& s);
Skin(const Skin& s);
};
这里是另一个文件中构造函数的定义。
Skin::Skin() {
img.reset();
}
Skin::Skin(ALLEGRO_BITMAP* bitmap) {
img.reset(bitmap);
}
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
return *this;
}
这是在我的访问冲突之前调用的代码。
generateBase(world, display.get());
调用这个函数。
void generateBase(World& world, ALLEGRO_DISPLAY* display) {
int x = TILESIZE - WIDTH;
int y = HEIGHT - TILESIZE;
int groundWidth = 3 * WIDTH - 2 * TILESIZE;
Point min{ x, y };
Point max{ x + groundWidth, y + (int)TILESIZE };
ALLEGRO_BITMAP* black = al_create_bitmap(groundWidth, TILESIZE);
ALLEGRO_BITMAP* white = al_create_bitmap(groundWidth, TILESIZE);
al_set_target_bitmap(black);
al_clear_to_color(al_map_rgb(0, 0, 0));
al_set_target_bitmap(white);
al_clear_to_color(al_map_rgb(255, 255, 255));
al_set_target_bitmap(al_get_backbuffer(display));
std::cout << "Errors incoming!" << endl;
createPlayer(world, x, y, 0, 0, 5, vector < AABB > { AABB(min, max) }, vector < Skin > { Skin(black), Skin(white) });
std::cout << "Did we make it?" << endl;
}
依次调用这个函数。
unsigned int createPlayer(World& world, int x, int y, float dx, float dy, float speed, vector<AABB>& mesh, vector<Skin>& imgs) {
unsigned int entity = newEntityIndex(world);
world.masks[entity].set(COMPONENT_TYPE);
world.masks[entity].set(COMPONENT_POINT);
world.masks[entity].set(COMPONENT_UNITVECTOR);
world.masks[entity].set(COMPONENT_SPEED);
world.masks[entity].set(COMPONENT_COLLISIONMESH);
world.masks[entity].set(COMPONENT_SKINLIST);
world.types[entity] = TYPE_PLAYER;
world.points[entity] = Point(x, y);
world.unitVectors[entity] = UnitVector(dx, dy);
world.speeds[entity] = Speed(speed);
world.collisionMeshes[entity].mesh = mesh;
cout << "Starting vector copy" << endl;
for (auto skin : imgs) {
world.skinLists[entity].imgs.push_back(move(skin));
}
cout << "Ending vector copy" << endl;
return entity;
}
这是我为 unique_ptr 删除的。
namespace std {
template<>
class default_delete < ALLEGRO_BITMAP > {
public:
void operator()(ALLEGRO_BITMAP* ptr) {
cout << ptr << endl;
al_destroy_bitmap(ptr);
}
};
}
这是输出。
Errors incoming!
Starting vector copy
00AF9468
00AF9468
当我通过删除 Skin(white)
修改 generateBase
中的 createPlayer
调用时,输出更改为。
Errors incoming!
Starting vector copy
00799468
Ending vector copy
00799468
输出的变化让我有点困惑,但我最大的问题是我需要改变我如何复制 vector
of structs
of unique_ptrs
以便我不会尝试删除同一个指针两次。
提前致谢!
首先要了解的是,您只能有一个 std::unique_ptr
包含指向特定对象的指针的对象。你的 Skin(const Skin& s)
构造函数违反了这个原则,导致了一个 unique_ptr
的两个副本。如果您的对象包含 unique_ptr
个成员,则需要执行以下操作之一:
- 没有复制构造函数或赋值运算符。
- 在复制构造函数and/or赋值运算符中,分配底层资源的新副本。这需要调用
al_clone_bitmap
来复制资源。
其次,当您在 unique_ptr
中持有资源时,您希望在创建资源的同一位置初始化 unique_ptr
。例如,不要创建局部变量 ALLEGRO_BITMAP* black
,而是使用以下内容:
std::unique_ptr<ALLEGRO_BITMAP> black(al_create_bitmap(groundWidth, TILESIZE));
由于此代码直接从 al_create_bitmap
的结果创建 unique_ptr
,您需要 删除 Skin
构造函数需要 ALLEGRO_BITMAP*
并将其替换为:
Skin::Skin(std::unique_ptr<ALLEGRO_BITMAP>&& bitmap)
: img(bitmap)
{
}
然后您可以通过将 unique_ptr
移动到其中来创建 Skin
:
Skin(std::move(black))
将以上内容放在一起,一个有效的复制构造函数可能如下所示。效率不是特别高,但是很安全
Skin::Skin(const Skin& s)
: img(al_clone_bitmap(s.img.get()))
{
}
问题出在这里:
Skin::Skin(const Skin& s) {
img.reset(s.img.get());
}
Skin& Skin::operator=(const Skin& s) {
img.reset(s.img.get());
您正在从一个 unique_ptr 窃取原始指针并将其分配给另一个。现在 unique_ptr 属于 RAII 类别。只要他们还活着,他们就希望拥有一个对象的生命周期。 当你这样做时
img.reset(s.img.get());
你从一个unique_ptr中取出了指针,交给了另一个unique_ptr。现在 unique_ptr1 认为它拥有底层对象,却不知道还有另一个 unique_ptr2 相信相同的对象。因此,当它们死掉时,它们会很高兴地释放分配给 _Ptr 的内存。所以你的代码必然会结束 accessing/freeing 内存,它已经被第一个 unique_ptr 释放了。
您必须移动 unique_ptr(从而放弃所有权)或在 s.img
上显式调用 release