各位好, 小弟在学习第八章时有一些疑问,希望过来人能帮小弟一下第八章是一个画线条,并可以保存文件的程序其中定义了一个类
class CStroke : public CObject
{
    ....
    DECLARE_SERIAL(CStroke)
}
IMPLEMENT_SERIAL(CStroke ,CObject, 1)宏展开后:class CStroke : public CObject
{
    ....
    friend CArchive& AFXAPI opeator>>(CArchive& ar, CStroke* &pOb) ;
}CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
{
    pOb = (CStroke*) ar.ReadObject(RUMTIME_CLASS(CStroke));
}以上的文字出现在书中400页(电子书536页)
我看不懂的地方是 CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
在何时调用,书中第386页(电子书518页)void CObList::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
...
}
else
{
DWORD nNewCount = ar.ReadCount();
CObject* newData;
while (nNewCount--)
{
ar >> newData;        -----|
AddTail(newData);          |
}                          |
                           |
                           V
operator>> 被重载(overloading )化_AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
{ pOb = ar.ReadObject(NULL); return ar; }该处调用的是  CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
        不是  CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)我找不出CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)在何处被
调用,看过这本书的过来人能帮我一下吗,帮我解答一下,小弟先谢了还有书中401页(电子书536页)写到
  
 好,你看到了,為什麼只改寫 operator>>,而沒有改寫 operator<<?原因是 WriteObject 並不需要CRuntimeClass 資訊,但 ReadObject 需要,因為在讀完檔案後還要做動態生成看不懂
各位过来人,尤其是看过这本书的人,帮帮我好吗,小弟先谢了
 

