Я отсутствую по веской причине.
Это зависит от того, как вы определяете "неотразимо".Многие из аргументов, которые вы до сих пор отвергали, безусловно, являются неотразимыми для большинства программистов на C ++, поскольку ваше предложение не является стандартным способом выделения голых массивов в C ++.
Простой факт заключается в следующем: да, вы абсолютно точно можете сделатьвещи, как вы описываете.Нет никакой причины, что то, что вы описываете, не будет функцией .
Но опять же, вы можете иметь виртуальные функции в C. Вы можете реализовать классы и наследование в простом C, если вы поставитевремя и усилия в этом.Они также полностью функциональны.
Следовательно, важно не то, может ли что-то работать .Но больше о том, что стоит .Реализация наследования и виртуальных функций в C гораздо более подвержена ошибкам, чем в C ++.Есть несколько способов реализовать это в C, что приводит к несовместимым реализациям.Принимая во внимание, что, поскольку они являются первоклассными языковыми возможностями C ++, маловероятно, что кто-то вручную реализует то, что предлагает язык.Таким образом, все наследующие и виртуальные функции могут взаимодействовать с правилами C ++.
То же самое относится и к этому.Так каковы выгоды и потери от ручного управления malloc / free массивом?
Я не могу сказать, что что-либо из того, что я собираюсь сказать, является для вас «веской причиной».Я скорее сомневаюсь, что это произойдет, поскольку вы, похоже, уже приняли решение.Но для записи:
Производительность
Вы заявляете следующее:
Насколько я могу судить, последнее намного эффективнее, чем первое (так как выне инициализируйте память некоторыми неслучайными конструкторами значений / вызовов по умолчанию), и единственное отличие на самом деле заключается в том факте, что вы очищаете с помощью:
Это утверждение предполагает, что повышение эффективностипрежде всего в строительстве рассматриваемых объектов.То есть, какие конструкторы называются.Этот оператор предполагает, что вы не хотите вызывать конструктор по умолчанию;что вы используете конструктор по умолчанию просто для создания массива, а затем используете реальную функцию инициализации для помещения фактических данных в объект.
Ну ... что если это не то, что вы хотите сделать?Что если вы хотите создать массив empty , созданный по умолчанию?В этом случае это преимущество полностью исчезает.
Хрупкость
Предположим, что каждый объект в массиве должен иметь специализированный конструктор или что-то вызываемое для него, так что инициализация массива требует такого родавещи.Но рассмотрите ваш код уничтожения:
for (int i=0;i<MY_ARRAY_SIZE;++i) my_array[i].~T();
Для простого случая это нормально.У вас есть макрос или константная переменная, которая говорит, сколько объектов у вас есть.И вы зацикливаетесь на каждом элементе, чтобы уничтожить данные.Это хорошо для простого примера.
Теперь рассмотрим настоящее приложение , а не пример.В скольких разных местах вы будете создавать массив?Десятки?Сотни?Каждый из них должен иметь свой собственный цикл for
для инициализации массива.У каждого из них должен быть свой цикл for
для уничтожения массива.
Неправильный ввод этого кода даже один раз, и вы можете повредить память.Или не удалить что-то.Или множество других ужасных вещей.
И вот важный вопрос: для какого массива вы храните размер ?Знаете ли вы, сколько элементов вы выделили для каждого создаваемого вами массива?Каждый массив, вероятно, будет иметь свой собственный способ узнать, сколько элементов он хранит.Таким образом, каждый цикл деструктора должен будет правильно извлекать эти данные.Если это не так ... бум.
И тогда у нас есть исключительная безопасность, которая представляет собой совершенно новую банку червей.Если один из конструкторов выдает исключение, ранее построенные объекты должны быть уничтожены.Ваш код этого не делает;это не исключение-исключение.
Теперь рассмотрим альтернативу:
delete[] my_array;
Это не может не сработать. Это всегда уничтожит каждый элемент. Он отслеживает размер массива и безопасен для исключений. Так что гарантировано для работы. Он не может не работать (если вы выделите его с помощью new[]
).
Конечно, вы могли бы сказать, что можете обернуть массив в объект. В этом есть смысл. Вы могли бы даже шаблон объекта на элементах типа массива. Таким образом, весь код деструктора одинаков. Размер содержится в объекте. И, может быть, просто возможно, вы понимаете, что пользователь должен иметь некоторый контроль над определенным способом распределения памяти, так что это не просто malloc/free
.
Поздравляем: вы только что изобрели std::vector
.
Именно поэтому многие программисты на C ++ даже не набирают new[]
.
Гибкость
Ваш код использует malloc/free
. Но допустим, я занимаюсь профилированием. И я понимаю, что malloc/free
для некоторых часто создаваемых типов просто слишком дорого. Я создаю специальный менеджер памяти для них. Но как подключить к ним все массивы?
Ну, мне нужно искать в кодовой базе любое место, где вы создаете / уничтожаете массивы этих типов. И тогда я должен изменить их распределители памяти соответственно. И затем я должен постоянно наблюдать за кодовой базой, чтобы кто-то другой не изменил эти распределители назад или не ввел новый код массива, который использует другие распределители.
Если бы вместо этого я использовал new[]/delete[]
, я мог бы использовать перегрузку операторов. Я просто предоставляю перегрузку для операторов new[]
и delete[]
для этих типов. Код не должен меняться. Кому-то гораздо сложнее обойти эти перегрузки; они должны активно пытаться. И пр.
Так что я получаю большую гибкость и разумную уверенность в том, что мои распределители будут использоваться там, где их следует использовать.
читаемость
Учтите это:
my_object *my_array = new my_object[10];
for (int i=0; i<MY_ARRAY_SIZE; ++i)
my_array[i]=my_object(i);
//... Do stuff with the array
delete [] my_array;
Сравните это с:
my_object *my_array = (my_object *)malloc(sizeof(my_object) * MY_ARRAY_SIZE);
if(my_object==NULL)
throw MEMORY_ERROR;
int i;
try
{
for(i=0; i<MY_ARRAY_SIZE; ++i)
new(my_array+i) my_object(i);
}
catch(...) //Exception safety.
{
for(i; i>0; --i) //The i-th object was not successfully constructed
my_array[i-1].~T();
throw;
}
//... Do stuff with the array
for(int i=MY_ARRAY_SIZE; i>=0; --i)
my_array[i].~T();
free(my_array);
Объективно говоря, какой из них легче читать и понимать, что происходит?
Просто посмотрите на это утверждение: (my_object *)malloc(sizeof(my_object) * MY_ARRAY_SIZE)
. Это очень вещь низкого уровня. Вы не выделяете массив чего-либо; Вы выделяете кусок памяти. Вы должны вручную вычислить размер фрагмента памяти, чтобы соответствовать размеру объекта * количество объектов, которое вы хотите. Это даже показывает бросок.
Напротив, new my_object[10]
рассказывает историю. new
- ключевое слово C ++ для «создания экземпляров типов». my_object[10]
- это массив из 10 элементов типа my_object
. Это просто, очевидно и интуитивно понятно. Там нет приведения, нет вычисления байтов, ничего.
Метод malloc
требует изучения как использовать malloc
идиоматически. Для метода new
необходимо просто понять, как работает new
. Это гораздо менее многословно и гораздо более очевидно, что происходит.
Кроме того, после оператора malloc
на самом деле у вас нет массива объектов. malloc
просто возвращает блок памяти, который вы указали компилятору C ++, чтобы он притворился указателем на объект (с приведением). Это не массив объектов, потому что объекты в C ++ имеют время жизни. И время жизни объекта не начинается, пока он не будет построен. Ничто в этой памяти еще не вызывало конструктор, и поэтому в нем нет живых объектов.
my_array
в этот момент не является массивом; это просто блок памяти. Он не станет массивом my_object
с, пока вы не создадите их на следующем шаге. Это невероятно не интуитивно понятно новому программисту; Требуется опытная рука C ++ (которая, вероятно, узнала от C), чтобы понять, что это не живые объекты и с ними нужно обращаться осторожно. Указатель еще не ведет себя как правильный my_object*
, поскольку он еще не указывает ни на один my_object
.
Напротив, у вас do есть живые объекты в случае new[]
. Объекты были построены; они живы и полностью сформированы. Вы можете использовать этот указатель, как и любой другой my_object*
.
Fin
Ничто из вышеперечисленного не говорит о том, что этот механизм потенциально бесполезен при правильных обстоятельствах. Но одно дело признать полезность чего-либо в определенных обстоятельствах. Другое дело сказать, что это должен быть способ по умолчанию .