代码分析 C26408 — 替换 InitInstance 中的 m_pszHelpFilePath 变量

Posted

技术标签:

【中文标题】代码分析 C26408 — 替换 InitInstance 中的 m_pszHelpFilePath 变量【英文标题】:Code analysis C26408 — Replacing the m_pszHelpFilePath variable in InitInstance 【发布时间】:2021-11-01 14:44:04 【问题描述】:

在我的应用程序的InitInstance 函数中,我有以下代码来重写 CHM 帮助文档的位置:

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

一切正常,但它给了我一个代码分析警告:

C26408 避免使用malloc()free(),更喜欢nothrow 版本的newdelete (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 框架试图释放我们分配的 m_pszHelpFilePath 指针。这可以通过在ExitInstanceMyApp 类覆盖中将其设置为nullptr 来完成:

int MyApp::ExitInstance()

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

但是,这可能看起来无所事事,正如其他人所说,您可能有理由简单地取消警告。

【讨论】:

感谢您的回答。我觉得我只会让它被压制。但这都是有用的信息...有趣的是您使用了const_cast... :) 为什么我说以后可能会清楚! @AndrewTruckle 没有。没有演员,你会得到一个错误:error C2664: 'void free(void *)': cannot convert argument 1 from 'LPCTSTR' to 'void * ' @AndrewTruckle 好吧,这是代码分析器中的一条愚蠢规则。这正是const_cast 的用途! 您可以在单个编译指示中抑制多个警告:逗号分隔列表。 抱歉 - 空格 分隔列表。 docs【参考方案2】:

从技术上讲,您可以利用 new / delete 在 Visual C++ 中默认映射到通常的 malloc/free 这一事实,然后继续替换。便携性不会受到太大影响,因为 MFC 无论如何都不是便携的。当然你可以使用unique_ptr&lt;TCHAR[]&gt;而不是直接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 并带有隐藏的警告。

【讨论】:

我只有这一个实例。但是,如果我将其更改为delete,那会不会触发另一个关于避免使用newdelete 的分析警告?或者,您是说可以拨打operator delete (m_pszHelpFilePath, std::nothrow);。 ? @AndrewTruckle,我添加了一个示例,您可以如何使用unique_ptr 而不是new/delete(没试过,写在这里)。但这只是技术上部分的解释,我认为你不应该遵循警告中的建议。 好吧,我会简单地用__pragma压制。

以上是关于代码分析 C26408 — 替换 InitInstance 中的 m_pszHelpFilePath 变量的主要内容,如果未能解决你的问题,请参考以下文章

详解逃逸分析标量替换栈上分配

千字分析剑指 Offer 05. 替换空格

基于模版文件复制替换的abpcore代码生成器

类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)

JVM逃逸分析

替换空格