2010-04-02 3 views
4

Я написал этот код.
Конструктор работает нормально, но в деструкторе я получаю «Windows вызвала точку останова». Как мне исправить это?Проблемы с удалением в destructor

template class CyclicalArray { 
private: 
    T* mem_ptr; 
public: 
    CyclicalArray(size_t capacity, const T& default_value) { 
    this->default_value = default_value; 
    this->capacity = capacity; 
    head_index = 0; 
    mem_ptr = ::new T[capacity]; //memory allocating 
    for(T* p = mem_ptr; p < mem_ptr + capacity * sizeof(T); p += sizeof(T)) { 
     ::new (p) T (default_value); //initialization 
    } 
} 
~CyclicalArray() { 
    for(T* p = mem_ptr + sizeof(T); p < mem_ptr + capacity * sizeof(T); p += sizeof(T)) { 
     p->~T(); 
    } 
    delete[] mem_ptr; 
} 

ответ

8

Если вы собираетесь разместить новое место, вам нужно сделать это в необработанной памяти. Что-то вроде:

template class CyclicalArray { 
private: 
    T* mem_ptr; 
public: 
    CyclicalArray(size_t capacity, const T& default_value) { 
    this->default_value = default_value; 
    this->capacity = capacity; 
    head_index = 0; 
    mem_ptr = reinterpret_cast<T*>(::new char[capacity * sizeof(T)]); //memory allocating 
    for(T* p = mem_ptr; p < mem_ptr + capacity; ++p) { 
     ::new (p) T (default_value); //initialization 
    } 
} 
~CyclicalArray() { 
    // this 
    for(T* p = mem_ptr + capacity; p != mem_ptr; --p) { 
     (p-1)->~T(); 
    } 
    delete[] reinterpret_cast<char*>(mem_ptr); 
} 

В противном случае вы будете называть T деструктор дважды на одной и той же памяти объекта (не очень хорошая вещь, чтобы сделать).

Кроме того, так как ваши p указателей типа T*, вы можете выполнять простой инкремент/декремент на нем - компилятор будет иметь дело с проблемой sizeof(T) как обычный курс арифметики указателей.

Наконец, строго говоря, вы должны уничтожить элементы массива в порядке убывания (противоположность конструкции).

Я надеюсь, что это поймает большинство или все ошибки.

Возможно, вы действительно захотите использовать что-то вроде std :: vector в качестве магазина. Ниже приведен пример с использованием std::vector<> (с несколькими другими исправлениями синтаксиса). Я не уверен, если ваш класс действительно нужна копия default_value или head_index - я оставил их в предположении, что вы планируете использовать их в других методах:

#include <vector> 

template <typename T> 
class CyclicalArray { 
private: 
    std::vector<T> backing_store; 
    T default_value; 
    size_t head_index; 

public: 
    CyclicalArray(size_t capacity, const T& def_val) : 
     backing_store(capacity, def_val), 
     default_value(def_val), 
     head_index(0) { 
    } 

    ~CyclicalArray() {} 
}; 

Обратите внимание, как много проще конструктор и деструктор поскольку вся сложность вашего первого класса управляется std:vector.

7

Возможно, вы собираетесь выйти за пределы массива mem_ptr. В C и C++ арифметика указателя находится в единицах используемого типа, а не в байтах. Например, если у вас есть int *a;, то если a - 0x100, а sizeof(int) == 4, a + 1 - 0x104.

Следовательно, вы увеличиваете p на размер квадрата типа, так как добавление 1 к нему приведет к перемещению sizeof(T) байт, и поэтому добавление sizeof(T) к нему будет увеличивать его слишком сильно.

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

1

Использовать глобальную функцию operator new вместо оператора new. Он будет выделять память, но не будет вызывать конструкторы. То же самое для удаления:

template class CyclicalArray { 
private: 
    T* mem_ptr; 
public: 
    CyclicalArray(size_t capacity, const T& default_value) { 
    this->default_value = default_value; 
    this->capacity = capacity; 
    head_index = 0; 
    mem_ptr = static_cast<T*>(::operator new[] (sizeof(T)*capacity)); //memory allocating 
    for(T* p = mem_ptr; p < mem_ptr + capacity; p ++) { 
     ::new (p) T (default_value); //initialization 
    } 
} 
~CyclicalArray() { 
    for(T* p = mem_ptr; p < mem_ptr + capacity; p ++) { 
     p->~T(); 
    } 
    ::operator delete[](static_cast<void*>(mem_ptr)); 
} 
+0

Если я не ошибаюсь, уродство можно скрыть, используя 'std :: allocator ' и 'allocate (capacity)', 'construct (p, default_value)', 'destroy (p)' и 'deallocate (mem_ptr, емкость) '? – UncleBens

1

Вы вызываете ваши конструкторы и деструкторы в два раза, потому что вы используете новый самовыражение и удаления-выражение:

// This allocates and calls constructor 
mem_ptr = ::new T[size]; 

// This calls the destructor and deallocates the memory 
delete[] mem_ptr; 

Если вы хотите просто выделить сырые памяти, вы могут явным образом звонить оператору new:

// This simply allocates raw memory 
mem_ptr = ::operator new(sizeof(T) * size); 

// And this simply deallocates memory 
::operator delete(mem_ptr); 
1

Зачем использовать размещение вообще?Этот код в основном сводится к следующему:

template <class T> 
class CyclicalArray { 
private: 
    T* mem_ptr; 
    size_t capacity; 
    T default_value; 
    size_t head_index; 
public: 
    CyclicalArray(size_t capacity, const T& default_value) : capacity(capacity), default_value(default_value), head_index(0) { 
     mem_ptr = new T[capacity](default_value); //memory allocating and construction 
    } 

    ~CyclicalArray() { 
     delete[] mem_ptr; 
    } 
}; 

EDIT: Если вы хотите использовать размещение нового, ваш цикл должен выглядеть следующим образом:

for(T* p = mem_ptr; p != mem_ptr + capacity; ++p) { 

Нет необходимости масштабировать вещи sizeof(T), это делается для вас на C/C++.