Как вы вызываете конструктор для глобальных объектов, для массивов объектов, и для объектов внутри классов/структур?

как бы вы назвали конструктор следующего класса в этих трех ситуациях: глобальные объекты, массивы объектов и объекты, содержащиеся в другом классе/структуре?

класс с конструктором (используется во всех трех примерах):

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;
}

8 ответов


глобальные объекты

ваш единственный способ. С другой стороны, постарайтесь этого избежать. Вместо этого лучше использовать функции (или даже другие объекты) в качестве фабрик. Таким образом, вы можете контролировать время создания.

массив объектов

нет никакого способа сделать это напрямую. Объекты Non-POD всегда будут построены по умолчанию. std::fill часто большая помощь. Вы также можете посмотреть в распределители и std::uninitialized_fill.

объекты, содержащиеся в classes / structs

используйте списки инициализации в своем конструкторе:

class Bar {
    Foo foo;

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

статические члены должны быть определены вне класса:

class Bar {
    static Foo 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++ по существу говорит, что она будет вызвана до main() и будет уничтожена некоторое время спустя. Кроме этого, компилятор может делать все, что ему заблагорассудится.

в первом случае массива вы создаете статический массив объектов Foo. По умолчанию каждое значение в массиве будет инициализировано конструктором Foo () по умолчанию. С необработанным массивом C++ невозможно заставить определенный перегруженный массив вызываемый конструктор. Вы можете вывести немного управления, переключившись на вектор вместо массива. Векторный конструктор имеет перегруженный вектор конструктора (size,defaultValue), который должен достичь того, что вы ищете. Но в этом случае вы должны быть осторожны, потому что вместо вызова Foo(3) он вызовет Foo(const Foo& other), где other-Foo(3).

второй случай массива очень похож на первый случай. Единственное реальное различие заключается в том, где выделяется память (on кучу вместо стека). Он имеет то же ограничение в отношении вызова конструктора.

содержащийся случай-это другая проблема. C++ имеет четкое разделение между определением поля внутри объекта и инициализацией поля. Чтобы это работало на C++, вам нужно изменить определение бара на следующее

class Bar{
  Foo foo;
  Bar() : foo(3){}
};

в этом потоке, похоже, есть общий смысл, который вы не можете инициализировать члены массива, кроме использования конструктора по умолчанию. Один ответ даже создает другой тип, просто чтобы вызвать другой конструктор. Даже если вы можете (если массив не является частью как член класса!):

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 техника описал Энди.Гурин-тоже вариант. Но заметьте, это все усложнит. Вам придется самому вызвать деструкторы. И если какой-либо конструктор бросает, пока вы все еще строите массив, то вам нужно выяснить, где вы остановились... В целом, если вы хотите иметь массивы в своем классе и хотите их инициализировать, используйте std::vector - Это простая ставка.


построение массивов объектов:

вы можете изменить исходный пример, используя параметры по умолчанию.

В настоящее время поддерживается только конструктор по умолчанию.
Это то, что рассматривается в следующей версии (потому что все задают этот вопрос)


списки инициализаторов C++0X решают эту проблему для случая массивов объектов. См.этой запись в блоге Херба Саттера, где он подробно описывает их.

тем временем вы, возможно, сможете обойти проблему следующим образом:

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 class вы действительно должны привыкнуть использовать списки инициализаторов членов вместо назначений в конструкторе, как в моем примере выше)


Я считаю, что есть два способа убедиться, что конструкторы объектов глобального класса вызываются безопасно во время их "создания":

  1. объявите их в пространстве имен и сделайте это пространство имен глобально доступным.

  2. сделайте его глобальным указателем на объект класса и назначьте ему новый объект класса в main (), предоставленный код для конструкторов других глобальных объектов, доступ к объекту будет выполняться до этот.

просто мои два цента.