2015-09-25 19 views
6

Tôi gặp vấn đề với một segfault mà tôi không thể tìm ra. Đó là từ số EntityManager cho một công cụ trò chơi nhỏ mà tôi đang thực hiện. Tôi có thể thêm Ship Entity và Tàu có thể thêm 1 Bullet Entity, nhưng nó sẽ phân cách nếu tôi cố gắng thêm nhiều hơn 1 Bullet. Tôi đã cố gắng để figgure này ra cho ngày hôm qua bây giờ. Dưới đây là một trích đoạn nhỏ từ mã thực tế.vectơ xóa unique_ptr?

#include <vector> 
#include <memory> 

struct EntityManager; 
struct Entity { 
    Entity(EntityManager* manager) : manager(manager) { } 
    virtual ~Entity() { } 
    virtual void update() = 0; 

    EntityManager* manager; 
}; 
struct EntityManager { 
    void update() { 
     for (auto& entity : entities) { 
      entity->update(); 
     } 
    } 
    void add(Entity* e) { 
     entities.emplace_back(e); 
    } 
    std::vector<std::unique_ptr<Entity>> entities; 
}; 
struct Bullet : public Entity { 
    Bullet(EntityManager* manager) : Entity(manager) { printf("Bullet ctor\n"); } 

    virtual void update() override { } 
}; 
struct Ship : public Entity { 
    Ship(EntityManager* manager) : Entity(manager) { } 

    virtual void update() override { 
     printf("Adding Bullet\n"); 
     manager->add(new Bullet(manager)); 
    } 
}; 
int main() { 
    EntityManager manager; 
    manager.add(new Ship(&manager)); 

    int loops{0}; 
    while (loops < 100) { 
     manager.update(); 
     loops++; 
     printf("Completed Loop #%d\n", loops); 
    } 
    return 0; 
} 

Trong mã thực tế, mọi thứ đều nằm trong tệp .h/.cpp của riêng chúng và các lớp thay vì cấu trúc, nhưng vấn đề thì giống nhau. Kết quả là `Thêm Bullet // Bullet ctor // Hoàn Vòng # 1 // Thêm Bullet // Bullet ctor // Signal: SIGSEGV (lỗi Segmentation)

Các segfault xảy ra trong EntityManager::update() trên dòng entity->update();.

+1

'EntityManager' cần các thao tác di chuyển tùy chỉnh để cập nhật con trỏ' manager' của thực thể. – dyp

+1

Khi bạn cập nhật các thực thể, vòng lặp đó sẽ thêm nhiều thực thể làm mất hiệu lực trình vòng lặp của bạn. Bạn không thể thêm vào vectơ của bạn trong khi bạn đang ở giữa vòng lặp thông qua nó. – Galik

+1

[Viết trò chơi không phải là công cụ] (http://geometrian.com/programming/tutorials/write-games-not-engines/) là một điều tốt để đọc. Điều đó nói rằng, thay vì thêm mọi thứ ngay lập tức hoặc phá hủy mọi thứ ngay lập tức, bạn có thể trì hoãn các hoạt động đó sau vòng lặp cập nhật, thông qua các sự kiện hoặc bất kỳ điều gì bạn thấy thích hợp, để bạn không làm mất hiệu lực trình vòng lặp. – aslg

Trả lời

13

Vấn đề là vòng lặp này sẽ thay đổi vector:

for (auto& entity : entities) { 
     entity->update(); 
    } 

Bạn đang bận rộn iterating qua nó khi bạn sửa đổi vector để thêm một yếu tố mới, mà làm mất hiệu lực lặp được sử dụng để đi qua các thùng chứa.

Phạm vi dựa trên for vòng lặp được mở rộng bởi trình biên dịch để:

auto begin = entities.begin(), end = entities.end(); 
for (; begin != end; ++begin) 
    begin->update(); 

Các cuộc gọi đến begin->update() thêm một yếu tố mới để vector, mà làm mất hiệu lực tất cả các vòng lặp vào container, do đó ++begin là hành vi không xác định . Trong điều khoản thực tế, begin không còn trỏ vào vectơ nữa (vì nó đã phân bổ lại và giải phóng bộ nhớ cũ mà begin trỏ tới).

Để làm điều đó một cách an toàn có thể bạn muốn sử dụng chỉ số không lặp:

for (size_t i = 0, size = entities.size(); i != size; ++i) 
    entities[i].update(); 

này ghi lại kích thước vào lúc bắt đầu của vòng lặp và vì vậy chỉ lặp lên đến yếu tố cuối cùng mà tồn tại khi vòng lặp bắt đầu, vì vậy các yếu tố mới được thêm vào cuối sẽ không được truy cập.

Điều này vẫn hoạt động khi vectơ được sửa đổi bởi vì bạn không lưu trữ các trình vòng lặp hoặc con trỏ tới các phần tử, chỉ một chỉ mục. Miễn là bạn không loại bỏ các phần tử khỏi vectơ thì chỉ mục vẫn còn hợp lệ ngay cả sau khi chèn các phần tử mới.

+0

Tôi đã thay đổi vòng lặp của mình thành một vòng lặp cố định để truy cập thông qua một trình lặp cho (chỉ mục tự động {0u}; index BFritz

+3

Không, điều đó không an toàn. Nó không phải là một "cố định cho vòng lặp" bởi vì 'vec.size()' thay đổi, và nó có thể vòng lặp mãi mãi đến thăm các thực thể mới khi chúng được thêm vào. Nó xảy ra để làm việc cho ví dụ của bạn ở trên vì 'Bullet' không sửa đổi vectơ, nhưng bạn truy cập cả hai thực thể trong vòng lặp cập nhật đầu tiên. Có một lý do chính đáng tôi đã viết vòng lặp được sửa chữa theo cách tôi đã làm. Như tôi đã nói: _ "Điều này ghi lại kích thước ở đầu vòng lặp và do đó chỉ lặp lại thành phần cuối cùng tồn tại khi vòng lặp bắt đầu, vì vậy các phần tử mới được thêm vào cuối sẽ không được truy cập." _ –

+0

Bạn đúng , sau khi chạy một số thử nghiệm, tôi phát hiện ra rằng nó không hoạt động theo cách tôi đã đề cập ở trên. Tôi sẽ sử dụng các biến được ghi trước, như bạn đã đề xuất trong câu trả lời của mình. Tôi thành thật cảm thấy như tôi nên biết vấn đề là gì. Cảm ơn bạn đã giúp đỡ! – BFritz

Các vấn đề liên quan