首先感谢an_bachelor 前辈关于我上个关于树形控件问题的答案,帖子在这里:
http://topic.csdn.net/u/20080429/15/f6a9b15d-ead7-4beb-8e58-84c417e6115c.html大概思路是这样的: 我现在每个树形节点需要关联信息,以反映它在数据库的位置(此树形控件用数据库条目构建),技术上是自己定义了一个包含信息的类:CTreeItemInfo,赋值后将此对象的指针存到TVITEM的lParam变量中. 以后响应节点的信息时就可以取出对应的对象再做进一步判断了.
我做的是一个基于对话框的程序: 在 Dlg类中定义了一个Vector:vector<CTreeItemInfo>,以便能动态添加对应的节点信息对象,这里我想用vector<CTreeItemInfo>保存所有的节点信息对象,就是CTreeItemInfo类型的对象. 每个节点的lParam的值为向vecTreeItemInfo push_back(CTreeItemInfo)后,(LPARAM)&vecTreeItemInfo.back().但是遇到一个奇怪的问题:在插入结点后,比如
         vecTreeItemInfo.push_back(infoTemp);//向Vector存入infoTemp对象
 lParamTemp=(LPARAM)&(vecTreeItemInfo.back());//得到此对象指针         tvInsert.hParent=NULL;
 tvInsert.hInsertAfter=NULL;
 tvInsert.item.mask = TVIF_TEXT;
 tvInsert.item.pszText="控制器";  hTreeParent=m_ctrlTree.InsertItem(&tvInsert);
  
就用:
 m_ctrlTree.SetItemData(hTreeParent,lParamTemp);
设置其对象的指针.在该句下断,测试语句为
   
         int temp=((CTreeItemInfo *)(lParamTemp))->nType;//nType和strPanelID为CTreeItemInfo的成员.
 CString strTemp=((CTreeItemInfo *)(lParamTemp))->strPanelID;显示正常,同时看到指针值 ,我这里是 lParamTemp=3622912,(CTreeItemInfo *)(lParamTemp)=0x00374800;
我不明白为什么LPARAM类型的值转换为CTreeItemInfo *的值会变? 希望高人讲解一下. 但是这句没问题,nType值期望是-1,strPanelID的值期望是"InValid",这里都没问题.   诡异的事情出现了!我在节点响应 TVN_SELCHANGED消息时 ,函数如下:  
void CDBTestDlg::OnSelchangedTreeDisp(NMHDR* pNMHDR, LRESULT* pResult) 
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here LPARAM lParamTemp=m_ctrlTree.GetItemData(pNMTreeView->itemNew.hItem); int temp=((CTreeItemInfo *)(lParamTemp))->nType;
strTemp=((CTreeItemInfo *)(lParamTemp))->strPanelID;}
 这里lParamTemp仍=3622912,(CTreeItemInfo *)(lParamTemp)=0x00374800;也就是说指针正确传到函数里了,但是temp的值变为5,不是期望的-1;而且执行到
strTemp=((CTreeItemInfo *)(lParamTemp))->strPanelID; 会产生异常,调试时看到的((CTreeItemInfo *)(lParamTemp))->strPanelID值为"".我就不理解了,为什么指针值传过来没变,那个vecTreeItemInfo,Vector变量我定义在Dlg类中,怎么通过同一个指针就得不到正确结果呢?是不是Vector的用法有问题?
谢谢大家帮忙看看了!!

