#c #class #constructor #new-operator
#c #класс #конструктор #new-operator
Вопрос:
Предположим, у меня есть следующий класс:
class Sample {
public:
Sample( int ) {}
};
некоторая функция, возвращающая int
int SomeFunction()
{
return 0;
}
и этот код:
Sample* sample = new Sample( SomeFunction() );
Теперь я ожидаю следующую последовательность:
SomeFunction()
выполняется, затем::operator new()
выполняется для выделения памяти для объекта, затемclass Sample
конструктор запускается через выделенную память
Этот порядок фиксирован или он может быть изменен реализацией таким образом, что, скажем, сначала выделяется память, затем SomeFunction()
вызывается, затем запускается конструктор? Другими словами, могут ли вызовы operator new()
функции и конструктора класса чередоваться с чем угодно?
Комментарии:
1. обязательно указана одна вещь, которая
SomeFunction()
будет вызываться всегда перед constructorSample()
. Таким образом, вопрос сузится только междуSomeFunction()
иoperator new
.
Ответ №1:
Порядок не указан. [5.3.4]/21 чтение:
Вызывается ли [operator new] перед вычислением аргументов конструктора или после вычисления аргументов конструктора, но перед вводом конструктора, не указано. Также не указано, вычисляются ли аргументы конструктора, если [operator new] возвращает нулевой указатель или завершается с использованием исключения.
Комментарии:
1. 1: Единственное, что должно соблюдаться, это то, что функция выделения должна возвращаться до вызова конструктора.
2. @Charles Bailey: Верно. Фактически единственными возможными последовательностями (при условии, что new не выдает) являются: a) оператор new, someFunction(), пример ctor b) someFunction(), оператор new, пример ctor
Ответ №2:
Порядок вызовов operator new и someFunction не указан, поэтому он может меняться в зависимости от настроек оптимизации, версии компилятора и т.д.
Вызов конструктора, я думаю, должен выполняться последним.
Ответ №3:
Да, это можно чередовать.
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
Хотя стандарт и не гарантирует, у компиляторов есть причина сначала выделить память. Если выделение памяти завершается неудачей, конструктор вообще не будет вызван. Поэтому слишком ранняя оценка аргументов конструктора, вероятно, не очень хорошая идея.
Комментарии:
1. С каким компилятором это происходит?
2. С таким же успехом вы могли бы сказать, что если вычисление аргумента выдает исключение, то конструктор вообще не будет вызван, и поэтому вызов
new
слишком рано — плохая идея!
Ответ №4:
На самом деле, я думаю, происходит следующее:
- new используется для выделения необработанной памяти
- Вызывается someFunction(), возвращающая значение X
- вызывается конструктор с X в качестве параметра a
но я могу ошибаться. Я бы сказал, что это показывает, что вам не следует беспокоиться о порядке.
Комментарии:
1. За исключением того, что порядок первых двух маркерных точек может быть изменен на противоположный. Была бы причина беспокоиться о порядке, если бы someFunction возвращала указатель на динамически выделяемую память, и в этом случае код следует переписать для безопасности исключений.
Ответ №5:
Вы не можете изменить то, что происходит при запуске этой строки кода. Вы можете запускать несколько разных строк кода.
void * p = ::operator new (sizeof (SomeFunction));
SomeFunction temp;
SomeFunction* sample = new (p) SomeFunction(temp);