Код шаблона C ++, приводящий к ошибке при вызове delete для объекта, который явно не был правильно выделен - PullRequest
1 голос
/ 22 марта 2012

Я учу себя шаблонам с ++.Я написал следующий код и получаю странную ошибку об освобождении указателя, который не был выделен.Я предполагаю, что что-то в моем конструкторе шаблонов классов на самом деле не вызывает new на int, когда я запрашиваю тип <int> этого класса.Код компилируется и запускается автоматически CodeRunner для Mac, который я настроил на использование компилятора clang++ для c++ файлов.

#include <vector>
#include <iostream>

template <typename T>
class HeapVal
{
    public:
        HeapVal(T val) {ptr = new T(val);}
        ~HeapVal() {delete ptr;}
        T get() {return *ptr;}
    private:
        T* ptr;
};

int main(){
  std::vector< ::HeapVal<int> > vec;
  for(int i = 0; i < 1000; ++i){
    ::HeapVal<int> h(i);
    vec.push_back(h);
  }
  for(int i = 0; i < 1000; ++i){
    std::cout << vec[i].get() << std::endl;
  }
  return( 0 );
}

Этот код приводит к следующей ошибке либо во время компиляцииили выполнение (выглядит как ошибка во время выполнения).

Untitled(30214) malloc: *** error for object 0x7f82f24007c0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Run Command: line 1: 30214 Abort trap: 6           ./"$2" "${@:3}"

Ответы [ 3 ]

6 голосов
/ 22 марта 2012

Вам не хватает конструктора копирования и оператора присваивания для HeapVal. Это означает, что в конечном итоге вы попытаетесь delete использовать одну и ту же память более одного раза, потому что у вас есть несколько указателей на нее - вот что вызывает сбой.

Попробуйте что-то вроде этого:

template <typename T>
class HeapVal
{
public:
    explicit HeapVal(T val)
    :   ptr(new T(val))
    {}

    ~HeapVal()
    {
        delete ptr;
    }

    HeapVal(const HeapVal& rhs)
    :   ptr(new T(*rhs.ptr))
    {}

    HeapVal& operator=(const HeapVal& rhs)
    {
        HeapVal(rhs).swap(*this);
        return *this;
    }

    T get() const
    {
        return *ptr;
    }

private:
    T* ptr;

    void swap(HeapVal& rhs)
    {
        std::swap(ptr, rhs.ptr);
    }
};
2 голосов
/ 22 марта 2012

Проблема в том, что когда вы нажимаете объект HeapVal, вы в конечном итоге делаете мелкую копию ptr, так что это может привести к двойному удалению.

Вам необходимо переопределить конструктор копирования по умолчанию и оператор присваивания, или вы можете использовать умные указатели вместо необработанного ptr. Boost :: shared_ptr Возможно, это излишнее убийство в вашем случае, но его полезно изучить.

2 голосов
/ 22 марта 2012

Каждый раз, когда HeapVal копируется (или присваивается), у вас получаются два HeapVal объекта, каждый из которых содержит указатели на одно и то же место. Когда первый из них будет уничтожен, он удалит указанный объект. Когда вторая будет уничтожена, она попытается снова удалить тот же объект, что приведет к неопределенному поведению.

В вашем случае вам повезло, что библиотека во время выполнения обнаружила и сообщила о проблеме.

Поиск в Google по "закону большой тройки C ++" или чему-то подобному должен привести к небольшому обсуждению этой темы.

...