C + + : 数组的构造函数初始化器

我有一个大脑痉挛... 我如何在 C + + 中正确地初始化一个对象数组?

非数组的例子:

struct Foo { Foo(int x) { /* ... */  } };


struct Bar {
Foo foo;


Bar() : foo(4) {}
};

数组例子:

struct Foo { Foo(int x) { /* ... */  } };


struct Baz {
Foo foo[3];


// ??? I know the following syntax is wrong, but what's correct?
Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};

编辑: 疯狂和疯狂的变通方法的想法是赞赏的,但他们不会帮助我在我的情况下。我正在开发一个嵌入式处理器,其中 std: : Vector 和其他 STL 结构都不可用,最明显的解决方案是创建一个缺省构造函数,并有一个可以在构建时调用的显式 init()方法,这样我就根本不需要使用初始化器了。(这是我被 Java 的 final关键字 + 构造函数的灵活性所破坏的情况之一。)

156443 次浏览

编辑: 看看 巴里的回答最近的一些东西,当我回答的时候没有办法,但是现在你很少被限制在 C + + 98。


不可能。你需要一个数组成员的缺省构造函数,它会被调用,然后你可以在构造函数中进行任何你想要的初始化。

在数组中创建对象时只能调用缺省构造函数。

现在,您不能为数组成员使用初始化器列表。

class Baz {
Foo foo[3];


Baz() {
foo[0] = Foo(4);
foo[1] = Foo(5);
foo[2] = Foo(6);
}
};

在 C + + 0x 中,你可以写:

class Baz {
Foo foo[3];


Baz() : foo({4, 5, 6}) {}
};

在特定情况下,如果数组是类的数据成员,则 不行将在该语言的当前版本中对其进行初始化。没有这样的语法。要么为数组元素提供一个缺省构造函数,要么使用 std::vector

可以使用聚合初始值设定项来初始化独立数组

Foo foo[3] = { 4, 5, 6 };

但不幸的是,构造函数初始化器列表没有相应的语法。

没有可以在此上下文中使用的数组构造语法,至少不能直接使用。你可以通过以下方式来完成你想要完成的事情:

Bar::Bar()
{
static const int inits [] = {4,5,6};
static const size_t numInits = sizeof(inits)/sizeof(inits[0]);
std::copy(&inits[0],&inits[numInits],foo);  // be careful that there are enough slots in foo
}

不过你要给阿福一个缺省构造函数。

这似乎有效,但我不相信它是正确的:

#include <iostream>


struct Foo { int x; Foo(int x): x(x) { } };


struct Baz {
Foo foo[3];


static int bar[3];
// Hmm...
Baz() : foo(bar) {}
};


int Baz::bar[3] = {4, 5, 6};


int main() {
Baz z;
std::cout << z.foo[1].x << "\n";
}

产出:

$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
g++ -pedantic    arrayinit.cpp   -o arrayinit
5

买者自慎。

编辑: 不,科莫拒绝了。

另一个编辑: 这是一种欺骗,它只是将逐个成员的数组初始化推到一个不同的地方。所以它仍然需要 Foo 有一个缺省构造函数,但是如果你没有 std::vector,那么你可以为自己实现你所需要的最低限度:

#include <iostream>


struct Foo {
int x;
Foo(int x): x(x) { };
Foo(){}
};


// very stripped-down replacement for vector
struct Three {
Foo data[3];
Three(int d0, int d1, int d2) {
data[0] = d0;
data[1] = d1;
data[2] = d2;
}
Foo &operator[](int idx) { return data[idx]; }
const Foo &operator[](int idx) const { return data[idx]; }
};


struct Baz {
Three foo;


static Three bar;
// construct foo using the copy ctor of Three with bar as parameter.
Baz() : foo(bar) {}
// or get rid of "bar" entirely and do this
Baz(bool) : foo(4,5,6) {}
};


Three Baz::bar(4,5,6);


int main() {
Baz z;
std::cout << z.foo[1].x << "\n";
}

z.foo实际上不是一个数组,但它看起来像一个向量。在 Three 中添加 begin()end()函数是很简单的。

来自扭曲思想的想法:

class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
//initialise with initVector[0] and then delete it :-)
}


};

