<b>调用虚拟函数、持续化视图状况及POD范例概念</b>[VC/C++编程]
本文“<b>调用虚拟函数、持续化视图状况及POD范例概念</b>[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
在 C++ 中,无法从某个类的构造函数中调用派生的虚拟函数,因为虚表还没有完好成立.但是在C#中仿佛便可以,是这样吗?为什么会有这种差别呢?
确切如此,在这个方面 C# 与 C++ 是有差别的.在 C++ 中,假如你从构造函数大概析构函数中调用虚拟函数,编译器调用的虚拟函数是定义在这个正在被构造的类实例中的(比方,假如从 Base::Base 中调用 Base::SomeVirtFn ),不是最底层派生的实例(the most derived instance),正像你说的那样,因为在最底层派生的构造函数履行之前,虚表还没有完好被初始化.另一种说法是派生类还没有被成立.
Figure 2 虚拟函数 TestSimilarly
当你从析构函数中调用虚函数时,C++ 调用该基类的析构函数,因为派生类已经被销毁(其析构已经被调用).固然这个行为可招致非常后果(此即为什么从构造函数或析构函数中调用虚函数被认为是糟糕的编程实践的缘由),它是大大都 C++ 程序员必须了然于心的基本常识.
正如你所指出的那样,在 C# 有所差别.托管对象——无论是在 C#,托管 C++ 中,还是任何别的的 .NET 兼容语言中——是作为其终究范例被成立的,也就是说,假如你从构造函数或析构函数中调用虚函数,系统调用的是最末层派生的函数.Figure 1 所示程序举例阐明了这一点.假如你编译并运行这个程序,你会看到 Figure 2 所示输出.
这种行为关于 C++ 程序员来说仿佛有些独特.它意味着在派生类被初始化之前,你可以调用某个派生范例的虚拟函数——也就是说在其构造函数运行之前.一样,假如你从基类析构函数中调用虚函数,该函数是在派生类被销毁之后运行的——也就是说在析构函数被调用之后.那么先不说这种差别存在的缘由,方才不是还说从构造函数/析构函数中调用虚函数被认为是糟糕的实践.
为什么微软的家伙们要像这样来计划 C# 呢?因为它简化了内存管理.垃圾汇集器为了释放内存,它需求知道对象有多大.假如 C# 像 C++ 那样构造对象,那么你大概会碰到这样一种情形:有两个对象,Obj1 和 Obj2,下面这两条语句都为真:
typeof(Obj1)==typeof(Obj2)
sizeof(Obj1)!= sizeof(Obj2)
因为对象之一是被部份构造.(不要忘了垃圾汇集器是异步运行的.)通过将对象构造成终究范例,垃圾汇集器能从其范例决意对象的大小.假如 C# 像 C++ 那样举行部份构造,则垃圾汇集器将需求更多的代码来决意部份构造对象的真实大小.这样将带来复杂性和性能下降,首先要办理这个问题很让人灰心,所认为了较快的垃圾汇集好处,微软的家伙们决意像上面那样来实现 C#.有关这方面的谈论拜见 Raymond Chen 的 blog:“The Old New Thing”.
在 2004 三月的专栏中,你展示了若何改变文件翻开对话框的最新视图状况设置,但没有触及到保存这个用户利用的最新视图设置.我碰到的问题是读取用户已有的翻开文件对话框设置.我只找到直接读取列表框信息的办法,但当用户挑选缩略图情势时,那样做不能得到精确的信息.对此你有没有办理办法?
我正在用大众的 CFileDialog 类做开辟,应当不是很难,但事情仿佛并非那样.我想强迫文件翻开对话框的视图情势为缩略图.我要用 Visual C++ 来做,你可否供应一些倡议?
有几个读者都在问文件翻开对话框中的缩略图问题.在我三月份的专栏中,我示范了假如向文件翻开对话框中的 SHELLDLL_DefView 专用窗口发送 WM_COMMAND 消息以设置差别的视图情势——但你若何知道当前所处的情势是哪一个呢?你必须获得列表控件并调用 CListCtrl::GetView:
// in dialog class
HWND hlc = ::FindWindowEx(m_hWnd,
NULL, _T("SysListView32"), NULL);
CListCtrl* plc = (CListCtrl*)CWnd::FromHandle(hlc);
DWORD dwView = plc->GetView();
CListCtrl::GetView 返回 LV_XXX 代码之一,但正像 Maarten 发现的那样,Windows 对图标情势和缩略图情势都返回 LV_VIEW_ICON.
那么若何辨别毕竟是哪类视图情势呢?我挖空心机并钻进头文件查找,最后发现一个叫 LVM_GETITEMSPACING 的消息,该消息是作什么用的呢——用来获得图标隔断.顾名思义,图标隔断是图标视图情势中图标之间的像素隔断.LVM_GETITEMSPACING 不是很好利用,以至于 MFC 都没有对之举行包装(比方说 MFC 中并没有 CListCtrl::GetIconSpacing 这样的函数).所以在 MFC 中你得自己发送消息:
CSize sz = CSize(plc->SendMessage(LVM_GETITEMSPACING));
Windows 按照普通方法返回尺寸,在高位和低位字中编码的 cx/cy,然后CSize很礼貌地为你举行解码.一旦有了图标隔断,你便可以将它与 GetSystemMetrics(SM_CXICONSPACING) 返回的系统隔断值举行对比.假如列表视图的图标隔断与系统的一样,则视图是图标情势.假如大于系统隔断,则视图为缩略图情势:
if (sz.cx > GetSystemMetrics(SM_CXICONSPACING)) {
// thumbnail view
} else {
// icon view
}
讲了那么多缩略图,接下来的问题是若何持续化差别用户会话的视图状况?对此,当程序终止时,你需求用 Profile 函数在用户配置文件中保存最后利用的情势,并在下一次启动程序时再次恢复它.我写了一个小示范程序,DlgTest.程序利用了一个实现持续化程序行为的类 CPersistOpenDlg.这个类又借助别的一个类 CListViewShellWnd,用它来封装 SHELLDLL_DefView 窗口(拜见三月份专栏).CListViewShellWnd 包含获得和设置视图情势的函数,由这些函数来辨别图标和缩略图情势:
CListViewShellWnd m_wndLVSW;
...
m_wndLVSW.SetViewMode(ODM_VIEW_THUMBS);
CListViewShellWnd 的 OnDestroy 处理器在某个数据成员 m_lastViewMode 中保存视图情势.当对话框被销毁时,CPersistOpenDlg 的析构函数调用 WriteProfileInt 将这个值写入用户配置文件.对话框启动时,CPersistOpenDlg 给自己送一个初始化消息;该消息处理例程调用 GetProfileInt 从磁盘读取存储在配置文件中的值并设置视图情势.PostMessage 是必须调用的,因为通例初始化消息 WM_INITDIALOG 和 CDN_INITDONE 在文件对话框被完好初始化之前就会到来——有关这一点的注释拜见三月份专栏.
趁便说一下,任什么时刻候你都应当利用 GetProfileXxx 和 WriteProfileXxx 来持续化利用程序的设置.MFC 用 CWinApp 包装了这些函数.假如你在利用程序启动时调用(普通都是在 InitInstance 函数中) CMyApp::SetRegistryKey("KeyName"),MFC 利用注册表来存储用户配置信息,而不是 INI 文件.下面是 DlgTest 用的 INI 文件:
[settings]
ViewMode=28717
无意在一些文字资料和 C++ 文档以及 Microsoft .NET 框架中看到术语“POD 范例”.这个术语是什么意思?
你可以将 POD 范例看做是一种来自外太空的用绿色保护层包装的数据范例,POD 意为“Plain Old Data”(译者:假如一定要译成中文,那就叫“彻彻底底的老数据”怎么样!)这就是 POD 范例的含义.其切当定义相当粗糙(拜见 C++ ISO 尺度),其基本意思是 POD 范例包含与 C 兼容的原始数据.比方,构造和整型是 POD 范例,但带有构造函数或虚拟函数的类则不是. POD 范例没有虚拟函数,基类,用户定义的构造函数,拷贝构造,赋值操作符或析构函数.
为了将 POD 范例概念化,你可以通过拷贝其比特来拷贝它们.此外, POD 范例可以是非初始化的.比方:
struct RECT r; // value undefined
POINT *ppoints = new POINT[100]; // ditto
CString s; // calls ctor ==> not POD
非 POD 范例普通需求初始化,不管是调用缺省的构造函数(编译器供应的)还是自己写的构造函数.
过去, POD 关于编写编译器或与C 兼容的 C++ 程序的人来说很重要.目前,POD 来到 .NET 的环境中.在托管 C++ 中,托管范例(包含 __value 和 __gc 二者)能包含嵌入的原生 POD 范例. Figure 3 展示了例举阐明代码.托管的 Circle 类能包含 POINT,但无法包含 CPoint 类.假如你尝试编译 pod.cpp 会报一个 C3633 错误:“Cannot define ''m_center'' as a member of managed ''Circle'' because of the presence of default constructor ''CPoint::CPoint'' on class ''CPoint''.”(译者:意思是由于类 CPoint 有缺省的构造函数‘CPoint::CPoint’,所以不能将‘m_center’定义为托管类‘Circle’的一个成员)
.NET 限定嵌入的本地对象只能为 POD 范例的来由是这样做能安全地拷贝它们,不用耽忧调用构造函数,初始化虚表,或任何非 POD 范例需求的别的机制.
以上是“<b>调用虚拟函数、持续化视图状况及POD范例概念</b>[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |