Делать недействительными удаленные указатели? - PullRequest
3 голосов
/ 11 октября 2011
template<typename T>
someclass<T>& operator=(const someclass<T>& other)
{
    typename std::vector<T *>::const_iterator rhs;
    typename std::vector<T *>::iterator lhs;

    //identity test
    //this->data is std::vector<T *>

    for(lhs = this->data.begin(); lhs != this->data.end(); lhs++)
    {
        delete *lhs;
    }

    this->data.clear(); // this is what I forgot

    this->data.reserve(other.data.size());
    for (rhs = other.data.begin(); rhs != other.data.end(); rhs++)
    {
        if (NULL == *rhs)
        {
            this->data.push_back(NULL);
        }
        else
        {
            this->data.push_back(new T(**rhs));
        }
    }
}

Как вы можете видеть в комментариях, я забыл очистить старые указатели в массиве. Когда я во второй раз вызвал оператор присваивания, я получил ошибку glibc с жалобой на двойное освобождение. Единственной предоставленной информацией был удаленный адрес.

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

Что было бы хорошо для отладки, так это какое-то значение, например INVALID, которое вы присваиваете этим указателям, говоря «вызов удаления для этого указателя - ошибка» вместо NULL, который говорит «вызов удаления для этого указателя ничего не делает». Есть ли что-то подобное?

Ответы [ 2 ]

6 голосов
/ 11 октября 2011

Нет.Лучше было бы не использовать необработанные указатели, когда вы хотите иметь семантику владения.Если вы сделаете тип data равным boost::ptr_vector<T> или std::vector<std::unique_ptr<T>>, то вам не придется вручную управлять временем жизни ваших указателей, и проблема исчезнет.

Ваш контейнер не поддерживает должным образомполиморфные объекты, так как представленный оператор присваивания будет разделять объекты в контейнере, когда они назначены другому контейнеру.Еще лучшим решением может быть просто иметь std::vector<T>.Это будет уместно только в том случае, если вы не рассчитываете на какое-то другое свойство контейнеров указателей (например, недействительность указателей на элементы или потенциально более быстрые операции сортировки).

2 голосов
/ 11 октября 2011

Решением этой проблемы является написание кода, который не содержит удалений.Используйте shared_ptr, где это возможно.Если у вас есть контейнер, который владеет полиморфными объектами, вы также можете использовать Pointer Container .

...