Ответ 1
Нет никакого способа. Вам нужен конструктор по умолчанию для членов массива, и он будет вызываться, после чего вы можете выполнить любую инициализацию, которую вы хотите в конструкторе.
У меня возникает судорога мозга... как я могу правильно инициализировать массив объектов на С++?
пример без массива:
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 correct?
Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};
изменить: Дикие и сумасшедшие обходные идеи оценены, но они не помогут мне в моем случае. Я работаю над встроенным процессором, где std::vector и другие конструкторы STL недоступны, и очевидным обходным решением является создание конструктора по умолчанию и наличие явного метода init()
, который можно вызывать после времени построения, так что я не нужно использовать инициализаторы вообще. (Это один из тех случаев, когда я испортил Java final
ключевое слово + гибкость с помощью конструкторов.)
Нет никакого способа. Вам нужен конструктор по умолчанию для членов массива, и он будет вызываться, после чего вы можете выполнить любую инициализацию, которую вы хотите в конструкторе.
Чтобы обновить этот вопрос для С++ 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} { }
};
В настоящий момент вы не можете использовать список инициализаций для элементов массива. Вы застряли, делая это тяжело.
class Baz {
Foo foo[3];
Baz() {
foo[0] = Foo(4);
foo[1] = Foo(5);
foo[2] = Foo(6);
}
};
В С++ 0x вы можете написать:
class Baz {
Foo foo[3];
Baz() : foo({4, 5, 6}) {}
};
К сожалению, нет возможности инициализировать элементы массива до С++ 0x.
Вы можете использовать std::vector и push_back экземпляры Foo в теле конструктора.
Вы можете дать Foo конструктор по умолчанию (может быть частным и сделать Baz другом).
Вы можете использовать объект массива, который является копируемым (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 };
Вы можете использовать С++ 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;
}
}
GCC-4.6 с -std=gnu++0x
и -O3
генерирует тот же самый двоичный код для
auto x = boost::make_array(1,2);
используя как A, так и B, как и для
boost::array<int, 2> x = {{1,2}};
Для определяемых пользователем типов (UDT), однако, вариант B приводит к дополнительному конструктору копирования, который обычно замедляет работу, и поэтому его следует избегать.
Обратите внимание, что ошибки boost::make_array
при вызове с явными литералами массива char, как в следующем случае
auto x = boost::make_array("a","b");
Я считаю, что это хорошо, поскольку литералы const char*
могут быть обманчивыми при их использовании.
Шаблоны Variadic, доступные в GCC начиная с версии 4.5, могут быть использованы для уменьшения кода кодовой таблички специализации шаблона для каждого N
в определении одного шаблона boost::make_array()
определяется как
/*! 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
template T
, а все остальные аргументы преобразуются в T
. В некоторых случаях это может быть нежелательным, но я не уверен, как это можно указать с помощью вариационных шаблонов.
Возможно, boost::make_array()
должен войти в библиотеки Boost?
Это похоже на работу, но я не уверен, что это правильно:
#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
Предостережение emptor.
Изменить: нет, Комо отвергает его.
Другое редактирование: это своего рода обман, он просто толкает инициализацию массива по-члену в другое место. Поэтому для 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
на самом деле не массив, но он выглядит примерно так же, как и вектор. Добавление функций begin()
и end()
в Three тривиально.
При создании объектов в массиве можно вызывать только конструктор по умолчанию.
В конкретном случае, когда массив является членом данных класса, вы не можете его инициализировать в текущей версии языка. Для этого нет синтаксиса. Либо укажите конструктор по умолчанию для элементов массива, либо используйте 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
}
... но вам нужно указать Foo конструктор по умолчанию.
Идеи из искривленного ума:
class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
//initialise with initVector[0] and then delete it :-)
}
};
теперь установите этот initVector
на то, что вам нужно, до того, как вы создадите экземпляр объекта. Затем ваши объекты инициализируются вашими параметрами.
Вы можете сделать это, но это не красиво:
#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 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);
}
}
};
в visual studio 2012 или выше, вы можете сделать это как
struct Foo { Foo(int x) { /* ... */ } };
struct Baz {
Foo foo[3];
Baz() : foo() { }
};
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;
}