3

谁能解释一下以下用于在地图容器中插入新对象的方法的区别?我已经知道指针等,我并没有真正深入虚拟内存,只有基础知识(地址等)

#include "StdAfx.h"
#include <windows.h>
#include <cstdlib>
#include <iostream>
#include <map>

using namespace std;

class CUser
{
public:
    CUser() { Init(); };
    ~CUser() {};
public:
        BOOL m_bActive;
        BOOL m_bLoggedIn;
        SYSTEMTIME m_sysTime;

        void Init();
};


void CUser::Init()
{
    (*this).m_bActive = FALSE;
    m_bLoggedIn = FALSE;
    GetSystemTime( &m_sysTime );
}

int main(int argc, char *argv[])
{

    map<DWORD, CUser*>mUserMap;


    //what is the difference between this
    {   
        CUser pUser;
        pUser.m_bActive = FALSE;
        pUser.m_bLoggedIn = FALSE;
        GetSystemTime( &pUser.m_sysTime );
        mUserMap.insert( make_pair( 351, &pUser ) );
    }
    //and this?
    {
        CUser *pUser = new CUser;
        if( pUser )
        {
            pUser->m_bActive = TRUE;
            pUser->m_bLoggedIn = TRUE;
            GetSystemTime( &pUser->m_sysTime );
            mUserMap.insert( make_pair( 351, pUser ) );
        }
    }

/*  map<DWORD, CUser*>::iterator it = mUserMap.find( 351 );
    if( it == mUserMap.end() )
        std::cout << "Not found" << std::endl;
    else
    {
        CUser *pUser = it->second;
        if( pUser )
            std::cout << pUser->m_sysTime.wHour << std::endl;
    } */


    return 0;
}
4

4 回答 4

4

在第一种情况下,pUser在堆栈上创建,并在其名称超出范围时自动删除(即在下一个右大括号处)。一般来说,将用于堆栈对象的指针插入容器是不明智的,因为当容器仍然有指向它的值时,对象将不复存在。在最好的情况下,这可能会导致崩溃。在最坏的情况下,它可能会导致在代码的遥远部分中定位错误且难以定位。

于 2012-06-12T19:06:34.023 回答
3
//what is the difference between this
{   
    CUser pUser;
    pUser.m_bActive = FALSE;
    pUser.m_bLoggedIn = FALSE;
    GetSystemTime( &pUser.m_sysTime );
    mUserMap.insert( make_pair( 351, &pUser ) );
}

这将创建一个本地对象:您的pUser变量仅存在于此块的范围,并且在到达最后一个时不再存在}。这意味着它的析构函数被调用,它所在的内存被回收并且可以被重用。

现在,当您在地图中存储指向这个短暂对象的指针时,您正在存储一个问题。如果您在此块关闭后的任何时间取消引用该指针},您将调用未定义的行为。它可能会起作用。它有时可能会起作用,然后开始失败。基本上,这是一个逻辑错误,也是不可预知错误的良好来源。

//and this?
{
    CUser *pUser = new CUser;
    if( pUser )
    {
        pUser->m_bActive = TRUE;
        pUser->m_bLoggedIn = TRUE;
        GetSystemTime( &pUser->m_sysTime );
        mUserMap.insert( make_pair( 351, pUser ) );
    }
}

在这里,您显式地创建了一个实例,该实例的寿命将超过封闭范围,一切都很好。不过,您不需要检查是否new返回NULL:除非您明确要求它不这样做,否则它会抛出异常。

于 2012-06-12T19:14:01.650 回答
1
{   
    CUser pUser;
    pUser.m_bActive = FALSE;
    pUser.m_bLoggedIn = FALSE;
    GetSystemTime( &pUser.m_sysTime );
    mUserMap.insert( make_pair( 351, &pUser ) );
}
//pUser is not available here

pUser (Object) 不可用(已删除),mUserMap 中的指针无效!

{
    CUser *pUser = new CUser;
    if( pUser )
    {
        pUser->m_bActive = TRUE;
        pUser->m_bLoggedIn = TRUE;
        GetSystemTime( &pUser->m_sysTime );
        mUserMap.insert( make_pair( 351, pUser ) );
    }
}
//pUser is not available here

pUser(指针!!)不可用(已删除),内存仍被占用,因此 mUserMap 中的指针有效!

于 2012-06-12T19:04:20.467 回答
1

不同之处在于调用创建的对象new是在堆上而不是堆栈上创建的。这意味着一旦指针超出范围,分配的内存仍然存在于堆上,您可以通过存储在映射中的指针安全地引用它。

在第一种情况下,您在堆栈上创建一个对象并将其地址添加到地图中。这意味着当您本地创建的变量超出范围时,它会被销毁,并且映射中的指针现在指向一个不再存在的变量。这无疑会导致您的代码出现问题。

如果您必须使用指针而不是实际对象本身,请使用第一种方法。当您使用内存时new,内存将一直存在,直到您将其删除(或让另一个对象像共享指针一样处理它)。堆栈对象一旦超出范围就会被销毁。

于 2012-06-12T19:12:31.327 回答