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

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

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

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;
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
0
18 497
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

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

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

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

Это невозможно сделать напрямую. Объекты, не относящиеся к 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 является простая ставка.

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

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

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

Всего два цента.

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

Brandon 30.03.2011 04:10

Другие вопросы по теме