Анализ кода C26408 — Замена переменной m_pszHelpFilePath в InitInstance

#visual-c #mfc #free #code-analysis #chm

Вопрос:

В InitInstance функции моего приложения у меня есть следующий код, чтобы переписать расположение справочной документации CHM:

 CString strHelp = GetProgramPath();
strHelp  = _T("MeetSchedAssist.CHM");
free((void*)m_pszHelpFilePath);
m_pszHelpFilePath = _tcsdup(strHelp);
 

Все это функционально, но это дает мне предупреждение об анализе кода:

C26408 Избегайте malloc() и free() , предпочитайте nothrow версию new with delete (r.10).


Когда вы смотрите на официальную документацию, в m_pszHelpFilePath ней говорится:

Если вы присваиваете значение m_pszHelpFilePath , оно должно быть динамически распределено в куче. CWinApp Деструктор вызывает free( ) с помощью этого указателя. Многие из вас хотят использовать _tcsdup( ) библиотечную функцию времени выполнения для выполнения выделения. Кроме того, освободите память, связанную с текущим указателем, прежде чем присваивать новое значение.

Можно ли переписать этот код, чтобы избежать предупреждения об анализе кода, или я должен добавить __pragma ?

Ответ №1:

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

Сначала объявите соответствующий std::unique_ptr член в производном классе приложения:

 class MyApp : public CWinApp // Presumably
{
// Add this member...
public:
    std::unique_ptr<TCHAR[]> spHelpPath;
// ...
};
 

Затем вам нужно будет изменить код, который создает и назначает путь к справке следующим образом (я изменил приведение в стиле C на, возможно, лучшее приведение C ):

 // First three (almost) lines as before ...
CString strHelp = GetProgramPath();
strHelp  = _T("MeetSchedAssist.CHM");
free(const_cast<TCHAR *>(m_pszHelpFilePath));

// Next, allocate the shared pointer data and copy the string...
size_t strSize = static_cast<size_t>(strHelp.GetLength()   1); 
spHelpPath std::make_unique<TCHAR[]>(strSize);
_tcscpy_s(spHelpPath.get(), strHelp.GetString()); // Use the "_s" 'safe' version!

// Now, we can use the embedded raw pointer for m_pszHelpFilePath ...
m_pszHelpFilePath = spHelpPath.get();
 

Пока все хорошо. Данные, выделенные в интеллектуальном указателе, будут автоматически освобождены при уничтожении вашего объекта приложения, и предупреждения анализа кода должны исчезнуть. Однако есть одно последнее изменение, которое нам нужно внести, чтобы предотвратить попытку MFC framework освободить назначенный m_pszHelpFilePath нам указатель. Это можно сделать, установив для этого значение nullptr в MyApp переопределении класса ExitInstance :

 int MyApp::ExitInstance()
{
    // <your other exit-time code>
    m_pszHelpFilePath = nullptr;
    return CWinApp::ExitInstance(); // Call base class
}
 

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

Комментарии:

1. Спасибо за ваш ответ. Я чувствую, что просто оставлю ее подавленной. Но это вся полезная информация … Интересно, что вы использовали const_cast … 🙂 Почему я сказал, что это может стать ясно позже!

2. @AndrewTruckle Нет. Без приведения вы получите сообщение об ошибке: ошибка C2664: ‘void free(void *)’: не удается преобразовать аргумент 1 из ‘LPCTSTR’ в ‘void *’

3. @AndrewTruckle Ну, это дурацкое правило в анализаторе кода. Это именно то, для чего const_cast предназначено!

4. Вы можете подавить несколько предупреждений в одном списке pragma: разделенном запятыми.

5. Извините — список, разделенный пробелом . Документы

Ответ №2:

Технически, вы можете воспользоваться тем фактом, что new / delete сопоставляется с обычным malloc / free по умолчанию в Visual C , и просто пойти дальше и заменить. Переносимость не сильно пострадает, поскольку MFC в любом случае не переносим. Уверен, что вы можете использовать unique_ptr<TCHAR[]> вместо direct new / delete , например:

 CString strHelp = GetProgramPath();
strHelp  = _T("MeetSchedAssist.CHM");
std::unique_ptr<TCHAR[]> str_old(m_pszHelpFilePath);
auto str_new = std::make_unique<TCHAR[]>(strHelp.GetLength()   1);
_tcscpy_s(str_new.get(), strHelp.GetLength()   1, strHelp.GetString());
m_pszHelpFilePath = str_new.release();
str_old.reset();
 

Для надежности заменяемого new оператора и принципа наименьшего удивления вы должны сохранить free / strdup .

Если вы заменяете несколько таких CWinApp строк, предложите написать для них функцию, чтобы было одно место с free / strdup с подавленными предупреждениями.

Комментарии:

1. У меня есть только один экземпляр, где я это делаю. Но, если я изменю ее на delete , не вызовет ли это еще одно предупреждение анализа об избежании использования new или delete ? Или вы говорите, что это будет нормально для вызова operator delete (m_pszHelpFilePath, std::nothrow); . ?

2. @AndrewTruckle, я добавил пример, как вы могли бы использовать unique_ptr вместо new / delete (не пробовал, написал прямо здесь). Но это только для объяснения технической части, я думаю, вам не следует следовать совету в предупреждении.

3. Хорошо, я просто подавлю с помощью a __pragma .