Могут ли вызовы выделения памяти и конструктора чередоваться с другими операциями, необходимыми для выполнения нового выражения?

Предположим, у меня есть следующий класс:

 class Sample {
 public:
     Sample( int ) {}
 };

некоторая функция, возвращающая int

int SomeFunction()
{
    return 0;
}

и этот код:

Sample* sample = new Sample( SomeFunction() );

Теперь ожидаю следующую последовательность:

  • SomeFunction() запускается, затем
  • ::operator new() запускается для выделения памяти для объекта, затем
  • class Sample конструктор запускается в выделенной памяти

Фиксирован ли этот порядок или его можно изменить с помощью такой реализации, которая, скажем, сначала выделяет память, затем вызывается SomeFunction(), а затем запускается конструктор? Другими словами, может ли вызов operator new() функции и вызов конструктора класса чередоваться с чем-либо?


person sharptooth    schedule 04.05.2011    source источник
comment
одно обязательно указано, что SomeFunction() будет вызываться всегда перед конструктором Sample(). Так что вопрос сузится только между SomeFunction() и operator new.   -  person iammilind    schedule 04.05.2011


Ответы (5)


Порядок не указан. [5.3.4] / 21 гласит:

Не определено, вызывается ли [operator new] перед оценкой аргументов конструктора или после оценки аргументов конструктора, но до входа в конструктор. Также не указано, оцениваются ли аргументы конструктора, если [operator new] возвращает нулевой указатель или завершается с использованием исключения.

person decltype    schedule 04.05.2011
comment
+1: единственное, что должно выполняться, - это то, что функция распределения должна возвращаться до вызова конструктора. - person CB Bailey; 04.05.2011
comment
@ Чарльз Бейли: Верно. Фактически, единственными возможными последовательностями (при условии, что new не генерирует) являются: a) оператор new, SomeFunction (), Sample ctor b) SomeFunction (), operator new, Sample ctor - person decltype; 04.05.2011

Порядок вызовов операторов new и SomeFunction не указан - поэтому он может измениться в зависимости от настроек оптимизации, версии компилятора и т. Д.

Я думаю, вызов конструктора должен быть последним.

person Alan Stokes    schedule 04.05.2011

Да, это можно было чередовать.

class A
{
public:
    A(int i)
    {
        cout << "constructor" << endl;
    }
    void* operator new(size_t size)
    {
        cout << "new" << endl;
        return malloc(size);
    }
    void operator delete(void*, size_t)
    {
        cout << "delete" << endl;
    }
};

int f()
{
    cout << "f()" << endl;
    return 1;
}

int main()
{
    A* a = new A(f());
}

Output:
new
f()
constructor

Хотя это и не гарантируется стандартом, у компиляторов есть причина сначала выделить память. В случае сбоя выделения памяти конструктор не будет вызываться вообще. Поэтому слишком ранняя оценка аргументов конструктора, вероятно, не лучшая идея.

person Eric Z    schedule 04.05.2011
comment
С каким компилятором это происходит? - person sharptooth; 04.05.2011
comment
С таким же успехом можно сказать, что если оценка аргумента вызывает исключение, конструктор вообще не будет вызываться, и поэтому слишком ранний вызов new - плохая идея! - person Steve Jessop; 04.05.2011

На самом деле, я думаю, происходит следующее:

  • new используется для выделения необработанной памяти
  • SomeFunction () вызывается, возвращая значение X
  • вызывается конструктор с X в качестве параметра

но я мог ошибаться. Я бы сказал, что это показывает, что вам не следует беспокоиться о порядке.

person Community    schedule 04.05.2011
comment
За исключением того, что порядок первых двух пунктов может быть изменен на обратный. Если SomeFunction вернет указатель на динамически выделяемую память, возникнет причина беспокоиться о порядке. В этом случае код следует переписать для обеспечения безопасности исключений. - person decltype; 04.05.2011

Вы не можете изменить то, что происходит, когда вы запускаете эту строку кода. Вы можете запускать несколько разных строк кода.

void * p = ::operator new (sizeof (SomeFunction));
SomeFunction temp;
SomeFunction* sample = new (p) SomeFunction(temp);
person bmargulies    schedule 04.05.2011