使用命名空间代替类

时间:2016-07-27 03:31:19

标签: c++ class namespaces

我正在设计一个界面来抽象管理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();
}

使用命名空间允许我在嵌套的未命名命名空间中隐藏实现细节。 一堂课不会让我隐藏东西,但我可以让它们在私人区域内无法进入,我认为这已经足够了。

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

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

命名空间方法似乎是最好的方法,但是在嵌套的未命名命名空间内处理变量的方式中,某些东西并不适合我。 这是一个好的做法吗?我的直觉告诉我没有!没有!没有!

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

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

1 个答案:

答案 0 :(得分:2)

[编辑:删除有关未命名的命名空间的内容开始对TU而言是唯一的,现在问题中的代码已经澄清。]

在C ++中,我们倾向于认为class是一些保持不变量的数据的包装器(而不是struct,它往往用于一堆数据而没有不变)。构造函数建立了不变量,析构函数将其删除,成员函数小心地维护它。在这里,如果我理解正确,那么在使用任何其他API函数之前,Initialized()必须调用您的不变量。

还有另一种选择,即使用所谓的&#34;魔法静电&#34;,也称为&#34; Meyers singletons&#34;。做类似以下的事情:

// 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()时,该实例只构造一次。 (它还将在WindowsAppImpl完成后运行main()的析构函数,这在这种情况下可能并不重要,但在某些情况下可能很有用。)这种方法的优点是内部{{1你可以确定已经运行了初始化例程,而不需要用户传递他们自己的某种GetMagicNumber()实例。