2010-05-02 2 views
1

К сожалению, решения еще не сработали; параметр result.values ​​указатель на 0 фактически не освобождает использование памяти. Я также попробовал бесплатно (result.values) вместо этого, но это нежелательно, поскольку это удаляет мою строку.Память не освобождается, вызывая гибель утечки памяти

Редактировать 2: Попробую написать деструктор стека.

Редактировать 3: Gotcha. Спасибо DeadMG, написав деструктор, который бесплатно (значения) отлично справился! Вау ... так просто.

В моей библиотеке Unicode для C++ класс ustring имеет оператор = функции, установленные для значений char * и других значений ustring. При выполнении простого теста на утечку памяти:

#include <cstdio> 
#include "ucpp" 
main() { 
    ustring a; 
    for(;;)a="MEMORY"; 
} 

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

Это текущий код библиотеки:

#include <cstdlib> 
#include <cstring> 
class ustring { 
    int * values; 
    long len; 
    public: 
    long length() { 
    return len; 
    } 
    ustring() { 
    len = 0; 
    values = (int *) malloc(0); 
    } 
    ustring(const ustring &input) { 
    len = input.len; 
    values = (int *) malloc(sizeof(int) * len); 
    for (long i = 0; i < len; i++) 
     values[i] = input.values[i]; 
    } 
    ustring operator=(ustring input) { 
    ustring result(input); 
    free(values); 
    len = input.len; 
    values = input.values; 
    return * this; 
    } 
    ustring(const char * input) { 
    values = (int *) malloc(0); 
    long s = 0;                 // s = number of parsed chars 
    int a, b, c, d, contNeed = 0, cont = 0; 
    for (long i = 0; input[i]; i++) 
     if (input[i] < 0x80) {             // ASCII, direct copy (00-7f) 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = input[i]; 
     } else if (input[i] < 0xc0) {            // this is a continuation (80-bf) 
     if (cont == contNeed) {             // no need for continuation, use U+fffd 
      values = (int *) realloc(values, sizeof(int) * ++s); 
      values[s - 1] = 0xfffd; 
     } 
     cont = cont + 1; 
     values[s - 1] = values[s - 1] | ((input[i] & 0x3f) << ((contNeed - cont) * 6)); 
     if (cont == contNeed) cont = contNeed = 0; 
     } else if (input[i] < 0xc2) {            // invalid byte, use U+fffd (c0-c1) 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = 0xfffd; 
     } else if (input[i] < 0xe0) {            // start of 2-byte sequence (c2-df) 
     contNeed = 1; 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = (input[i] & 0x1f) << 6; 
     } else if (input[i] < 0xf0) {            // start of 3-byte sequence (e0-ef) 
     contNeed = 2; 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = (input[i] & 0x0f) << 12; 
     } else if (input[i] < 0xf5) {            // start of 4-byte sequence (f0-f4) 
     contNeed = 3; 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = (input[i] & 0x07) << 18; 
     } else {                 // restricted or invalid (f5-ff) 
     values = (int *) realloc(values, sizeof(int) * ++s); 
     values[s - 1] = 0xfffd; 
     } 
    len = s; 
    } 
    ustring operator=(const char * input) { 
    ustring result(input); 
    free(values); 
    len = result.len; 
    values = result.values; 
    return * this; 
    } 
    ustring operator+(ustring input) { 
    ustring result; 
    result.len = len + input.len; 
    result.values = (int *) malloc(sizeof(int) * result.len); 
    for (long i = 0; i < len; i++) 
     result.values[i] = values[i]; 
    for (long i = 0; i < input.len; i++) 
     result.values[i + len] = input.values[i]; 
    return result; 
    } 
    ustring operator[](long index) { 
    ustring result; 
    result.len = 1; 
    result.values = (int *) malloc(sizeof(int)); 
    result.values[0] = values[index]; 
    return result; 
    } 
    operator char *() { 
    return this -> encode(); 
    } 
    char * encode() { 
    char * r = (char *) malloc(0); 
    long s = 0; 
    for (long i = 0; i < len; i++) { 
     if (values[i] < 0x80) 
     r = (char *) realloc(r, s + 1), 
     r[s + 0] = char(values[i]), 
     s += 1; 
     else if (values[i] < 0x800) 
     r = (char *) realloc(r, s + 2), 
     r[s + 0] = char(values[i] >> 6 | 0x60), 
     r[s + 1] = char(values[i] & 0x3f | 0x80), 
     s += 2; 
     else if (values[i] < 0x10000) 
     r = (char *) realloc(r, s + 3), 
     r[s + 0] = char(values[i] >> 12 | 0xe0), 
     r[s + 1] = char(values[i] >> 6 & 0x3f | 0x80), 
     r[s + 2] = char(values[i] & 0x3f | 0x80), 
     s += 3; 
     else 
     r = (char *) realloc(r, s + 4), 
     r[s + 0] = char(values[i] >> 18 | 0xf0), 
     r[s + 1] = char(values[i] >> 12 & 0x3f | 0x80), 
     r[s + 2] = char(values[i] >> 6 & 0x3f | 0x80), 
     r[s + 3] = char(values[i] & 0x3f | 0x80), 
     s += 4; 
    } 
    return r; 
    } 
}; 
+2

phew, это какой-то уродливый код – Kugel

+0

@kugel: иногда небольшие дисплеи диктуют стиль кода. –

ответ

4

В принципе, вы создаете слишком много ustrings, вам нужно много больше ссылок, и вы не реализовали деструктор так, когда все они падают из стека, они не получают свободу.