解决方案 »

  1.   

    其实也可以这么用的
    CStroke* newData;
    while (nNewCount--)
    {
    ar >> newData;
    宏是常用代码的组合,并不是每一行都必须用到
      

  2.   

    谢谢jiangsheng!后来我把
    DECLARE_SERIAL(CStroke)该为DECLARE_DYNCREATEIMPLEMENT_SERIAL(CStroke ,CObject, 1)该为IMPLEMENT_DYNCREATE(CStroke,CObject)重新编译后通过,但可执行文件不能读写文件
    我想既然不用到CArchive& AFXAPI operator>>(CArchive& ar,CStroke*& pOb)
    是否可以象上面那样更改.
    还有
    _AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
    的使用是否同IMPLEMENT_SERIAL(CStroke ,CObject, 1)没什么关系
    我认为只要CStroke 派生之 CObject 就可以使用
    _AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
     
      

  3.   

    该处调用的是  CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
            不是  CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)我找不出CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)在何处被
    调用...我想既然不用到CArchive& AFXAPI operator>>(CArchive& ar,CStroke*& pOb)________________________
    你忘了CStroke是从CObject派生的了?建议回头看看第二章先
      

  4.   

    还有,看看电子书501页“檯面上的Serialize 動作”部分,你就明白了
      

  5.   

    谢谢pomelowu!
    DECLARE_SERIAL(CStroke)该为DECLARE_DYNCREATE(CStroke)IMPLEMENT_SERIAL(CStroke ,CObject, 1)该为IMPLEMENT_DYNCREATE(CStroke,CObject)保证了CStroke派生之CObject,并具有动太生成能力,满足
    CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)的调用你的意思是ar >> newData;  应该是使用
    CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
    而不是
    CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)我不是很清楚, newData 是CObject* 类型的,
    operator>> 不是虚函数吧?后来我想用单步跟踪一下到底是调用哪个函数,但老是出现汇编代码
    ,你能帮我单步一下吗,Frame/scribbl/step1(网上)
    Dissect/scribbl/step1(书上附带光盘)第二章,要看哪一部分
      

  6.   

    你的意思是ar >> newData;  应该是使用
    CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
    而不是
    CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)____你理解错了,我的意思是,因为CStroke是从CObject派生的,Serialize到CObject::Serialize(CArchive& ar)的时候会调用CStroke::Serialize(CArchive& ar),于是自然就会调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)这个重载。至于为什么Serialize到CObject::Serialize(CArchive& ar)的时候会调用CStroke::Serialize(CArchive& ar),就是第二章的内容了。第二章的继承、虚函数与多态部分。
      

  7.   

    谢谢pomelowu!!
    书中((CObject*)pOb->Serialize(*this) 
    实际调用的是CStroke::Serialize(*this),这个我理解,Serialize是虚函数
    CStroke::Serialize(CArchive& ar),于是自然就会调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)这个重载。上面的我不理解书中401页(电子书536页)写到
      
     好,你看到了,為什麼只改寫 operator>>,而沒有改寫 operator<<?原因是 WriteObject 並不需要CRuntimeClass 資訊,但 ReadObject 需要,因為在讀完檔案後還要做動態生成
    但前面的代码出现过
    ar >> newData;  
    ar << newData;  为什么有IMPLEMENT_SERIAL(CStroke ,CObject, 1)展开时有
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    而不实现
    CArchive& AFXAPI operator<< (CArchive& ar,CStroke*& pOb)
    前面ar << newData 用到是CArchive& AFXAPI operator<<(CArchive& ar,CObject*& pOb)
                        不是CArchive& AFXAPI operator<<(CArchive& ar,CStroke*& pOb)
    那么这个CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb) 又在哪里用到呢
    如果不用到DECLARE_DYNCREATE(CStroke),IMPLEMENT_DYNCREATE(CStroke,CObject)就可以满足要求了,但生成可执行文件又不能读写文件。
      

  8.   

    operator<< 是直接调用现有CObject派生类的对象的虚函数Serialize,不需要知道具体的类
    operator>>到一个指针的话,是创建一个对象再调用创建的CObject派生类的对象的虚函数Serialize,所以要知道具体的类型。
    如果对象已经创建,可以直接调用其Serialize函数而不用operator。
      

  9.   

    CStroke::Serialize(CArchive& ar),于是自然就会调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)这个重载。_____________________Stroke.cpp中
    CStroke::Serialize调用之前,有IMPLEMENT_SERIAL(CStroke, CObject, 1),IMPLEMENT_SERIAL(CStroke, CObject, 1)展开成为
    class CStroke : public CObject
    {
        ....
        friend CArchive& AFXAPI opeator>>(CArchive& ar, CStroke* &pOb) ;
    }CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
    {
        pOb = (CStroke*) ar.ReadObject(RUMTIME_CLASS(CStroke));
    }
    这里已经在CStroke类里重载友元运算符>>调用CStroke::Serialize的时候,在函数里会用到友元运算符>>,自然是调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)了。
    ============================书中401页(电子书536页)写到
      
     好,你看到了,為什麼只改寫 operator>>,而沒有改寫 operator<<?原因是 WriteObject 並不需要CRuntimeClass 資訊,但 ReadObject 需要,因為在讀完檔案後還要做動態生成
    但前面的代码出现过
    ar >> newData;  
    ar << newData;  为什么有IMPLEMENT_SERIAL(CStroke ,CObject, 1)展开时有
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    而不实现
    CArchive& AFXAPI operator<< (CArchive& ar,CStroke*& pOb)
    前面ar << newData 用到是CArchive& AFXAPI operator<<(CArchive& ar,CObject*& pOb)
                        不是CArchive& AFXAPI operator<<(CArchive& ar,CStroke*& pOb)
    那么这个CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb) 又在哪里用到呢
    如果不用到DECLARE_DYNCREATE(CStroke),IMPLEMENT_DYNCREATE(CStroke,CObject)就可以满足要求了,但生成可执行文件又不能读写文件。
    ___________________你又理解错了。等下午有空了再给你解释
      

  10.   

    呵呵,第二个问题 jiangsheng(蒋晟.MSMVP2004Jan) 说得很清楚了。
    另外,IMPLEMENT_SERIAL展开之后没有重载<<,也就是说,没有所谓的CArchive& AFXAPI operator<<(CArchive& ar,CStroke*& pOb)这个东西。
    而CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)的确是用到的。再回头看看虚函数、多态和动态创建吧。
      

  11.   

    谢谢两位,我觉得 jiangsheng(蒋晟.MSMVP2004Jan) 说得
    "宏是常用代码的组合,并不是每一行都必须用到" 
    有道理
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    根本没有调用到
    而CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)
    到是调用了我查看宏IMPLEMENT_SERIAL
    #define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)  CObject* PASCAL class_name::CreateObject()  { return new class_name; }  _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema,  class_name::CreateObject)  AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));  CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  { pOb = (class_name*) ar.ReadObject(RUNTIME_CLAS(class_name));  return ar; } 
    以是我复制该宏到我自己的宏IMP_SERIAL
    #define IMP_SERIAL(class_name, base_class_name, wSchema)  CObject* PASCAL class_name::CreateObject()  { return new class_name; }  _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema,  class_name::CreateObject)  AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); 并把该宏的内容放在ScribDoc.h 最顶处
    在ScribDoc.cpp 文件中把IMPLEMENT_SERIAL(CStroke, CObject, 1)
    改成                   IMP_SERIAL(CStroke, CObject, 1)
    也就是没有了
    CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  { pOb = (class_name*) ar.ReadObject(RUNTIME_CLAS(class_name));  return ar; } 
    的这个宏,编译工程后,文件具有读写文件功能,
    也就是说根本没有调用CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    调用的是CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)
    因为我自己的宏IMP_SERIAL(CStroke, CObject, 1)根本就没有
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    的实现至于用IMPLEMENT_DYNCREATE(CStroke,CObject)为什么不可以代替IMPLEMENT_SERIAL
    是因为IMPLEMENT_DYNCREATE展开后没有
    AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); 
    而我自己定义的宏IMP_SERIAL有我用的是vc6.0,而书中390页(电子书523页)
    _IMPLEMENT_RUNTIMECLASS(....)
    {
        .....
    static AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); 
        .....
    }
    而我的afx.h文件中
    #define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew)  CRuntimeClass* PASCAL class_name::_GetBaseClass()  { return RUNTIME_CLASS(base_class_name); }  AFX_COMDAT AFX_DATADEF CRuntimeClass class_name::class##class_name = {  #class_name, sizeof(class class_name), wSchema, pfnNew,  &class_name::_GetBaseClass, NULL };  CRuntimeClass* class_name::GetRuntimeClass() const  { return RUNTIME_CLASS(class_name); } 
    所以IMPLEMENT_SERIAL展开后比IMPLEMENT_DYNCREATE展开多了
    AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));  CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  { pOb = (class_name*) ar.ReadObject(RUNTIME_CLAS(class_name));  return ar; } 所以即使没有用到CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)
    也不能用IMPLEMENT_DYNCREATE代替IMPLEMENT_SERIAL,因为没有AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); 
    不知我的理解是否正确,我看书一向很仔细.不知<<深入>>上的_IMPLEMENT_RUNTIMECLASS
    是否有错,我用的vc6.0,而书上用的是vc5.0
      

  12.   

    恐怕我不能同意你的意思。使用你定义的IMP_SERIAL宏在VC5.0下也能实现读写。所以这个跟编译器无关。如果你看书仔细的话应该记得,书上的这个例程框架是由wizzard生成的,候先生并没有自己去写那个宏(当然,在之前他写过)。如果你还不信,可以在工程里边看看这个宏的定义,是在“AFX.H”中的。如果是出错,那就将是微软的一个大BUG了。把
    CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  { pOb = (class_name*) ar.ReadObject(RUNTIME_CLAS(class_name));  return ar; } 这一段去掉,那么程序的确是没有调用CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)了,也说明如果调用CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)的话正好能在这个例子中完成文件保存的工作。但是,并不能说明没去掉它之前它就没有用到啊。Serialize是虚函数,在调用到CObject::Serialize的时候会跳到CStroke::Serialize里去。此时,如果CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)存在,当然是调用CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb),如果CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)不存在,则会折回到CObject中去寻找有没有>>的重载,如果有,则会调用CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)。
      

  13.   

    谢谢,我对我的IMP_SERIAL进行修该使它等效于IMPLEMENT_SERIAL并加入调试信息,其它不变define IMP_SERIAL(class_name, base_class_name, wSchema)  CObject* PASCAL class_name::CreateObject()  {                      TRACE0("call CreateObject()\n");                      return new class_name; }  _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \ class_name::CreateObject)  AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));  CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  {  TRACE0("Call  operator>>\n"); pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name));  return ar; } 重新编译,编译环境设置好,F5后,打开文件,保存文件,
    调试窗口中
    有:
    call CreateObject()
    没有:
    Call  operator>>
    说明:
    如果CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)存在,也不调用CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)不知是否正确
      

  14.   

    你这样做只说明如果CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)不存在的情况下能进行正常操作。不能证明如果CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)存在的情况下也不被调用。举个例子
    class A
    {
    public:
        virtual void sound(){AfxMessageBox("Test");}
    }class B: public A
    {
    public:
        virtual void sound(){AfxMessageBox("Test");}
    }如果有 
    B b;
    那么
    b.sound();
    回弹出一个对话框。而修改B:
    class B: public A
    {
    public:
        virtual void sound();
    }
    再运行
    b.sound();
    也会弹出对话框。那么你就能说修改以前的B中的virtual void sound(){AfxMessageBox("Test");}没有被调用到吗?
      

  15.   

    谢谢我的证明对应你的模型是这个样子的
    #include <iostream>
    using namespace std ;
    class A
    {
    public:
        virtual void sound()=0 ;
    };class B: public A
    {
    public:
        virtual void sound(){cout<<"test"<<endl;}
    };void main()
    {
    B b ;
    b.sound() ;
    }
    如果没有调用b.sound(),就不会有test ,按你的例子有 test 不一定会调用
    但没有test就一定没有调用b.sound(),因为调用了就一定会有test
    同样道理Call  operator>> 没出现就没有调用
    CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)  { TRACE0("Call  operator>>\n");pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name));  return ar; } 
    在CcriblDoc.cpp中IMP_SERIAL(CStroke, CObject, 1)
    展开后就有
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    {
    TRACE0("Call  operator>>\n");
    pOb = (CStroke*) ar.ReadObject(RUNTIME_CLASS(CStroke));  return ar; }
    没有输出 "Call operator"就一定没有调用
    CArchive& AFXAPI operator>> (CArchive& ar,CStroke*& pOb)
    虽然出现"Call operator"不一定没有调用再者这个问题跟虚函数没有什么关系
     operator>>  不是一个虚函数,没有 virtual关健字CObject* newData;
    while (nNewCount--)
    {
    ar >> newData;       
    AddTail(newData);          
    } ;
    到底>>该调用哪一个>>呢 ,根据参数的类型决定
    ar 属于CArchive类型
    newData 属于CObject*类型
    故调用CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)
    operator >>不是虚函数
    你多处说到:
    CStroke::Serialize(CArchive& ar),于是自然就会调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)这个重载。
     
    我十分不理解,"自然会调用" ?? 难到CStroke::Serialize(CArchive& ar)里面出现的代码
    要调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
    要调用operator>> 是出现在CObjectList::Serialize里边的ar>>newData ;
    而此处调用的是CArchive& AFXAPI operator>> (CArchive& ar,CObject*& pOb)
    根据参数类型确定,operator>> 不是虚函数
    程序里边根本就没有调用CArchive& AFXAPI operator>>(CArchive& ar,CStroke* &pOb)
    这个函数,调用了就输出"Call operator>>" ,没输出何来调用