在创建类的常量对象之前的非const变量初始化

时间:2016-01-07 22:25:52

标签: c++

我有一个矢量:

std::vector<uint16_t> free_ids;

我需要它的运算符==我的类GameObject。创建对象时,它将从向量中接收空闲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类型的全局常量,但它会导致分段错误,因为在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;类的任何对象之前初始化const - 不能是GameObject? (将有许多不同继承类的祖先,我将使用的模板式对象(已经是但我想重新排列代码)来创建实时克隆)

3 个答案:

答案 0 :(得分:2)

虽然很容易创建一个静态对象来初始化它的构造函数中的向量,但是你永远不能保证在不同翻译单元中的所有其他静态对象之前初始化这个静态对象。

相反,你可能会做的是采用单件式的东西。在这个单例中,您可以公开get_id和release_id函数。根据所提供的代码判断,我认为你不需要我为你画出这个单身人士,但如果你这样做,请随时提出要求。

答案 1 :(得分:1)

老实说,你可以这样做。

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

protected:
    const InstanceId mId;

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

当然,如果您的游戏在运行期间产生超过 18446744073709551615 的对象,您可能会遇到冲突。

答案 2 :(得分:0)

简易选项:

您可以定义一个帮助程序类,以确保您的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语句并导致竞争状态。我没有首先解决这个问题,因为你的剩余代码并没有显示多线程的证据:你对全局向量的访问不是线程安全的,可能会导致不利的竞争条件。如果您确实需要多线程,并且如果您不能应用第二个选项,那么您应该使用atomic donecompare_exchange_strong()来测试其值。

清洁变体:

如果你想避免你的矢量是全局的,你也可以定义一个帮助类,如:

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

并在GameObject类中创建一个静态成员obect:

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

当然,当且仅当您的向量仅由GameObject及其派生使用时,此方法才有效。

但是这个是线程安全的,因为静态对象是guaranteed to be initialized only once