Как зайти в Даркнет?!
25th January, 01:11
5
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
893
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
912
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4380
0
Помогите пожалуйста решить задачи
24th November, 23:53
6084
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4350
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4395
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Можно ли использовать новое размещение для массивов переносным способом?
Можно ли на самом деле использовать размещение нового в переносимом коде при использовании его для массивов?
Похоже, что указатель, который вы получаете от new[], не всегда совпадает с адресом, который вы передаете (5.3.4, Примечание 12 в стандарте, кажется, подтверждает, что это правильно), но я не вижу, как вы можете выделить буфер для массива, чтобы войти в него, если это так.
В следующем примере показана проблема. Скомпилированный с помощью Visual Studio, этот пример приводит к повреждению памяти:
#include <new>
#include <stdio.h>
class A
{
public:
A() : data(0) {}
virtual ~A() {}
int data;
};
int main()
{
const int NUMELEMENTS=20;
char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
A *pA = new(pBuffer) A[NUMELEMENTS];
// With VC++, pA will be four bytes higher than pBuffer
printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);
// Debug runtime will assert here due to heap corruption
delete[] pBuffer;
return 0;
}
Глядя на память, компилятор, похоже, использует первые четыре байта буфера для хранения подсчета количества элементов в нем. Это означает, что поскольку буфер имеет только sizeof(A)*NUMELEMENTS размер, последний элемент массива записывается в нераспределенную кучу.
Итак, вопрос в том, Можете ли вы узнать, сколько дополнительных накладных расходов требуется вашей реализации для безопасного использования placement new[]? В идеале мне нужна техника, переносимая между разными компиляторами. Обратите внимание, что, по крайней мере, в случае VC, накладные расходы, похоже, отличаются для разных классов. Например, если я удаляю виртуальный деструктор в Примере, то адрес, возвращаемый из new[], совпадает с адресом, который я передаю.
Лично я бы предпочел не использовать placement new в массиве, а вместо этого использовать placement new для каждого элемента в массиве по отдельности. Например:
int main(int argc, char* argv[])
{
const int NUMELEMENTS=20;
char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
A *pA = (A*)pBuffer;
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i] = new (pA + i) A();
}
printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);
// dont forget to destroy!
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i].~A();
}
delete[] pBuffer;
return 0;
}
Независимо от используемого метода, убедитесь, что вы вручную уничтожили каждый из этих элементов в массиве, прежде чем удалить pBuffer, так как это может привести к утечке данных ;)
Примечание: Я не компилировал это, но я думаю, что это должно работать (я нахожусь на машине, на которой не установлен компилятор C++). Это все еще указывает на точку :) надеюсь, что это поможет в некотором роде!
Редактировать:
Причина, по которой он должен отслеживать количество элементов, заключается в том, что он может перебирать их при вызове delete в массиве и убедиться, что деструкторы вызываются для каждого из объектов. Если бы он не знал, сколько их там, то не смог бы этого сделать.
@Derek
5.3.4, раздел 12 говорит о накладных расходах на выделение массива, и, если я не ошибаюсь, мне кажется, что компилятор может добавить его также и при новом размещении:
Эти накладные расходы могут быть применены во всех выражениях массива new-expressions, включая те, которые ссылаются на оператор библиотечной функции new[](std::size_t, void*) и другие функции распределения размещения. Объем накладных расходов может варьироваться от одного вызова new к другому.
Тем не менее, я думаю, что VC был единственным компилятором, который дал мне проблемы с этим, из него, GCC, Codewarrior и ProDG. Хотя мне придется проверить еще раз, чтобы убедиться в этом.
Спасибо за ответы. Использование размещения new для каждого элемента в массиве было решением, которое я использовал, когда столкнулся с этим (извините, я должен был упомянуть об этом в вопросе). Я просто чувствовал, что, должно быть, что-то упустил, делая это с размещением new[]. Как бы то ни было, похоже, что placement new[] по существу непригоден для использования благодаря стандарту, позволяющему компилятору добавлять дополнительные неопределенные накладные расходы к массиву. Я не понимаю, как вы могли бы использовать его безопасно и переносимо.
Я даже не совсем понимаю, зачем ему нужны дополнительные данные, поскольку вы все равно не вызовете delete[] в массиве, поэтому я не совсем понимаю, зачем ему нужно знать, сколько элементов в нем находится.
@James
Я даже не совсем понимаю, зачем ему нужны дополнительные данные, поскольку вы все равно не вызовете delete[] в массиве, поэтому я не совсем понимаю, зачем ему нужно знать, сколько элементов в нем находится.
Поразмыслив немного, я соглашусь с вами. Нет никакой причины, по которой placement new должен хранить количество элементов, потому что нет никакого placement delete. Так как там нет размещения delete, нет никакой причины для размещения new, чтобы сохранить количество элементов.
Я также протестировал это с gcc на моем Mac, используя класс с деструктором. В моей системе размещение нового не изменяло указатель. Это заставляет меня задуматься, является ли это проблемой VC++, и может ли это нарушить Стандарт (Стандарт специально не рассматривает это, насколько я могу найти).
Само по себе размещение new является переносимым, но предположения, которые вы делаете о том, что оно делает с определенным блоком памяти, не являются переносимыми. Как было сказано ранее, если бы Вы были компилятором и вам дали кусок памяти, как бы вы знали, как выделить массив и правильно уничтожить каждый элемент, если бы у вас был только указатель? (См. интерфейс оператора delete[].)
Редактировать:
И там на самом деле есть placement delete, только он вызывается только тогда, когда конструктор создает исключение при выделении массива с placement new[].
Действительно ли new[] нужно каким-то образом отслеживать количество элементов-это то, что остается до стандарта, который оставляет это на усмотрение компилятора. К сожалению, в данном случае.
Аналогично тому, как вы использовали бы один элемент для вычисления размера для одного размещения-new, используйте массив этих элементов для вычисления размера, необходимого для массива.
Если вам требуется размер для других вычислений, где количество элементов может быть неизвестно, Вы можете использовать sizeof (A[1]) и умножить его на необходимое количество элементов.
е.г
char *pBuffer = new char[ sizeof(A[NUMELEMENTS]) ];
A *pA = (A*)pBuffer;
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i] = new (pA + i) A();
}