«новый» оператор и массивы typedef в C++

Возможный дубликат:
Можно ли динамически создать массив постоянного размера в C++?

Это скорее теоретический вопрос - мне интересно, почему на самом деле оператор new[] в C++ возвращает указатель на первый элемент массива, а не на фактический массив (или указатель на него). Это пришло ко мне, когда я пытался сделать что-то вроде

typedef int int4[4];
int4* ni4 = new int4;

Хотя я знаю, почему это не работает (хотя это было не так ясно в начале ;)), меня действительно глючит тот код, который в принципе A* ptr= new A; не компилируется. Мне одному это кажется странным?


person j_kubik    schedule 28.04.2011    source источник
comment
Это опечатка или вы действительно имели в виду typedef int[4] int4; ?   -  person Eric Fortin    schedule 28.04.2011
comment
@Eric: Это правильный синтаксис typedef. Но в следующей строке опечатка, должно быть int4* ni4 = new int4;   -  person Ben Voigt    schedule 28.04.2011
comment
с int[4] int4 я получаю сообщение об ошибке: ожидаемый неквалифицированный идентификатор перед токеном «[»; Вторая опечатка исправлена   -  person j_kubik    schedule 28.04.2011
comment
Хам не знал, что мы можем использовать typedef таким образом, приятно знать, даже если я никогда не буду его использовать.   -  person Eric Fortin    schedule 28.04.2011


Ответы (4)


Что мне кажется странным, так это то, что используется operator new[]. Код пытается выделить один экземпляр агрегата, что было бы допустимо, если бы агрегат был struct.

Но это поведение предусмотрено стандартом в разделе [expr.new].

Однако есть очень простой обходной путь:

typedef int int4[4];
int4* ni4 = new int4[1];

...

delete [] ni4;
person Ben Voigt    schedule 28.04.2011
comment
Это действительно странно, что это стандартное поведение. Возможно, typedef массива не рассматривается как совокупность? В любом случае, спасибо за подсказку с обходным решением - кажется, это намного лучше, чем int4* ni4 =(int4*) new int4;, который я использовал. Но все же мой вопрос остается - почему стандарт сделан именно так? Я всегда думал, что стандарт нацелен на полную корректность типов - это похоже на нарушение... - person j_kubik; 28.04.2011
comment
Почему это было бы странно? Код пытается выделить массив, поэтому используется operator new[]. Вот как стандарт определяет семантику. Код, который показывает парень, эквивалентен new int[4], который выделяет точно такой же тип объекта. - person Johannes Schaub - litb; 28.04.2011
comment
@Johannes: В new int[4] спецификатор массива [4] на самом деле является частью грамматики для нового выражения, а не частью типа. Обычно я ожидаю, что new-expression с необязательным noptr-new-declarator вызовет operator new[](), а без вызова operator new(). Текущее поведение типов массивов в особых случаях по сравнению с другими агрегатами затрудняет написание кода шаблона. - person Ben Voigt; 28.04.2011
comment
int[4] является new-type-id и указывает тип. Если вы используете его как new int[4], он сообщает системе, что она должна создать объект типа int[4]. Наверняка [4] анализируется какой-то грамматической продукцией. Все :) Правила полностью определены в терминах: если объект массива создан, вызовите 'operator new[]', в противном случае используйте 'operator new'. Я тоже думаю, что не совсем правильно ожидать того, что вы говорите. new T определенно выглядит иначе, чем new U[N]. Но с точки зрения языка это то же самое, когда T является типом U[N]. - person Johannes Schaub - litb; 28.04.2011

Вероятно, можно было бы аргументировать следующее

new (int[N]); // type int(*)[N]
new int[N]; // type int*
new T; /* T* */

Только в среднем случае N может быть значением времени выполнения. Тем не менее, спецификация не устанавливает такого различия типов. Массивы в любом случае нуждаются в специальной обработке почти во всех случаях (например, вы также не можете просто скопировать их). Так что вы должны быть готовы обращаться с ними специально. Например, в вашем случае вы также должны использовать delete[] вместо delete.

Просто чтобы было ясно, если вышесказанное будет правдой, вам понадобится неуклюжий синтаксис

int (*p)[N] = new (int[N]);
(*p)[N-1] = 0;
p[0][N-1] = 0; /* or, equivalently */
p[N-1] = 0; /* but not this, error! */

Сначала вам нужно разыменовать указатель массива.

person Johannes Schaub - litb    schedule 28.04.2011
comment
или просто int *p= new int[N]; - если вышесказанное верно ;) но это зависит от того, что вам нужно - например. рассматривайте (int[N]) как совокупность (с struct { int f[N]; } это будет работать так, как я хочу). - person j_kubik; 28.04.2011
comment
@j_kubik Я не понимаю, что вы имеете в виду, говоря о том, что (int [N]) является агрегатом. int[N] сам по себе является агрегатом. Массивы являются агрегатами. - person Johannes Schaub - litb; 28.04.2011
comment
Вот что я имею в виду - массивы являются агрегатами, но их семантика отличается от скажем структур - если вы создаете структуру new, то возвращаемый указатель указывает на всю структуру, а не на ее часть. В случае массива я ожидаю, что будет возвращен указатель на массив. - person j_kubik; 27.03.2013

Это наследие C, которое на самом деле было наследием B. Вся обработка нативных массивов в C++ ужасна, но они не могут ее изменить. Это относится ко многим проблемам в C++.

person Puppy    schedule 28.04.2011

Я не совсем уверен, понимаю ли я ваш вопрос. В C++, как и в C, нет разницы между указателем на первый элемент массива и самим массивом.

РЕДАКТИРОВАТЬ: Как мне было указано, это не совсем правильно - пожалуйста, простите мою ошибку, я недавно тратил слишком много времени на Java и С# ;-)

person chris    schedule 28.04.2011
comment
Разница конечно есть - это разные типы. sizeof даст разные результаты, и вы можете писать функции, которые принимают ссылки на массивы (определенного размера), но не на указатели. - person Mike Seymour; 28.04.2011
comment
Это неправильно, есть большая разница. Массив не является указателем, но в некоторых случаях его можно рассматривать как указатель. Некоторые отличия: 1. другая sizeof 2. другая инициализация - person Mihran Hovsepyan; 28.04.2011