我想定义一个全局容器(C++03),下面是我尝试过的一个示例代码,但它不起作用。
#include <vector>
#include <string>
using namespace std;
vector<string> Aries;
Aries.push_back("Taurus"); // line 6
int main() {}
编译错误:
prog.cpp:6:1: error: 'Aries' does not name a type
我似乎可以定义一个空的全局向量,但无法填充它。看起来在C++03中,我也不能指定初始值设定项,例如:
vector<string> Aries = { "Taurus" };
我在这里犯了错误吗?或者我该如何解决这个问题?
我试着在StackOverflow上搜索,看看以前是否有人回答过这个问题,但只看到了以下帖子:C++中的全局对象,C++中的定义全局常量,这对回答这个问题没有帮助。
我找到了一个巧妙的解决方法来"初始化"C++03全局STL容器(实际上是在main()
之前"全局"执行代码)。这使用逗号运算符。参见示例:
#include <vector>
#include <string>
#include <iostream>
using namespace std;
vector<string> Aries;
// dummy variable initialization to setup the vector.
// using comma operator here to cause code execution in global scope.
int dummy = (Aries.push_back("Taurus"), Aries.push_back("Leo"), 0);
int main() {
cout << Aries.at(0) << endl;
cout << Aries.at(1) << endl;
}
输出
Taurus
Leo
唯一真正的问题,如果你可以这么说的话,就是额外的全局变量。
虽然函数(如main
)之外的声明和初始化没有问题,但函数之外不能有代码。您要么需要在初始化时设置正确的值(如C++11),要么在main中填充全局对象:
std::vector<string> Aries;
/* ... */
int main() {
Aries.push_back("Taurus");
/* ... */
}
其他方式(不填充main中的矢量)
单个值
还有其他方式基本上不需要.push_back
或其他代码。例如,如果Aries
仅包含一个项目,则可以将std::vector<T>::vector(size_t s, T t)
与s == 1
和t == "Taurus"
:一起使用
std::vector<string> Aries(1, "Taurus");
/* ... */
int main() { /* ... */ }
如果您多次需要相同的值,您可以简单地调整s
。
多个不同值
现在这变得有点棘手了。您希望通过使用合适的构造函数来填充vector
。std::vector<T>
在C++03:中提供了四种不同的构造函数
std::vector<T>::vector()
std::vector<T>::vector(size_t, T = T())
template <class InputIt> std::vector<T>::vector(InputIt, InputIt)
std::vector<T>::vector(const vector&)
请注意,它们实际上都将分配器作为附加的最后一个参数,但这不是我们关心的问题
我们可以忘记std::vector<T>::vector()
,因为我们想用值填充向量,而且std::vector<T>::vector(size_t, T)
不适合,因为我们想要不同的值。剩下的是模板化构造函数和复制构造函数。以下示例显示了如何使用模板化构造函数:
std::string const values[] = {"Taurus", "Ares", "Testos"};
template <class T, size_t N>
T* begin(T (&array)[N]){ // this is already in C++11, very helpful
return array;
}
template <class T, size_t N>
T* end(T (&array)[N]){
return array+N;
}
std::vector<std::string> Aries(begin(values), end(values));
注意,begin
和end
不是必需的——我们可以简单地使用Aries(values, values+3)
。然而,事情往往会发生变化,通常你会添加或删除一个值。如果您忘记更改偏移3
,则可能会忘记values
的入口或跨境。
然而,这个解决方案引入了一个新的全局变量,但效果并不好。让我们后退一步。我们需要values
,因为我们想要使用std::vector<T>::vector(InputIt, InputIt)
,它需要一个有效的范围。在我们使用构造函数的时候,这个范围必须是已知的,所以它需要是全局的。诅咒!但是请注意:我们的工具箱仍然包含一个构造函数,即复制构造函数。为了使用它,我们创建了一个附加功能:
namespace {
std::vector<std::string> initializer(){
const std::string dummy_array[] = {"Taurus", "Ares", "Testos"};
return std::vector<std::string>(begin(dummy_array), end(dummy_array));
}
}
std::vector<std::string> Aries(initializer());
此示例同时使用复制构造函数和范围构造函数。您也可以创建一个临时向量,并使用std::vector<T>::push_back
在函数中填充它。
我的经验是,这个"惊人但可怕"(帽子提示)的解决方案是不可预测的。您的初始化在加载时运行。您不能保证加载订单。因此,所有使用该容器的东西都必须在同一个模块中,或者以某种方式保证在访问容器之前加载模块。否则,容器构造函数还没有运行,但是您的代码愉快地调用了它的访问器。
当它出错时,它就会出错,而且错误取决于编译器、平台和月球相位——在所有情况下,都没有给出任何关于南下的线索。
为了完整性,此解决方案使用迭代器对构造函数和普通的旧数组初始值设定项。
#include <vector>
#include <string>
#include <iostream>
using namespace std;
string contents[] = {"Taurus", "Leo"};
vector<string> Aries(contents, contents + sizeof contents/sizeof contents[0]);
int main() {
cout << Aries.at(0) << endl;
cout << Aries.at(1) << endl;
}
这可能更清楚,但它调用了更多的复制构造函数。