#cpp #language_lawyer #delete
Стандартный способ выделить массив из 5 элементов - так: int *a = new int[5]; ну и удаляется он delete[] a; А что если добавить скобки? int *b = new (int[5]); Круглые скобки заставляют выделять один объект, являющийся массивом из 5 элементов, а не массив из 5 элементов. Это подтверждается сообщением об ошибке при попытке подставить переменную в размер массива (где d=): http://codepad.org/BvHyTPs6 #includeusing namespace std; int main() { int *a = new int[5]; int *b = new (int[5]); int n = 5; int *c = new int[n]; int *d = new (int[n]); return 0; } In function 'int main()': Line 12: error: ISO C++ forbids variable-size array compilation terminated due to -Wfatal-errors. Чем массив b отличается от массива a и как правильно освободить за собой память, выделенную таким образом? delete b; или всё-таки delete[] b; и почему? А может через такой указатель вообще удалять нельзя, потому что его тип отличается от типа выделенного объекта? Кстати, на ideone аналогичный код компилируется, как и обычный variable-size array: https://ideone.com/MGmBya. Этот факт как-то влияет на освобождение памяти? Также выяснилось, что компилятор не хочет выводить константность размера из такого объявления https://ideone.com/XwtTMQ & http://codepad.org/p3VKqMvd #include using namespace std; template void f(int a[n]) { cout << n << endl; } int main() { f(new (int[5])); return 0; } prog.cpp: In function ‘int main()’: prog.cpp:12:17: error: no matching function for call to ‘f(int*)’ f(new (int[5])); ^ prog.cpp:5:26: note: candidate: ‘template void f(int*)’ template void f(int a[n]) ^ prog.cpp:5:26: note: template argument deduction/substitution failed: prog.cpp:12:17: note: couldn't deduce template parameter ‘n’ f(new (int[5])); ^ Тогда возникает вопрос, почему та же конструкция с переменной - это VLA, а с константой - простой указатель.
Ответы
Ответ 1
Ключевой момент, как уже заметил @VTT в комментариях: Круглые скобки заставляют выделять один объект, являющийся массивом из 5 элементов, а не массив из 5 элементов. это неправда. #includeint main() { int ptr[5] = {0, 1, 2, 3, 4}; auto p1 = new (ptr)(int[5]); auto p2 = new (int[5]); std::cout << p1[3] << std::endl; delete []p2; // ok delete []p1; // error return 0; } new expression - вариант синтаксиса (1), с указанием типа в скобках То есть вы по прежнему выделяете массив из 5 элементов. ::(optional) new (placement_params)(optional) ( type ) initializer(optional) вот описание синтаксиса new expression, собcтвенно вы указываете тип в скобках. Вот здесь: auto p2 = new (int[5]); Типом будет массив из 5 элементов. Это то же самое что написать auto p2 = new int[5]; только это уже второй вариант синтаксиса, когда тип указывается без скобок. И во втором варианте (без скобок) вы можете в качестве первой размерности задавать переменную, которую можно привести к std::size_t, а в первом варианте так делать уже нельзя. То есть можно: int n = 5; auto p = new int[n][5]; Но нельзя int n = 5; auto p = new int[5][n]; auto p1 = new (int[n][5]); If type is an array type, all dimensions other than the first must be specified as positive integral constant expression (until C++14)converted constant expression of type std::size_t (since C++14), but (only when using un-parenthesized syntax (2)) the first dimension may be any expression convertible to std::size_t. This is the only way to directly create an array with size defined at runtime, such arrays are often referred to as dynamic arrays Ответ 2
Компилятор смотрит тип предоставленный оператору new и если этот тип является массивом, то возвращаемое значение будет указателем на первый элемент. int * ip = new int[1]; ip имеет тип указателя на int, а тип int[1] - это уже массив. Второй случай, где оператору new предоставлен тип не массив: char * cp = new char ; Здесь cp это указатель на char, а аргумент оператору тоже char. Чтобы определить, как удалять память нужно сравнить типы *указатель и аргумент new. При сложном случае, когда типы неизвестны (typedef или шаблон) можно воспользоваться сравнением типа с помощью typeid. // g++ -Wall -Wextra -Wpedantic -Os -std=c++11 newarr3.cpp #include#include using namespace std; class C{ public: int i; C(){cout<<"C;";} ~C(){cout<<"~C;";} }; typedef C A[5]; typedef C B[1] ; typedef C D ; int main() { cout<<"auto ap = new A :"< "<< "typeid(A).name = "< "<< "typeid(B).name = "< "<< "typeid(D).name = "< typeid(A).name = A5_1C ~C;~C;~C;~C;~C; C; typeid(*bp).name = 1C <> typeid(B).name = A1_1C ~C; C; typeid(*dp).name == typeid(D).name == 1C ~C; В вашем примере однозначно видны типы. Возвращаемый тип это указатель на int, а память выделяется на тип массива. Удалять нужно с помощью delete[].
Комментариев нет:
Отправить комментарий