2016-01-25 4 views
0

Я пытаюсь исправить потенциально эскизное использование кастования void* в приложении планирования задач в течение нескольких недель. Обратите внимание, что я НЕ получаю ошибку компилятора, но программа планирования вылетает через несколько часов (по какой-то неизвестной причине). Рассмотрим следующие фрагменты кода из программы:Кастинг в пустоту * с потенциально эскизным поведением

В main:

CString* buffer = new CString(temp); 
parameters.set("jobID", (void *) buffer); 
runJob(parameters); 

Кроме того, VGridTaskParam класс выглядит следующим образом:

class VGridTaskParam{ 
    map<CString, void *> p; // maps from CString to a pointer that is not known 
public: 
    void * get(CString name){ 
     return p[name]; // returns the map value of the name which is an unknown pointer 
    } 
    void set(CString name, void * data){ 
     p[name] = data; // sets the mpa value given a particular key 
    } 
}; 

Snippet некоторой работы в runJob(VGridTaskParam parameters) функции:

void runJob(VGridTaskParam parameters) 
{ 
     CString JIDstr; // job ID string 
//get job ID as CString 
     CString* pJID = (CString*)parameters.get("jobID"); 
     JIDstr = CString(*pJID); 
     delete pJID; ****************************** 
} 

Некоторые вопросы: лань s последняя строка delete (отмечена несколькими звездочками) удаляет выделение памяти, созданное в основной программе? Является ли мое использование void * casting оправданным в этой ситуации. Обратите внимание, что всякий раз, когда я запускаю задание, я создаю новый поток. Может ли кто-нибудь предложить потенциальное решение этой проблемы? На что я должен обратить внимание, чтобы исправить эту проблему?

+1

Если он сработает через несколько часов и не сразу, у вас наверняка закончится память, поэтому ваша интуиция о 'delete' находится в точке. По общему признанию, здесь все в порядке, поэтому я думаю, что проблема находится где-то еще в 'runJob' или методе, который он вызывает. Возможно, выделено больше памяти или возможно, что ваш 'CString *' будет освобожден другим способом, что приведет к двойному удалению. – AndyG

+1

Я не могу сказать, что такое точная ошибка, но могу сказать, что динамическое распределение объекта и требование его удаления вручную запрашивает утечку памяти. Мое предложение, по возможности, состояло бы в том, чтобы изменить тип данных с параметрами hold-values ​​с (void *) на unique_ptr (или даже на простое старое хранимое значение Cstring), так что требуемые удаления (и удалять ваши явные вызовы для удаления, конечно). Вероятно, это поможет решить проблему. –

+0

Я не вижу никакой причины использовать 'void *' cast здесь, однако, похоже, что они не способствуют сбою. Я бы предложил удалить void * арифметику из C++-программы, посмотреть, требуется ли вам какое-либо управление динамической памятью - возможно, удалите это. Это сделает код более понятным и понятным, так что ошибка может быть найдена легче (если она по-прежнему остается). – SergeyA

ответ

1

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

Да.

Мое применение void* литье в этой ситуации?

Нет, это лишний. Любой указатель может быть отличен до void* без явного приведения.

Может кто-нибудь предложить потенциальное решение этой проблемы? На что я должен обратить внимание, чтобы исправить эту проблему?

Я не могу предложить исправление без MCVE.

1

Я вижу две вещи, которые кажутся мне немного удивительными.

1) runJob принимает параметры типа VGridTaskParam по значению. Возможно, вам нужна ссылка. Однако это не может объяснить крах.

2) Вы никогда ничего не удаляете с карты, даже если вы удаляете память, связанную с указателем. Таким образом, существует риск того, что позже вы будете использовать значение указателя. Либо как разыменование, либо как двойное удаление.

3) Вы не осуществляете проверку того, что ключ присутствует на карте.

1

Несколько возможных вопросов:

что произойдет, если вы пишете дважды же ключ? Это приведет к утечке памяти. есть несколько возможных решений, как:

void set(CString name, void * data){ 
    if(p[name]!=NULL) 
     delete p[name]; 
    p[name] = data; // sets the mpa value given a particular key 
} 

Другое дело: Вы работаете в многопоточных или однопоточный?для многопоточности вы должны синхронизировать методы get и set, чтобы предотвратить изменения указателя в середине действия.

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

void runJob(VGridTaskParam parameters) 
{ 
    CString JIDstr; // job ID string 
    //get job ID as CString 
    CString* pJID = (CString*)parameters.get("jobID"); 
    if(pJID){ 
     parameters.set("jobID",NULL); 
     JIDstr = CString(*pJID); 
     delete pJID; ****************************** 
    } 
} 
+0

Многопоточность –

1

Ваш delete является правильным. Он удаляет выделенный блок памяти размера типа указателя по адресу указателя. Поэтому, когда вы выделяете CString с new, вы получите delete точный объем памяти.

Отклонение происходит, когда вы удаляете нераспределенный память. Есть несколько способов, которыми я мог видеть, как это происходит в текущем коде: (. Это будет возвращать по умолчанию инициализируется CString* как вновь созданная стоимость)

  1. runJob, запрашивает ключ не в map
  2. Вы называете runJob на ранее удаленный элемент в map (это значение уже было бы освобождено и его удаление было бы незаконным.)
  3. Мы не можем видеть весь ваш код, но если есть потенциал для delete, эти указатели в другом месте также могут быть проблема двойной очистки там.

Вы должны закодировать карту более оборонительно, например:

сопзЬ недействительными * получить (Const CString & имя) сопзЬ { возвращение p.find (имя) == p.cend ()? nullptr: p [name]; // возвращает значение карты имени, которое неизвестный указатель }

void set(const CString& name, void* data) { 
    void* toOverwrite = get(name); 

    if(toOverwrite != nullptr) { 
     delete toOverwrite; 
    } 
    p[name] = data; // sets the mpa value given a particular key 
} 

void remove(const CString& name) { 
    if(p.find(name) != p.end()) { 
     p.erase(name); 
    } 
} 

В функции вам нужно изменить на тестирование, если возвращение get был nullptr перед работой на результат, а скорее чем delete, вам необходимо позвонить remove. Это сохраняет все изменения p, принадлежащие классу, что гарантирует правильное обслуживание map.