2015-06-08 82 views
0

我正在使用C++和Allegro進行一個簡單的遊戲。我遇到一個Access violation運行時錯誤,其中包含unique_ptrsALLEGRO_BITMAPsvectorstructs複製包含unique_ptrs的結構向量

這是我的結構聲明。

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; 
} 

這是我的deleter for 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 

當我修改我的generateBasecreatePlayer呼叫通過去除Skin(white),輸出改爲。

Errors incoming! 
Starting vector copy 
00799468 
Ending vector copy 
00799468 

輸出的變化讓我有點疑惑,但我最大的問題是什麼,我需要改變我是如何複製我的unique_ptrsstructsvector所以我不嘗試刪除相同的指針兩次。

在此先感謝!

+0

1)你已經被告知[最小的完整的例子(http://stackoverflow.com/help/mcve),2)你的'Skin :: operator ='違反了'std :: unique_ptr'的規則。 – Beta

回答

3

要理解的第一件事是你只能有一個std::unique_ptr對象包含一個指向特定對象的指針。您的Skin(const Skin& s)構造函數違反了這一原則,導致unique_ptr的兩個副本。如果你有一個包含unique_ptr成員的對象,你需要做以下之一:

  1. 不是有一個拷貝構造函數或賦值操作符。
  2. 在複製構造函數和/或賦值運算符中,分配基礎資源的新副本。這需要調用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())) 
{ 
} 
1

的問題是在這裏:

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的內存。所以你的代碼必然會最終訪問/釋放已經被死亡的第一個unique_ptr釋放的內存。
Now both unique_ptr believes that they own _Ptr

必須要麼移動的unique_ptr(從而產生所有權)或顯式調用發佈上s.img