std::vector 不保留在实现中实例化的推回对象

std::vector not retaining pushed back objects instantiated in the implementation

我在 plane.h:

中声明了一个向量和一个对象
extern Plane Default;
extern std::vector<Plane *>universe;

它们在 plane.cpp:

中定义
Plane Default("Default");
std::vector<Plane *>universe;

平面构造函数:

Plane::Plane(const std::string &label) {
    /* check universe to ensure uniqueness */
    std::cout << this << std:endl; //DEBUG CHECK to see what I push_back

    universe.push_back(this); //ACTION to keep track of the planes

    std::cout << universe.back() << std::endl; //DEBUG CHECK to ensure that it was stored correctly
}

输出确认平面确实存储在向量中。

主线:

if(universe.empty()) cout << "EMPTY UNIVERSE" << endl;

说明vector没有保留值。我希望默认值(如 plane.cpp 中所定义)存储在 Universe 中。

Demo

但是,当我从 main

实例化平面时,universe 保留了这些值

我想这与本地副本、范围和按值传递有关,但我想不出一种方法来在实现内部实例化默认平面,以便它的地址保留在向量中。

我也试过从堆中声明向量,如下所示:

extern std::vector<Plane *> *universe;

并这样定义:

std::vector<Plane *> *universe = new std::vector<Plane *>;

只是程序崩溃了。 我在 Code:Blocks 16.01 和 -std=C++11

的 64 位 Vista 机器上使用 MinGW32 4.9

你的全局初始化顺序失败了:

Plane Default("Default");
std::vector<Plane *>universe;

Defaultuniverse有依赖,但是构造Default时,universe还没有构造

这就是为什么您通常应该避免全局。

您的情况的解决方法是更改​​顺序,假设有相同的翻译单元:

std::vector<Plane *>universe;
Plane Default("Default");

如果您对指针的范围有疑问,也许使用 shared_ptr 就足够了?我不知道你的整体设计,但是,如果你担心指针的持久性可能会被删除或移动但仍然需要数据;智能指针可能就是答案。如果任何地方都存在循环问题,请使用 weak_ptr 来解决它。

祝你好运!

应该可以解决您遇到的问题。我将提供一些指南来防止出现此类问题。

一般来说,最好避免使用 extern 声明变量访问的全局数据。最好使用函数接口提供对全局数据的访问。

你可以改变

extern std::vector<Plane *>universe;

extern std::vector<Plane *>& getUniverse();

并将其实现为:

extern std::vector<Plane *>& getUniverse()
{
   static std::vector<Plane *>universe;
   return universe;
}

有了它,您可以将 Plane 的构造函数更改为:

 Plane::Plane(const std::string &label) {
    getUniverse().push_back(this);
}

这将消除与全局数据初始化顺序相关的问题。

你也可以改变

extern Plane Default;

extern Plane& getDefaultPlane();

并将其实现为

extern Plane& getDefaultPlane()
{
   static Plane Default("Default");
   return Default;
}

如果必须在初始化时构造默认值 Plane,您可以使用类似以下内容的内容:

// Use a helper struct in an anonymous namespace to initialize
// whatever needs to be initialized.
namespace
{
    struct Initializer
    {
       Initializer();
    };
}

// Make sure that Initializer::Initializer() gets called
// at initialization time.
static Initializer initer;

Initializer::Initializer()
{
   // Trigger construction of the default Plane
   getDefaultPlane();
}