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

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

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

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 ]

10 голосов
/ 16 ноября 2008

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

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

Массивы объектов

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

Объекты, содержащиеся в классах / структурах

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

class Bar {
    Foo foo;

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

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

class Bar {
    static Foo foo;
};

Foo Bar::foo(3);
4 голосов
/ 16 ноября 2008

Чтобы исправить некоторые неправильные представления о глобалах:

  • Порядок четко определен в единице компиляции.
    • То же, что и порядок определения
  • Порядок в единицах компиляции не определен.
  • Порядок уничтожения является ТОЧНОЙ противоположностью творения.

Не то, что я рекомендую, но: Итак, простое решение - поместить все глобальные переменные в один модуль компиляции.

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

Foo& getGlobalA() // passed parameters can be passed to constructor
{
    static Foo  A;
    return A;
}
Foo& getGlobalB()
{
    static Foo  B;
    return B;
}
etc. 
2 голосов
/ 16 ноября 2008

С ответом Конрада все в порядке, просто размышления о массивах .... Существует способ создать массив элементов (не указателей), и здесь он выглядит следующим образом:

//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

1 голос
/ 17 ноября 2008

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

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 простая ставка.

1 голос
/ 16 ноября 2008

Для глобального случая нет способа контролировать, когда он вызывается. Спецификация C ++ по существу говорит, что он будет вызван перед main () и будет уничтожен через некоторое время. Кроме этого, компилятор может делать все, что пожелает.

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

Второй случай массива очень похож на первый случай. Единственная реальная разница в том, где выделяется память (в куче, а не в стеке). Это имеет то же ограничение в отношении обращения к конструктору.

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

class Bar{
  Foo foo;
  Bar() : foo(3){}
};
0 голосов
/ 30 марта 2011

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

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

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

Только мои два цента.

0 голосов
/ 16 ноября 2008

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

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

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

0 голосов
/ 16 ноября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...