#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
withdelete
(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
.