c++ - 使用命名空间代替类

标签 c++ class namespaces

我正在设计一个接口(interface)来抽象管理 Direct3D、Direct2D、DXGI 和关联的 Win32API 调用的任务。

将所有内容都保留在命名空间中或重构以使用类?

WindowsApp.h

#pragma once
#include <Windows.h>

namespace WindowsApp
{
    bool Initialize(HINSTANCE instanceHandle);
}

WindowsApp.cpp

#include "WindowsApp.h"

namespace WindowsApp
{
    namespace
    {
        HWND ghMainWnd = 0;
    }

    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
        break;

        default:
        {
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
        }
    }

    bool Initialize(HINSTANCE instanceHandle)
    {
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = instanceHandle;
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = LoadCursor(0, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName = 0;
        wc.lpszClassName = L"BasicWndClass";

        if (!RegisterClass(&wc))
        {
            MessageBox(0, L"RegisterClass FAILED", 0, 0);
            return false;
        }

        ghMainWnd = CreateWindow(
            L"BasicWndClass",
            L"Win32Basic",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            0,
            0,
            instanceHandle,
            0);

        if (ghMainWnd == 0)
        {
            MessageBox(0, L"CreateWindow FAILED", 0, 0);
        }

        ShowWindow(ghMainWnd, 1);
        UpdateWindow(ghMainWnd);

        return true;
    }
}

main.cpp

#include "WindowsApp.h"

int Run()
{
    MSG msg = { 0 };

    BOOL bRet = 1;
    while ((bRet = GetMessage(&msg, 0, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            MessageBox(0, L"GetMessage FAILED", L"Error", MB_OK);
            break;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // Deinitialize Here
    return (int)msg.wParam;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)
{
    if (!WindowsApp::Initialize(hInstance)) { return 0; }
    return Run();
}

使用命名空间允许我将实现细节隐藏在嵌套的未命名命名空间中。 一个类不会让我隐藏东西,但我可以让它们在私有(private)部分内不可访问,我想这已经足够了。

使用类存在用户尝试实例化多个对象的风险,这会导致应用程序因初始化 DirectX 两次或其他原因而崩溃。 命名空间避免了这个问题,但是它们会降低性能,我必须在每次函数调用期间检查 Initialized 变量。我真的不喜欢这个。

最后,使用类需要用户在整个应用程序中需要底层方法的地方传递实例化对象。这真的很令人失望,因为只要我在一个包含命名空间头文件的 #include 的文件中,命名空间方法就可以让我访问。我真的很喜欢这个。

命名空间方法似乎是最好的方法,但在嵌套的未命名命名空间内处理变量的方式中,有些东西不太适合我。 这样可以吗?我的直觉告诉我不行!不!不!

所以我想我的问题是:这是命名空间的合适用例吗?

澄清: 我在 WindowsApp.cpp 中定义了未命名的命名空间以及函数定义 - 所有函数的转发声明都在 WindowsApp.h 中 - 通过调用这些函数来操作未命名命名空间内的变量。这是命名空间的错误使用还是应该以不同的方式完成?只需将头文件包含在任何其他 .cpp 中,您就可以访问函数,进而访问底层数据。这很吸引人。我的直觉告诉我,这样的结构会导致某种性能损失。

最佳答案

[编辑:删除有关未命名命名空间的内容开始对 TU 独有,现在问题中的代码已得到澄清。]

在 C++ 中,我们倾向于将 class 视为一些数据的包装器,它保持不变(与 struct 相对,后者通常用于一堆没有不变性的数据)。构造函数建立不变量,析构函数拆毁它,成员函数小心翼翼地维护它。在这里,如果我理解正确的话,你的不变性似乎是 Initialized() 必须在使用任何其他 API 函数之前调用。

还有另一种选择,即使用所谓的“魔术静力学”,也称为“Meyers 单例”。执行如下操作:

// In WindowsApp.cpp

namespace {

class WindowsAppImpl {
public:
    WindowsAppImpl()
    {
        // Do initialization stuff here
    }

    ~WindowsAppImpl()
    { 
        // Do teardown stuff if necessary
    }

    // Example function
    int getMagicNumber()
    {
        return 3;
    }
};

WindowsAppImpl& GetInstance() {
    static WindowsAppImpl instance{};
    return instance;
}

} // end private namespace

// Public function declared in WindowApp.h
int GetMagicNumber() {
    // Get the singleton instance
    WindowsAppImpl& instance = GetInstance();

    // Call member function
    return instance.getMagicNumber();
}

此方法添加了一个函数,该函数返回对单例 WindowsAppImpl 实例的引用。编译器保证此实例仅构造一次,即第一次调用 GetInstance() 时。 (它还会在 main() 完成后运行 WindowsAppImpl 的析构函数,这在这种情况下可能不重要,但在某些情况下很有用。)这种方法是在 GetMagicNumber() 内部,您可以确定初始化例程已经运行,而无需用户传递他们自己的某种 WindowsAppContext 实例。

关于c++ - 使用命名空间代替类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38603245/

相关文章:

c++ - 函数指针类型定义

c++ - 使用 OpenGL 和 gluLookAt 绕球体旋转

c# - 命名空间的名称和模型冲突

php - magento 在同一页面中显示具有不同命名空间的 2 个不同模块

.net - 在 .net 中重命名命名空间

c++ - 在引用 : Linking C compiled static library to C++ Program

C++ 变量/对象和堆内存管理

c++ - C++ 类的预先定义

C++:删除对象还是删除成员?

java - 类加载器、类差异