Кроме того, когда в вашем операторе присваивания вам нужно установить result.values ​​в NULL, иначе память будет удалена.Вы можете использовать оператор перемещения, чтобы сделать эту операцию быстрой, хотя я до сих пор не понимаю, почему вы это сделали.

13

Где деструктор из ustring? Разве он не освобождает выделенную память?


Посмотрим, например. у оператора присваивания:

ustring operator=(ustring input) 
{ 
    ustring result(input); 
    ... 
    return *this; 
} 

Вы передаете параметр ustring по значению. Вероятно, это приведет к созданию копии с помощью конструктора копирования. Вы вызываете копирование в другое время для инициализации result. Я подозреваю, что вы вызываете создание копии еще раз, когда вы возвращаете *this как ustring (опять же по значению, а не по ссылке).

Давайте рассмотрим один из этих трех случаев, а именно: result: Когда эта локальная переменная выходит за пределы области действия (т. Е. В конце функции), она будет автоматически уничтожена. Но если вы не предоставите деструктор (~ustring), что выделенная память free s, вы получите утечку памяти.

И так как у вас, видимо, есть много копий и пропускной стоимости, не имея деструктора для освобождения выделенной памяти, вы получите много и много и много разборчивой памяти.


Кроме того: Почему вы используете malloc и free вместо new[] и delete[]? Вы можете избавиться от уродливых вычислений sizeof(int) * ... и (int*) типа.Вместо того, чтобы:

values = (int *) malloc(sizeof(int) * len); 

вы бы просто написать:

values = new int[len]; 
+7

И почему бы вам использовать 'new []' и 'delete []', когда вы могли бы использовать 'std :: vector'? – jamesdlin

+0

Конечно, можно, это решение ОП, что лучше в его случае. Я просто решил, что переход от 'malloc/free' к' new []/delete [] ', вероятно, более нежный, чем прямой переход на STL (у IMO есть довольно крутая кривая обучения, если вы только начинаете). – stakx

+1

Ответ на вопрос, почему он не использует новый/удалить, - это все, что перераспределяется посередине. Я сомневаюсь, что стоит переписать его, чтобы получить чуть более короткие звонки, чтобы выделить память. Использование вектора значительно улучшит ситуацию, поэтому стоит кривая. Или даже лучше полностью удалить этот класс, используйте строку 'std :: string' для строк UTF-8 и либо' std :: wstring', либо 'vector ' для широкоформатных данных в зависимости от того, согласны ли вы с определением вашей платформы широкий символ и сделать 'encode' свободной функцией. –

2
ustring operator=(ustring input) { 
    ustring result(input); 
    free(values); 
    len = input.len; 
    values = input.values; 
    return * this; 
    } 

Почему result был объявлен в этом?

+0

-1 Это не ответ, это вопрос. –

+0

Технически, это как в некотором смысле. http://en.wikipedia.org/wiki/Socratic_method – Amber

+2

+1, а не потому, что он отвечает на вопрос (это не так), а потому, что он намекает на возможное улучшение кода OP. – stakx

0
  1. Попробуйте использовать RAII-techniques, чтобы избежать проблем с утечками памяти. Если вы обернете свои массивы чем-то вроде boost::shared_array, вам не нужно будет писать явный деструктор, чтобы освободить память.
  2. ustring operator=(ustring input) должно быть ustring operator=(const ustring & input), чтобы избежать копирования аргумента. Это всегда должно выполняться, если вы передаете нецелый тип в качестве параметра только для чтения.
  3. Использование shared_array-то вроде этого также избавляет от проблемы расчета размера.

Просто некоторые подсказки. С другой стороны, ваш код невероятно трудно читать и понимать. Вы действительно должны что-то сделать, если вы ожидаете, что другие люди будут работать с этим. Сделайте свои имена более наглядными, повторяйте код рефакторинга в функции, которые выражают то, что он делает. Например, вместо того, чтобы использовать for -loop для копирования ваших значений, вы можете использовать std::copy. Вы также можете рассмотреть возможность заменить все свои магические числа (например, 0x1f) на константы, чьи имена выражают семантику значения.

2

Как только Вы реализуете деструктор, это реализация присвоения укусит Вы

ustring operator=(const char * input) { 
    ustring result(input); 
    free(values); 
    len = result.len; 
    values = result.values; 
    return * this; 
    } 

Нет, это не аргумент для ухода деструктор не реализован.

EDIT:

ustring объект имеет массив, выделенный с таНос и должен освободить эту память, когда она разрушается.

При создании локального объекта

ustring result(input); 

Он будет разрушаться, когда функция возвращает.

В строке

values = result.values; 

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

ustring& operator=(const char * input) { 
    ustring result(input); 
    free(values); 
    len = result.len; 
    values = result.values; 
    result.values = 0; 
    return * this; 
} 
+0

+1 Но может быть, объясните, как это вас укусит? – UncleBens

+0

Отличный совет, но, к сожалению, ваше решение не работает. Использование памяти по-прежнему значительно возрастает. Я также попробовал бесплатно (result.values) вместо «result.values ​​= 0», но это удалило мою строку. –

+0

@ Delan Azabani Это был совет. только то. Я не подразумевал, что мое исправление решит всю вашу проблему со всеми утечками. Но проблема, о которой я говорил, была реальной и могла стать источником путаницы после исправления остальной части кода. значения и result.values ​​после того, как точка назначения указывает на тот же кусок памяти, поэтому, конечно, освобождение одного освобождает то, что другие указывают (то же самое). Установка result.values ​​в 0 предотвращает освобождение памяти, на которую она указывала ранее. Функция ustring = (ustring input) имеет ту же проблему. В вашем коде есть много вещей, и другие указали на это. –

Смежные вопросы