是否可以只将变量分配给我函数中的变量一次?

问题描述

采取这种方法

int cmeetingScheduleAssistantApp::DoMessageBox(LPCTSTR lpszPrompt,UINT nType,UINT nIDPrompt)
{
    CString strContent = CString(lpszPrompt);
    CString strTitle = CString();

    if (!CTaskDialog::IsSupported())
        return CWinAppEx::DoMessageBox(lpszPrompt,nType,nIDPrompt);

    ENSURE(strTitle.LoadString(AFX_IDS_APP_TITLE));
    CTaskDialog dlgTaskMessageBox(strContent,_T(""),strTitle);

    // =================================
    // Can this be calculated just once?

    HDC screen = GetDC(nullptr);
    auto hSize = static_cast<double>(GetDeviceCaps(screen,HORZSIZE));
    auto hRes = static_cast<double>(GetDeviceCaps(screen,HORZRES));
    auto PixelsPerMM = hRes / hSize;   // pixels per millimeter
    auto MaxPixelWidth = PixelsPerMM * 150.0;
    auto PixelWidth = (hRes / 100.0) * 30.0;
    // =================================

    int iDialogUnitsWidth = MulDiv(
        min(static_cast<int>(PixelWidth),static_cast<int>(MaxPixelWidth)),4,LOWORD(GetDialogBaseUnits()));
    dlgTaskMessageBox.SetDialogWidth(iDialogUnitsWidth);

    // Code snipped
}

是否可以调整此函数使其只计算一次 MaxPixelWidth 值?不需要在我的类中添加其他变量?

我的目标是允许多次调用 DoMessageBox 并且只计算一次最大宽度。

解决方法

这可以通过标记函数本地值 static 轻松完成。静态变量最多初始化一次。由于需要将变量初始化为执行代码的结果,因此需要 C++ 的另一个强大功能:立即求值 lambda expressions

// ================================= 注释之间的代码替换为以下内容即可完成所要求的操作。

static auto const MaxPixelWidth = []() {
    HDC screen = GetDC(nullptr);
    auto hSize = static_cast<double>(GetDeviceCaps(screen,HORZSIZE));
    auto hRes = static_cast<double>(GetDeviceCaps(screen,HORZRES));
    auto PixelsPerMM = hRes / hSize;   // pixels per millimeter
    ReleaseDC(screen);
    return PixelsPerMM * 150.0;
}();

这确保 MaxPixelWidth 最多初始化一次,不能从函数外部访问,并且可以设置为 const 以防止对其进行意外更改。

Live demo 来说明这个概念。


请记住以下几点,以帮助您了解后果,并就是否要使用上述内容做出明智的决定:

使用函数局部静态变量不是免费的。如果您查看编译器资源管理器的反汇编,您会发现两个细节:

  • 编译器分配一个隐藏标志,用于存储静态是否已初始化。
  • 隐藏标志在锁下更新,以防止并发线程多次初始化静态。

在访问静态变量之前,编译器会发出代码来检查初始化标志。 Clang 和 GCC 似乎都使用双重检查锁定来避免在每次访问时使用锁定(具有非平凡的成本)。这种情况下的空间和时间开销可以忽略不计。我也尝试理解 MSVC 的代码,但没有多大意义。始终使用锁来访问静态可能成本更高。

底线:使用所谓的魔法静态并不是严格意义上的零成本抽象。您总是为标志和线程安全实现付费,即使在您的用例中两者都不是严格要求的。