You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数返回LPCTSTR指针后,如何正确释放动态分配的内存?

问题描述

我查过类似问题,但它们都假设返回类型是指针。刚接触C++,表述不当还请见谅。

我写了下面这个函数,用来把std::string转成LPCTSTR(等价于wchar_t*):

//Converts string to LPCTSTR for dialog box
LPCTSTR CAnotherAttemptDlg::convertToLPCTSTR(std::string conversion)
{

    size_t convertedChars = 0;
    const size_t newsizew = conversion.size() + 1;
    wchar_t* wcstring = new wchar_t[newsizew];
    mbstowcs_s(&convertedChars, wcstring, newsizew, conversion.c_str(), _TRUNCATE);
    return wcstring;

}

现在的问题是,我返回的是指针,但没法在函数外通过删除赋值后的指针来释放内存(当前代码里的delete[]在return之后根本不会执行,已经造成内存泄漏)。请问最佳解决方案是什么?

补充:调用该函数的代码如下:

BOOL CAnotherAttemptDlg::OnInitDialog()
{

    string settingsLine;
    LPCTSTR defStrPath = L""; // = "C:\\Ste\\Runtime\\TestFiles";
    LPCTSTR defEndPath = L"";

    //grabs default start and end paths from FileTrams.ini, parses it into proper variables
    ifstream settingsStream("FileTrans.ini");

    while (getline(settingsStream, settingsLine)) {
        string delimiter = "=";
        string tokenOne = settingsLine.substr(0, settingsLine.find(delimiter)); 
        string tokenTwo = settingsLine.substr(settingsLine.find(delimiter) + 1, '10');

        if (tokenOne == "startPath") {
            defStrPath = convertToLPCTSTR(tokenTwo);
        }
        else if (tokenOne == "endPath") {
            defEndPath = convertToLPCTSTR(tokenTwo);
        }

    }

    

    settingsStream.close();
    


    CDialogEx::OnInitDialog();

    SetWindowLong(this->m_hWnd, GWL_STYLE, GetWindowLong(this->m_hWnd, GWL_STYLE) |  WS_SYSMENU);

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here
    
    SetDlgItemText(IDC_MFCEDITBROWSE1, defStrPath);
    SetDlgItemText(IDC_MFCEDITBROWSE2, defEndPath);

    return TRUE;  // return TRUE  unless you set the focus to a control

    delete[] defStrPath;
    delete[] defEndPath;
}
解决方案

首先明确:LPCTSTR本质就是指针类型(Unicode环境下是const wchar_t*),你没法让它不是指针,但可以用C++的RAII机制自动管理内存,彻底避免手动new/delete带来的泄漏问题。

方案1:用std::wstring代替手动分配的wchar_t*

修改转换函数返回std::wstring,需要LPCTSTR时调用c_str()方法即可,std::wstring会自动管理内存:

std::wstring CAnotherAttemptDlg::convertToLPCTSTR(std::string conversion)
{
    size_t convertedChars = 0;
    const size_t newsizew = conversion.size() + 1;
    // 初始化对应长度的宽字符串
    std::wstring wcstring(newsizew, L'\0');
    mbstowcs_s(&convertedChars, &wcstring[0], newsizew, conversion.c_str(), _TRUNCATE);
    // 调整字符串到实际转换后的有效长度
    wcstring.resize(convertedChars);
    return wcstring;
}

然后修改调用代码,把defStrPath和defEndPath改成std::wstring类型:

BOOL CAnotherAttemptDlg::OnInitDialog()
{
    string settingsLine;
    std::wstring defStrPath = L"";
    std::wstring defEndPath = L"";

    ifstream settingsStream("FileTrans.ini");
    while (getline(settingsStream, settingsLine)) {
        string delimiter = "=";
        size_t delimiterPos = settingsLine.find(delimiter);
        if (delimiterPos == std::string::npos) continue; // 跳过无效配置行

        string tokenOne = settingsLine.substr(0, delimiterPos); 
        // 原代码用'10'作为长度是错误的,直接截取到字符串末尾
        string tokenTwo = settingsLine.substr(delimiterPos + 1);

        if (tokenOne == "startPath") {
            defStrPath = convertToLPCTSTR(tokenTwo);
        }
        else if (tokenOne == "endPath") {
            defEndPath = convertToLPCTSTR(tokenTwo);
        }
    }
    settingsStream.close();

    CDialogEx::OnInitDialog();
    SetWindowLong(this->m_hWnd, GWL_STYLE, GetWindowLong(this->m_hWnd, GWL_STYLE) | WS_SYSMENU);
    SetIcon(m_hIcon, TRUE);
    SetIcon(m_hIcon, FALSE);
    
    // 调用c_str()获取LPCTSTR类型的指针
    SetDlgItemText(IDC_MFCEDITBROWSE1, defStrPath.c_str());
    SetDlgItemText(IDC_MFCEDITBROWSE2, defEndPath.c_str());

    return TRUE;
}

这样函数结束时std::wstring会自动释放内存,完全不用手动delete,彻底解决泄漏问题。

方案2:直接在调用处完成转换(跳过单独函数)

如果这个转换只在OnInitDialog里使用,也可以直接在循环内完成转换,省掉单独的函数:

BOOL CAnotherAttemptDlg::OnInitDialog()
{
    string settingsLine;
    std::wstring defStrPath = L"";
    std::wstring defEndPath = L"";

    ifstream settingsStream("FileTrans.ini");
    while (getline(settingsStream, settingsLine)) {
        string delimiter = "=";
        size_t delimiterPos = settingsLine.find(delimiter);
        if (delimiterPos == std::string::npos) continue;

        string tokenOne = settingsLine.substr(0, delimiterPos); 
        string tokenTwo = settingsLine.substr(delimiterPos + 1);

        if (tokenOne == "startPath" || tokenOne == "endPath") {
            size_t convertedChars = 0;
            const size_t newsizew = tokenTwo.size() + 1;
            std::wstring temp(newsizew, L'\0');
            mbstowcs_s(&convertedChars, &temp[0], newsizew, tokenTwo.c_str(), _TRUNCATE);
            temp.resize(convertedChars);

            if (tokenOne == "startPath") {
                defStrPath = std::move(temp); // 用move避免拷贝
            } else {
                defEndPath = std::move(temp);
            }
        }
    }
    settingsStream.close();

    // 后续初始化代码不变
    CDialogEx::OnInitDialog();
    SetWindowLong(this->m_hWnd, GWL_STYLE, GetWindowLong(this->m_hWnd, GWL_STYLE) | WS_SYSMENU);
    SetIcon(m_hIcon, TRUE);
    SetIcon(m_hIcon, FALSE);
    
    SetDlgItemText(IDC_MFCEDITBROWSE1, defStrPath.c_str());
    SetDlgItemText(IDC_MFCEDITBROWSE2, defEndPath.c_str());

    return TRUE;
}

额外注意点

  1. 原代码中substr的第二个参数用'10'是错误的——substr的第二个参数是截取长度,'10'是字符,对应ASCII值49,会导致只截取49个字符,正确做法是去掉第二个参数,直接截取到字符串末尾。
  2. 原代码里的delete[]语句在return TRUE之后,永远不会执行,这是典型的内存泄漏场景,用std::wstring这类RAII容器可以彻底避免这类问题。

内容的提问来源于stack exchange,提问作者Jeremy Hahn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:19:50