【问题标题】:Must a pointer to an instance of class A be static in class B?指向 A 类实例的指针在 B 类中必须是静态的吗?
【发布时间】:2013-09-07 09:40:07
【问题描述】:

IDE:Eclipse Juno; 编译器:MinGW 4.6.2; 项目:Win32

我有一个 MainWindow 和两个有点不同的 MDI 子窗口:MdiChildWindowAMdiChildWindowB。第三个子窗口 SharedWindow 不是 MDI,但可以由任一 MDI 子窗口使用。所有这些都封装在自己的 C++ 类中。

为了避免 SharedWindow 的泛滥,我借用了单例设计的一部分:MainWindowClass::GetSharedWindowInstance() 将返回一个指向 SharedWindow 实例的指针,如果没有则创建一个' t 已经存在。 MainWindow.h包含SharedWindow* pSharedWindow备份功能。 (这与 SharedWindow 接近单身。)

MainWindow 实例化 MdiChildWindowAMdiChildWindowB 时,它会将this 传递给它们的构造函数,它们保存在类变量pMainWindow 中(在MdiChildWindowA.hMdiChildWindowB.h 中定义为MainWindow*)。

cout of this in MainWindowcout of pMainWindow 在 MDI 子窗口构造函数中匹配,但到另一个函数调用 pMainWindow->GetSharedWindowInstance() 时,pMainWindow 已更改!将pMainWindow设为静态似乎解决了问题但是pMainWindow是如何变化的呢?

同样,我发现HMODULELPPICTURE 变量在SharedWindow.h 中是静态的,否则它们会忘记它们在SharedWindow.cpp 中的函数之间的值。 指针类型是否以某种方式免于作为类变量的持久性?我认为static 旨在确保其类的所有实例都有一个值。

编辑 2013 年 9 月 4 日:
下面是我的Application.cpp(主要是从教程中复制的)。我以为我的 MainWindow 实例是在堆上创建的,并且会一直存在直到退出。

#include "MainWindow.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nCmdShow )
{   MSG  msg;
    HWND hMdiClientWindow;

    MainWindow *winMain = new MainWindow( hInstance );
    if( !winMain->Run( nCmdShow ) )
    {   delete winMain;
        return 1;
    }

    hMdiClientWindow = winMain->GetMdiClientWindow();

    while( GetMessage( &msg, NULL, 0, 0 ) )
    {   if( ! TranslateMDISysAccel( hMdiClientWindow, &msg ) )
        {   TranslateMessage( &msg );
            DispatchMessage ( &msg );
        }
    }

    delete winMain;

    return msg.wParam;
}

new MainWindow(...) 调用MainWindow::MainWindow(),其中cout 显示this0xdd13a0

MainWindow是在调用Run(...)时创建的,它将指针传递给lpParamMainWindow的实例:

bool MainWindow::Run( int nCmdShow )
{   ...
    hMainWindow = CreateWindowEx( ..., this );
    ...
}

在窗口过程中,指针保存在MainWindow的实例数据中:

LRESULT CALLBACK MainWindow::MainWindowProcedure( HWND hMainWindow, UINT Msg, WPARAM wParam, LPARAM lParam )
{   MainWindow* pThis;

    if( Msg == WM_NCCREATE )
    {   CREATESTRUCT* pCreateStruct = (CREATESTRUCT*) lParam;
        pThis = (MainWindow*) pCreateStruct->lpCreateParams;
        SetWindowLongPtr( hMainWindow, GWL_USERDATA, (LONG) pThis );
    } else
    {   pThis = (MainWindow*) GetWindowLongPtr( hMainWindow, GWL_USERDATA );
    }

WM_CREATE 中,cout 在传递给MdiChildWindowAMdiChildWindowB 的构造函数时显示pThis0xdd13a0

switch( Msg )
{   ...
    case WM_CREATE:
    {   unique_ptr<MdiChildWindowA> upMdiChildWindowA;
        unique_ptr<MdiChildWindowB> upMdiChildWindowB;
        ...
        up_MdiChildWindowA = unique_ptr<MdiChildWindowA>( new MdiChildWindowA( m_hInstance, pThis, [window dimensions] ) );
        up_MdiChildWindowB = unique_ptr<MdiChildWindowB>( new MdiChildWindowB( m_hInstance, pThis, [window dimensions] ) );

MDI子窗口的构造函数将参数pMainWindow中的MainWindow指针复制到类变量m_pMainWindow中,cout显示两者都包含0xdd13a0

MdiChildWindowA::MdiChildWindowA( HINSTANCE hInstance, MainWindow* pMainWindow, ... )
{   m_pMainWindow = pMainWindow;
    ....
}

在MDI子窗口过程的WM_CREATE中,cout显示m_pMainWindow仍然包含0xdd13a0。对m_pMainWindow 的唯一其他引用出现在WM_LBUTTONDBLCLICK 中,除非我将其设为静态,否则它会以某种方式变为0xdd1380(可能在通过DefMDIChildProc(...) 的过程中?):

MdiChildWindowA::MdiChildWindowProcedure( ... )
{   ...
    switch( ... )
    {   ...
        case WM_LBUTTONDBLCLICK:
        {   SharedWindow* pSharedWindow;
            ...
            pSharedWindow = pThis->m_pMainWindow->GetInstanceOfSharedWindow();  // pThis points to this instance of MdiChildWindowA. cout confirms its value hasn't changed.

由于m_pMainWindow 指向错误的位置,当通过pSharedWindow 调用SharedWindow 函数时程序崩溃。然而,GetInstanceOfSharedWindow() 似乎存在于 MainWindow 的这个虚假实例中,因为返回了一个地址 - 但是,在上面的代码中,它是 MdiChildWindowA 的地址!

(注意:我的命名习惯让人抓狂,所以我用不太危险的名字重新输入了代码。希望没有错别字。)

@brunocodutra、@Chris Hayes:我还没有足够的评论意见,但我很欣赏你的想法。

【问题讨论】:

  • pMainWindow 发生了什么变化?您很可能导致在某处调用了复制构造函数,因此您在两个不同的内存地址处拥有相同的对象。

标签: c++ class pointers static


【解决方案1】:

没有完整的相关代码很难说,但我猜 MainWindow 正在被释放。我会进一步猜测 MainWindow 最初存储在堆栈中,而不是堆中,因为导致地址更改的是函数调用(它会更改堆栈)。

我的建议:验证 MainWindowMdiChildWindowAMdiChildWindowB(不确定哪些 pMainWindow 更改)局部变量,如果是,请更改您的代码,以便它们在堆中分配,即通过使用关键字 new 动态分配。

回答第二个问题,指针与任何其他类型的处理方式没有区别,本质上它们非常像整数,但它们的内容被迅速允许解释为内存地址。

【讨论】:

    猜你喜欢
    • 2011-09-14
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 2010-11-30
    相关资源
    最近更新 更多