解决方案 »

  1.   

    infoTemp 是结构不是指针?
    检查在 OnSelchangedTreeDisp 之前是否有 pop等操作如果有那么infoTemp已经释放!
      

  2.   

    本帖最后由 laiyiling 于 2008-05-07 15:29:15 编辑
      

  3.   


    vector <CTreeItemInfo*> vecTreeItemInfo;
    CTreeItemInfo*  infoTemp=new CTreeItemInfo;
    infoTemp->nType=0xFFFFFF;
    infoTemp->strPanelID="01";
    vecTreeItemInfo.push_back(infoTemp);//向Vector存入infoTemp对象
    LPARAM lParamTemp=(LPARAM)vecTreeItemInfo.back();
    //后面省略代码不全,估计是infoTemp对象以析构。改成以上样子看看。
      

  4.   

    确实没有pop,我做的东西很简单的,....: )那个用Vector 的原因就是因为它能动态分配空间,我想在一个函数中对树控件初始化,但如果在函数中分配了空间,退出函数会不会造成泄漏阿?我需要知道程序关闭前一直保存节点信息对象.
      

  5.   

    粗略看了下 back()他返回的是一個元素的引用 CTreeItemInfo& 你取出來后把它當成了CTreeItemInfo*來用 應該是這個引起的
    你用vector<CTreeItemInfo*>試試
      

  6.   

    哦看錯了 你前面又取了地址 那應該是一個CTreeItemInfo&的指針
      

  7.   

    另外应该不会是析构了把,因为这个对象我已经push进入Vector 中了,不应该析构了吧?难道获取Vector中的成员不能利用指针? Vector中的成员的地址会变? 在前面插入成员可能出现这种情况(我猜测),可我直接加在后面的话一个单元(CTreeItemInfo对象)的地址也会变吗?
      

  8.   

    你push进入Vector中的只是CTreeItemInfo的引用,Vector并不能控制它的生命周期,你改成我上面代码的样式,
    push进入Vector中的为new出来的CTreeItemInfo的指针,infoTemp的生命周期由vecTreeItemInfo控制,程序退出时,再由vecTreeItemInfo remove和delete。
      

  9.   

    那我试一试 vector <CTreeItemInfo*> ,既然大家都这么建议 .那么我对于CTreeItemInfo对象的内存分配只能在我那个初始化树形控件的函数中 infoTemp=new CTreeItemInfo,那么程序退出的时候我需不需要delete 一下? 可是我Delete什么呢? 是把Vector储存的CTreeItemInfo* 变量挨个delete掉吗?不Delete VC能自动处理不能?
    那小弟就先试一下了,可是我还是很想知道我的方法为什么不行....
      

  10.   

    strPanelID 是什么类型?char? 
    检测是否有写溢出//////////////////////////////////////////////////////////////////////////////////////////////
    CString? 不能在结构间用 memcpy 类似型为拷贝
    CString 自已记录一个缓中,如只是 memcpy 那么么只是把缓冲地址拷到另一具 CString 中,内容并没有复制!    vecTreeItemInfo.push_back(infoTemp);//向Vector存入infoTemp对象
         lParamTemp=(LPARAM)&(vecTreeItemInfo.back());//得到此对象指针如果infoTemp 只是一个函数内局部变量,那么在函数退出是 CString 中的缓冲也释放了,你在
    m_ctrlTree.SetItemData(hTreeParent,lParamTemp); 
    后检测如果,是在一具函数内那么显示结果是正常的,因为这时CString 还没释放而在
    void CDBTestDlg::OnSelchangedTreeDisp(NMHDR* pNMHDR, LRESULT* pResult) 
    中你取出的只是一个已经释放了的CString 指针strTemp=((CTreeItemInfo *)(lParamTemp))->strPanelID;
      

  11.   

    恩,是CStringclass CTreeItemInfo  
    {
    public:
    void ItemReset();
    int nType;   //0:控制器节点;1:板卡;2:回路;
    int nLoopID;
    CString strPanelID;
    int nControllerID;
    CTreeItemInfo();
    virtual ~CTreeItemInfo();};您的话很深奥,我理解下,呵呵 T_T!
      

  12.   

    push_back應該是會值拷貝的 如果你的CTreeItemInfo沒有重載=運算符的話應該是值拷貝另外我細看了下back函數 他返回的值類型是allocator::const_reference
    而allocator<CTreeItemInfo>::const_reference最終被推演為const CTreeItemInfo&
    所以歸根結底 你是把一個CTreeItemInfo&再取地址保存在了LPARAM中 
      

  13.   

    其實你用vector  <CTreeItemInfo>也沒問題 關鍵你LPARAM中保存的東西有問題 保存相應數據在vector中的索引就一點問題沒有了
      

  14.   

    ////////////////////////////////////////////////////////////////////////
    CString
    {
    ...............
    protected:
    LPTSTR m_pchData;   // 真实的数据指针,对像随对像而存在
    ..............
    }///////////////////////////////////////////////////////////////////////////
    vecTreeItemInfo.push_back(infoTemp);//向Vector存入infoTemp对象
    它会新建一个内存区 A 然后将infoTemp复制到 A
    等同于
    xxxx::push_back(...)
    {
    ....
    CTreeItemInfo  *A = new CTreeItemInfo;
    memcpy(A,&infoTemp);
    .....
    }
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    将CString strPanelID;
    换成 
    char strPanelID[xxx];就行了//如果大小不确定就用CTreeItemInfo* ,自已管理释放问题
      

  15.   

    Vector:vector <CTreeItemInfo> //<== 这儿会是对象的拷贝或克隆Vector:vector <CTreeItemInfo*> //这样就保存的是指针,和Tree里的就一致了,但是要负责CTreeItemInfo*的分配和释放如果用Vector:vector <CTreeItemInfo>,就把在vector里的索引值保存到lParamTemp这里,像:
    vecTreeItemInfo.back();
    lParamTemp=vecTreeItemInfo.size();
    //
    LPARAM lParamTemp=m_ctrlTree.GetItemData(pNMTreeView->itemNew.hItem);    int idx= (int)lParamTemp;
        CTreeItemInfo * pTemp= & vecTreeItemInfo[idx];
        
      

  16.   

    Vector:vector  <CTreeItemInfo> // <== 这儿会是对象的拷贝或克隆Vector:vector  <CTreeItemInfo*> //这样就保存的是指针,和Tree里的就一致了,但是要负责CTreeItemInfo*的分配和释放如果用Vector:vector  <CTreeItemInfo>,就把在vector里的索引值保存到lParamTemp这里,像:
    vecTreeItemInfo.push_back();
    lParamTemp=vecTreeItemInfo.size();
    //
    LPARAM lParamTemp=m_ctrlTree.GetItemData(pNMTreeView->itemNew.hItem);    int idx= (int)lParamTemp;
        CTreeItemInfo * pTemp= & vecTreeItemInfo[idx]; 
      

  17.   

    你有没有为infoTemp分配内存空间,用局部定义是不行的哦
      

  18.   

    infoTemp只是一个临时变量,压入Vector后它就没用了(我是这么想的),压入Vector是否是复制一份infoTemp,还是简单的把指针传进去?我觉得是复制吧,这样infoTemp被析构掉后对结果应该没什么影响吧.
    fairyprince:
     您好!
     我试了下定义成strPanelID[20],可还是会出错,只是指针值传进TVN_SELCHANGED响应函数来,但感觉指针指向的单元已经不存在了,感好像是压入Vector时没有把临时变量infoTemp的值复制,或许只是引用了一下. 而且不光是CString 类型的变量,int变量的值也不对.谢谢大家了,我今晚抓紧试一下吧,最早明天结帖了,100分对于大家这么热心的帮忙来说真的太少了,我明天再加100再结,谢谢各位了.
      

  19.   

    給你個還是用vector   <CTreeItemInfo>的方法
      vecTreeItemInfo.push_back(infoTemp);//向Vector存入infoTemp对象
      lParamTemp= vecTreeItemInfo.size();//得到此对象指针

    tvInsert.hParent=NULL;
    tvInsert.hInsertAfter=NULL;
    tvInsert.item.mask = TVIF_TEXT;
    tvInsert.item.pszText="控制器";

                hTreeParent=m_ctrlTree.InsertItem(&tvInsert);
    然後要用到它取值的時候void CDBTestDlg::OnSelchangedTreeDisp(NMHDR* pNMHDR, LRESULT* pResult) 
    {
        NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
        // TODO: Add your control notification handler code here    DWORD lParamTemp=m_ctrlTree.GetItemData(pNMTreeView->itemNew.hItem);    if (lParamTemp--){
            int temp=vecTreeItemInfo[lParamTemp].nType;
            strTemp=vecTreeItemInfo[lParamTemp].strPanelID;
        }
    }
    按説CTreeItemInfo中要重載operator=運算符為 CTreeItemInfo& operator =(const CTreeItemInfo& val)
    {
    this->strPanelID = val.strPanelID;
    this->nType = val.nType;
    return *this;
    }但奇怪的是沒有重載也沒出問題 看來默認的拷貝構造函數對struct還是會一個一個成員調用=來賦值二步是值拷貝 至少對於非簡單類型對象如此
      

  20.   

    Vector 是动态数组,它的大小是可变的,也就是说它保存的对象的地址会发生变化,但是对象的成员的值会不变,所以不应该认为对象的地址是固定的。假设没16个对象分配一次内存,可能在1-16个的时候地址是固定的,在1-32的时候又是另一个值,因为大于16个的时候,重新重组了内存
      

  21.   

    Vector 是容器,每项的地址是可能变的
    如果预留的空间不够,他会执行得新分配一个,足够的空间然后再将旧的数据复制到新的空间里,然后再删除旧数据!vecTreeItemInfo.push_back(); 会引起上述操作
    所以,strTemp=((CTreeItemInfo *)(lParamTemp))->strPanelID; 这里可能已不是原始值了!Vector 改成 listlist 不会有上述问题
      

  22.   

    按照 xjh_net,an_bachelor和fairyprince大哥的做法,已经搞定了,果然是在向Vector  Push数据的时候,指针发生了移动,用索引访问一点问题没有;再次感谢 xjh_net,an_bachelor的算法. 明天加分结帖.北京手机尾号1616的朋友发来一条感慨:看到奥运会上中国健儿的优异表现,心情久久不能平息。他有一个美好的设想,如果让夺得110米栏冠军的刘翔和夺得女子万米长跑冠军的邢惠娜结为夫妻,并且生下一个小孩儿,奥运会能不能为这个小孩儿增加一个竞赛项目--一万米栏。