簡體   English   中英

在創建類的常量對象之前進行非常量變量初始化

[英]Non-const variable initialization before constant objects of class are created

我有一個向量:

std::vector<uint16_t> free_ids;

我需要我的GameObject類的operator ==。 創建對象時,它將收到來自vector的免費id,因此它將被“移動”到對象。 它會像這樣簡單地獲取值:

void init(void)
{
    for(uint64_t i=0; i<30; ++i)
        free_ids.push_back(i);
}

所以我有成功使用該類的類。

class GameObject
{
    public:
        static std::vector<GameObject*> created_objects;     // all objects created ever
        static constexpr auto& CO = created_objects;

    GameObject()
    {
        id = free_ids.front();               // get id from vector
        free_ids.erase(free_ids.begin());    // delete it from vector
        CO.push_back(this);                  // add address to all object created ever
    }

    GameObject(const GameObject& other)
    {
        // copy attributes I didn't include in this code
        id = free_ids.front();
        free_ids.erase(free_ids.begin());
        CO.push_back(this);
    }

    ~GameObject()
    {
        free_ids.push_back(id); // return id to vector
        CO.erase(std::remove(CO.begin(), CO.end(), this), CO.end()); 
                                       // remove object by address
    }

    bool operator==(const GameObject& other)
    {
        return id==other.id;    // check if id is the same (if it's the same object)
    }

    const uint64_t& get_id(void) const
    {
        return id;
    }

private:
    uint64_t id;
};

std::vector<GameObject*> GameObject::created_objects;

我很想擁有GameObject類型GameObject全局常量,但這會導致分段錯誤,因為init()從未在main()調用之前被調用

//const GameObject Progenitor; //segmentation fault, free_ids not initialized yet

和一個示例程序:

int main()
{
    srand(time(NULL));

    init();

    const GameObject Progenitor; // It's temporary replacement for my global

    std::vector<GameObject> clone_army;
    clone_army.reserve(20); // GameObjects can't be reallocated bacause 
                            // their addresses are stored in class static vector
    auto& CA = clone_army;

    for(uint64_t i=0; i<20; ++i)
        CA.push_back(Progenitor);

    std::cout << "Storage used. Unoccupied ids: " << std::endl;
    for(auto& x : free_ids)
        std::cout << x << std::endl;
    auto& victim = clone_army[rand()%20]; // it's much more compilated

    std::cout << "\nOne will die. Victim is... clone no. " << victim.get_id() << std::endl;
    CA.erase(std::remove(CA.begin(), CA.end(), victim), CA.end()); 
                 // need to remove victim by value, operator== involved

    std::cout << "\nProgenitor id: ";

    for(auto& x : GameObject::CO)
        std::cout << x->get_id() << std::endl;
}

負責的標題:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>

我的問題是,如何初始化std::vector<uint16_t> free_ids; -在創建GameObject類的任何對象之前不能將其設為const (會有許多不同繼承類的祖先,我將使用模板類對象(已經是但想重新排列代碼)來創建實時克隆)

雖然創建靜態對象很容易,它將在其構造函數中初始化矢量,但是您永遠不能保證此靜態對象會先於其他翻譯單元中的所有其他靜態對象被初始化。

相反,您可能要做的是使用單例類型的東西。 在此單例中,您可以公開get_id和release_id函數。 從提供的代碼來看,我認為您不需要我為您繪制此單例,但是如果您願意,可以隨時提出要求。

老實說,您可以這樣做。

class GameObject
{
private:
    using InstanceId = unsigned long long;
    static InstanceId _OBJECT_ID = 0;

protected:
    const InstanceId mId;

public:
    GameObject()
            : mId(_OBJECT_ID++)
    {}
};

當然,如果您的游戲在運行過程中生成了超過18446744073709551615個對象,則可能會產生沖突。

簡單的選擇:

您可以定義一個幫助程序類,以確保僅對vecotr初始化一次:

class GameObjectHelper {
    static bool done; 
public:
    GameObjectHelper() {
        if (!done) {
            init(); 
            done = false; 
        }
    } 
}; 
bool GameObjectHelper::done = false; 

然后,您可以利用基類在派生之前構造一個事實,從而確保在GameObject的構造函數之前調用此對象的構造函數:

class GameObject : private GameObjectHelper { 
    ...
 } ;

重要編輯: 如果您的GameObjects是多線程的,則done只會以線程安全的方式初始化為false。 不幸的是,幾個線程可以輸入if語句並導致競爭條件。 我沒有首先討論這個主題,因為您剩余的代碼沒有顯示多線程的證據:您對全局向量的訪問不是線程安全的,並且可能導致不利的競爭狀況。 如果您確實需要多線程,並且您無法應用第二個選項,則應使用用compare_exchange_strong() done atomic測試其值。

清潔劑變體:

如果要避免向量是全局的,則最好定義一個輔助類,例如:

class GameObjectHelper {
    vector<uint16_t> free_ids;
public:
    GameObjectHelper() {
        for(uint64_t i=0; i<30; ++i)
            free_ids.push_back(i);
        }
    } 
}; 

並在GameObject類中創建一個靜態成員對象:

class GameObject  { 
protected:
    static GameObjectHelper goh; 
    ...
 } ;

當然,當且僅當您的向量僅由GameObject及其衍生物使用時,此方法才有效。

但這是線程安全的,因為那里的靜態對象只能被初始化一次

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM