CheckedArray::CheckedArray(int size) :mSize(size){
int *mArray = new int[size];
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
CheckedArray::~CheckedArray() {
if (mArray == NULL){
return;
}
else {
delete[] mArray;
}
}
Я использую динамическое выделение памяти для создания нового массива. Я хочу проверить, является ли указатель нулевым, а затем вернуться. Если нет, то удалите. Я получаю эти сообщения об ошибках, но я понятия не имею, что не так.
(9094,0x100094600) malloc: *** ошибка для объекта 0x10001e7b3: освобождаемый указатель не был выделен
(9094,0x100094600) malloc: *** установить точку останова в malloc_error_break для отладки
int *mArray = new int[size];
создает локальную переменную и утечку памяти, поскольку после завершения функции указатель теряется. Если у вас есть переменная-член с именем mArray
, вы должны использовать вместо нее mArray = new int[size];
. Нет необходимости проверять, является ли указатель NULL
или нет перед его удалением. Удаление указателя NULL
— это нормально, это просто недопустимо.
Другие примечания: не используйте NULL, а используйте nullptr
в C++. И во всех ваших конструкторах инициализируйте указатель переменной-члена на nullptr, если вы действительно не выделяете память.
Вы инициализируете размер, используя список инициализаторов элементов, почему бы вам не сделать это и для массива?
Также обратите внимание, что, как и для free
, delete
и delete[]
изящно обрабатывают нулевые указатели и ничего не делают в этом случае. Поэтому никогда не нужно проверять наличие нулевых указателей перед освобождением памяти.
Также помните, что если класс владеет ресурсом , он также отвечает за правильное копирование и перемещение этого ресурса .
По умолчанию выражение new
не может возвращать нулевой указатель (вместо этого оно генерирует исключение). Таким образом, деструктору не нужно проверять, является ли mArray
значением null, если конструкторы (и другие функции-члены, изменяющие mArray
) ведут себя разумно. Поскольку у вас есть один конструктор с выражением new
, я подозреваю, что ваша реальная проблема заключается в том, что вы не следовали «правилу трех» или (возможно) «правилу пяти» - в результате чего ваш деструктор для некоторых объектов delete[]
ing одни и те же данные дважды (вызывая неопределенное поведение).
Вот пример того, что std::unique_ptr может сделать для вас:
#include <iostream>
#include <algorithm>
#include <memory>
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r11-avoid-calling-new-and-delete-explicitly
// so use https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
// prefer range based for loops, they can't go out of bounds : https://en.cppreference.com/w/cpp/language/range-for
class dynamic_int_array_t final
{
public:
// creates an "empty array" with enough memory for 4 ints.
dynamic_int_array_t() :
m_capacity{ 4ul }, // start with a capacity for 4 ints.
m_size{ 0ul }, // but actually none are stored yet
m_values{ std::make_unique<int[]>(m_capacity) }
{
}
// allows you to construct an array from a list of integers
dynamic_int_array_t(std::initializer_list<int>&& values) :
m_capacity{ values.size() },
m_size{ values.size() },
m_values{ std::make_unique<int[]>(m_capacity) }
{
std::copy(values.begin(), values.end(), m_values.get());
}
~dynamic_int_array_t() = default; // destructor will destruct unique_ptr and free memory
// non-copyable non-movable (simplifies things for now)
dynamic_int_array_t(const dynamic_int_array_t&) = delete;
dynamic_int_array_t& operator=(const dynamic_int_array_t&) = delete;
dynamic_int_array_t(dynamic_int_array_t&&) = delete;
dynamic_int_array_t& operator=(dynamic_int_array_t&&) = delete;
// begin and end allow range based for loops to work
// range based for loops don't allow you to go out of bounds.
auto begin() const
{
return m_values.get();
}
// end should point "one past" the array (that's how end works)
auto end() const
{
int* ptr = begin();
ptr += m_size;
return ptr;
}
std::size_t size() const
{
return m_size;
}
void add(const int value)
{
// if not enough memory then allocate more
if (m_size == m_capacity) grow_capacity();
// add new value at the end
m_values[m_size] = value;
m_size++;
}
// add another array to this one
void append(const dynamic_int_array_t& rhs)
{
for (int value : rhs)
{
add(value);
}
}
private:
void grow_capacity()
{
m_capacity *= 2;
// allocate new memory
auto tmp = std::make_unique<int[]>(m_capacity);
// copy content to new memory
std::copy(begin(), end(), tmp.get());
// swap new memory with tmp so m_values will now be the newly allocated memory and tmp will hold the previously allocated memory
std::swap(tmp, m_values);
// tmp will go out of scope and delete old buffer
}
std::size_t m_capacity;
std::size_t m_size;
std::unique_ptr<int[]> m_values;
};
int main()
{
dynamic_int_array_t array{ 4,5 };
for (int n = 10; n < 20; ++n)
{
array.add(n);
}
for (const int value : array)
{
std::cout << value << " ";
}
return 0;
}
Чтобы было совершенно ясно
CheckedArray::CheckedArray(int size) :mSize(size){
int *mArray = new int[size];
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
должно быть
CheckedArray::CheckedArray(int size) :mSize(size), mArray(new int[size]){
for(int i = 0; i < size; i++)
mArray[i] = 0;
}
Ваша версия создает локальную переменную mArray
, которая скрывает одноименную переменную класса.
Почему бы не использовать std::unique_ptr<int[]>? избегайте явного вызова new/delete и сделайте его переменной-членом