Как бы вы вызывали конструктор следующего класса в этих трех ситуациях: глобальные объекты, массивы объектов и объекты, содержащиеся в другом классе / структуре?
Класс с конструктором (используется во всех трех примерах):
class Foo {
public:
Foo(int a) { b = a; }
private:
int b;
};
И вот мои попытки вызвать этот конструктор:
Foo global_foo(3); // works, but I can't control when the constructor is called.
int main() {
// ...
}
int main() {
// Array on stack
Foo array_of_foos[30](3); // doesn't work
// Array on heap
Foo *pointer_to_another_array = new Foo(3) [30]; // doesn't work
}
Здесь я пытаюсь вызвать конструктор для всех элементов массивов, но я также хотел бы знать, как вызывать его для отдельных элементов.
class Bar {
Foo foo(3); // doesn't work
};
int main() {
Bar bar;
}





Ваш единственный способ. С другой стороны, постарайтесь этого избежать. Вместо этого лучше использовать функции (или даже другие объекты) как фабрики. Таким образом, вы можете контролировать время создания.
Это невозможно сделать напрямую. Объекты, не относящиеся к POD, всегда создаются по умолчанию. std::fill часто оказывается большим подспорьем. Вы также можете изучить распределители и std::uninitialized_fill.
Используйте списки инициализации в своем конструкторе:
class Bar {
Foo foo;
Bar() : foo(3) { }
};
Статические члены фактически должны быть определены вне класса:
class Bar {
static Foo foo;
};
Foo Bar::foo(3);
Для глобального случая нет возможности контролировать, когда он вызывается. Спецификация C++ по существу говорит, что он будет вызываться перед main () и будет уничтожен через некоторое время после этого. В остальном компилятор может делать все, что ему заблагорассудится.
В первом случае с массивом вы создаете статический массив объектов Foo. По умолчанию каждое значение в массиве будет инициализировано конструктором по умолчанию Foo (). Невозможно с помощью необработанного массива C++ вызвать конкретный перегруженный конструктор. Вы можете получить некоторый контроль, переключившись на вектор вместо массива. Конструктор вектора имеет перегруженный вектор конструктора (size, defaultValue), который должен достичь того, что вы ищете. Но в этом случае вы должны быть осторожны, потому что вместо вызова Foo (3) он вызовет Foo (const Foo & other), где other - Foo (3).
Второй случай массива очень похож на первый случай. Единственная реальная разница в том, где выделяется память (в куче, а не в стеке). У него такое же ограничение в отношении вызова конструктора.
Содержащийся в контейнере футляр - это другой вопрос. В C++ есть четкое разделение между определением поля в объекте и инициализацией поля. Чтобы это работало в C++, вам нужно изменить определение Bar на следующее
class Bar{
Foo foo;
Bar() : foo(3){}
};
Чтобы исправить некоторые заблуждения о глобальных объектах:
Не то, что я рекомендую, но: Итак, простое решение - поместить все глобальные объекты в одну единицу компиляции.
В качестве альтернативы вы можете настроить использование статических переменных функции. В принципе, у вас может быть функция, которая возвращает ссылку на глобальный объект, который вы хотите (определение глобального внутри функции). Он будет создан при первом использовании (и уничтожен в порядке, обратном созданию).
Foo& getGlobalA() // passed parameters can be passed to constructor
{
static Foo A;
return A;
}
Foo& getGlobalB()
{
static Foo B;
return B;
}
etc.
Ответ Конрада в порядке, просто пунтуализация массивов .... Есть способ создать массив элементов (не указателей), и вот он:
//allocate raw memory for our array
void *rawMemory = operator new[](30 * sizeof(Foo))
// point array_of_foos to this memory so we can use it as an array of Foo
Foo *array_of_foos = static_cast<Foo *>(rawMemory);
// and now we can create the array of objects(NOT pointers to the objects)
// using the buffered new operator
for (int i = 0; i < 30; i++)
new(array_of_foos[i])Foo(3);
Этот подход описан здесь: http://www.amazon.com/gp/product/0321334876?ie=UTF8&tag=aristeia.com-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0321334876
Построение массивов объектов:
Вы можете изменить исходный пример, используя параметры по умолчанию.
В настоящее время поддерживается только конструктор по умолчанию. Это то, что будет решено в следующей версии (потому что все задают этот вопрос)
Списки инициализаторов C++ 0X решают эту проблему для кейса массивов объектов. См. Запись в блоге это Herb Sutter, где он подробно их описывает.
А пока вы можете решить эту проблему следующим образом:
class Foo {
public:
Foo(int a) : b(a) {}
private:
int b;
};
class Foo_3 : public Foo {
public:
Foo_3() : Foo(3) {}
};
Foo_3 array_of_foos[30];
Здесь класс Foo_3 существует исключительно с целью вызова конструктора Foo с правильным аргументом. Вы можете сделать его шаблоном даже:
template <int i>
class Foo_n : public Foo {
public:
Foo_n() : Foo(i) {}
};
Foo_n<3> array_of_foos[30];
Опять же, это может не совсем то, что вы хотите, но может дать пищу для размышлений.
(Также обратите внимание, что в вашем классе Foo вам действительно следует привыкнуть использовать списки инициализаторов членов вместо назначений в конструкторе, как в моем примере выше)
Похоже, что общая суть этого потока заключается в том, что вы не можете инициализировать элементы массива, кроме как с помощью конструктора по умолчанию. Один ответ даже создает другой тип, просто чтобы вызвать другой конструктор. Хотя вы можете (если массив не входит в состав класса!):
struct foo {
foo(int a): a(a) { }
explicit foo(std::string s): s(s) { }
private:
int a;
std::string s;
};
/* global */
foo f[] = { foo("global"), foo("array") };
int main() {
/* local */
foo f[] = { 10, 20, 30, foo("a"), foo("b") };
}
Однако тип должен иметь возможность копирования: данные элементы инициализируются копированием в члены массива.
Для массивов в качестве членов классов лучше всего использовать контейнеры в настоящее время:
struct bar {
/* create a vector of 100 foo's, initialized with "initial" */
bar(): f(100, foo("initial")) { }
private:
std::vector<foo> f;
};
Также возможно использование техники placement-new, описанной andy.gurin. Но учтите, что это все усложнит. Вам придется вызывать деструкторы самостоятельно. И если какой-либо конструктор выдает ошибку, пока вы все еще создаете массив, вам нужно выяснить, где вы остановились ... В целом, если вы хотите иметь массивы в своем классе и хотите их инициализировать, использование std::vector является простая ставка.
Я считаю, что есть два способа убедиться, что конструкторы объектов глобального класса безопасно вызываются во время их «создания»:
Объявите их в пространстве имен и сделайте это пространство имен глобально доступным.
Сделайте его глобальным указателем на объект класса и назначьте ему новый объект класса в main (), предоставленный код для других конструкторов глобальных объектов, которые обращаются к объекту, будет выполняться перед этим.
Всего два цента.
ну, я просто попробовал решение для пространства имен, которое я предложил в своей собственной реализации, и все же оно не работает. Иногда я назначаю переменные-члены объекта класса до вызова конструктора объекта. какать.