现在将这个 initVector设置为在实例化对象之前需要的值。然后使用参数初始化对象。

不幸的是,在 C + + 0x 之前无法初始化数组成员。

您可以在构造函数体中使用 std: : Vector 和 push _ back Foo 实例。

你可以给福一个缺省构造函数(可能是私人的,让巴兹成为你的朋友)。

您可以使用 可复制的数组对象(Boost 或 std: : tr1) ,并从静态数组初始化:

#include <boost/array.hpp>


struct Baz {


boost::array<Foo, 3> foo;
static boost::array<Foo, 3> initFoo;
Baz() : foo(initFoo)
{


}
};


boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };

可以将 C + + 0x auto关键字与 模板专门化一起使用,例如一个名为 boost::make_array()的函数(类似于 make_pair())。对于 N是1或2个参数的情况,我们可以将 变体 A写成

namespace boost
{
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
return boost::array<T,2> (\{\{ a }});
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
return boost::array<T,2> (\{\{ a, b }});
}
}

以及 变体 B

namespace boost {
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
boost::array<T,1> x;
x[0] = a;
return x;
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
boost::array<T,2> x;
x[0] = a;
x[1] = b;
return x;
}
}

具有 -std=gnu++0x-O3的 GCC-4.6为

auto x = boost::make_array(1,2);

同时使用 AB

boost::array<int, 2> x = \{\{1,2}};

然而,对于 用户定义类型(UDT) ,变体 B 导致 多加一个复制建构子,这通常会使事情变慢,因此应该避免。

请注意,在使用显式 char 数组文字调用它时,boost::make_array会出错,如下例所示

auto x = boost::make_array("a","b");

我相信这是一件好事,因为 const char*字面值可以被 欺骗在他们的使用。

可变模板 ,从4.5开始在 GCC 中可用,可以进一步使用将每个 N的所有模板专门化锅炉板代码减少为定义为

/*! Construct Array from @p a, @p b. */
template <typename T, typename ... R>
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
{
return boost::array<T,1+sizeof...(R)>(\{\{ a, b... }});
}

这和我们预期的差不多。第一个参数确定 boost::array模板参数 T和所有其他参数转换成 T。在某些情况下,这可能是不可取的,但我不确定如何使用可变模板来指定这一点。

也许 boost::make_array()应该进入 Boost 图书馆?

class C
{
static const int myARRAY[10];  // only declaration !!!


public:
C(){}
}


const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9};  // here is definition


int main(void)
{
C myObj;
}

为了更新 C + + 11的这个问题,现在可以这样做了,而且非常自然:

struct Foo { Foo(int x) { /* ... */  } };


struct Baz {
Foo foo[3];


Baz() : foo\{\{4}, {5}, {6}} { }
};

这些花括号也可以省略,以便使用更简洁的表达方式:

struct Baz {
Foo foo[3];


Baz() : foo{4, 5, 6} { }
};

它也可以很容易地扩展到多维数组:

struct Baz {
Foo foo[3][2];


Baz() : foo{1, 2, 3, 4, 5, 6} { }
};

在视觉工作室2012年或以上,你可以这样做

struct Foo { Foo(int x) { /* ... */  } };


struct Baz {
Foo foo[3];


Baz() : foo() { }
};

你可以这么做,但这并不漂亮:

#include <iostream>


class A {
int mvalue;
public:
A(int value) : mvalue(value) {}
int value() { return mvalue; }
};


class B {
// TODO: hack that respects alignment of A.. maybe C++14's alignof?
char _hack[sizeof(A[3])];
A* marr;
public:
B() : marr(reinterpret_cast<A*>(_hack)) {
new (&marr[0]) A(5);
new (&marr[1]) A(6);
new (&marr[2]) A(7);
}


A* arr() { return marr; }
};


int main(int argc, char** argv) {
B b;
A* arr = b.arr();
std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n";
return 0;
}

如果你把这个放在你的代码中,我希望你有一个非常好的理由。

这是我的解决方案,供你参考:

struct Foo
{
Foo(){}//used to make compiler happy!
Foo(int x){/*...*/}
};


struct Bar
{
Foo foo[3];


Bar()
{
//initialize foo array here:
for(int i=0;i<3;++i)
{
foo[i]=Foo(4+i);
}
}
};