2016-12-11 7 views
6

У меня есть иерархия объектов и вы должны иметь возможность клонировать объекты из базового класса. Я следил за типичным шаблоном CRTP, за исключением того, что я также хочу иметь возможность возвращать дочерний класс, если копия вызывается непосредственно у ребенка. Для этого я следовал предложению здесь: https://stackoverflow.com/a/30252692/1180785Метод копирования CRTP предупреждает о потенциальной утечке памяти

Кажется, что все нормально, но Кланг предупреждает меня, что у меня есть потенциальная утечка памяти. Я уменьшил код до этого MCVE:

template <typename T> 
class CRTP { 
protected: 
    virtual CRTP<T> *internal_copy(void) const { 
     return new T(static_cast<const T&>(*this)); 
    } 

public: 
    T *copy(void) const { 
     return static_cast<T*>(internal_copy()); 
    } 

    virtual ~CRTP(void) = default; 
}; 

class Impl : public CRTP<Impl> { 
}; 

int main(void) { 
    Impl a; 
    Impl *b = a.copy(); 
    delete b; 
} 

Насколько я могу сказать, что нет никакой возможной утечки памяти, но работаешь Clang через XCode показывает это:

Clang potential memory leak

Есть там утечка памяти здесь? Если нет, что вызывает ложный позитив и как я могу его обойти? (Я бы предпочел не отключать статический анализ)

+0

Программа вы показываете [не вызывает 'CRTP :: copy'] (http://rextester.com/ UBB92957). Я подозреваю, что код, который вы запускаете, может отличаться от отображаемого вами кода. –

+0

@IgorTandetnik хорошая точка; Я пропустил это, пока я его уменьшал. Тем не менее, предупреждение, которое я опубликовал, берется непосредственно из кода, который я разместил, поэтому как-то он действительно запускает CRTP :: copy. Это заставляет меня думать, что это может быть ошибкой в ​​анализаторе, связанном с виртуальными методами. – Dave

+0

@IgorTandetnik Я обновил код с улучшенной демонстрацией проблемы, которая удаляет ненужный «виртуальный» экземпляр и удаляет массив. Этот метод действительно вызывает метод CRTP :: copy, а анализ clang - тот же. – Dave

ответ

1

Я нашел обходное решение, которое делает анализатор счастливым, все еще позволяя использовать этот шаблон. Просто обратная связь между copy и internal_copy:

template <typename T> 
class CRTP : public Base { 
protected: 
    virtual CRTP<T> *internal_copy(void) const { 
     return copy(); 
    } 

public: 
    T *copy(void) const { 
     return new T(static_cast<const T&>(*this)); 
    } 
}; 

Это все еще работает в контексте первоначального предложения here, потому что при решении copy внутри CRTP, он предпочтет переопределение CRTP (даже если это не виртуальный метод), поэтому нет бесконечного цикла.

Что касается того, почему анализатор доволен этим заказом, но недоволен первоначальным заказом, я понятия не имею.

1

Я думаю, что анализатор запутывается static_cast в вашем методе copy. Если вы просто измените его на dynamic_cast, он перестанет отмечать линию как утечку памяти. Это заставляет меня полагать, что он полагает, что вы, возможно, ставите экземпляр базового типа (CRTP<T>) на его производный тип (T), который, конечно, был бы недействительным при разыменовании. Очевидно, вы этого не делаете, поэтому это может быть ошибка в детекторе утечки, или это может быть нечто более тонкое, чем то, о чем я сейчас думаю. Это может быть не ошибка, но если Impl становится более сложным типом (например, с множественным наследованием), это может стать проблемой (как и ваш static_cast в вызове copy-CTOR).

Эта реализация (с меньшим количеством забросов) имели нулевые утечки для меня, а также:

template <typename T> 
class CRTP { 
protected: 
    virtual T *internal_copy() const { 
     return new T(static_cast<const T&>(*this)); 
    } 

public: 
    T *copy() const { 
     return internal_copy(); 
    } 

    virtual ~CRTP() = default; 
}; 
+0

См. Мой ответ на ваш комментарий для объяснения причин, почему 'internal_copy' не может вернуть' T * 'в этом прецеденте. Интересно, что 'dynamic_cast' делает его счастливым; это не то, что я тестировал, потому что я использую '-no-rtti', но, как вы говорите; возможно, он просто думает, что static_cast может использовать неверный тип. – Dave