UNREFERENCEDPARAMETER的用处.docx
- 文档编号:26195490
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:11
- 大小:23.84KB
UNREFERENCEDPARAMETER的用处.docx
《UNREFERENCEDPARAMETER的用处.docx》由会员分享,可在线阅读,更多相关《UNREFERENCEDPARAMETER的用处.docx(11页珍藏版)》请在冰豆网上搜索。
UNREFERENCEDPARAMETER的用处
UNREFERENCED_PARAMETER的用处
作用:
告诉编译器,已经使用了该变量,不必检测警告!
在VC编译器下,如果您用最高级别进行编译,编译器就会很苛刻地指出您的非常细小的警告。
当你生命了一个变量,而没有使用时,编译器就会报警告:
“warningC4100:
''XXXX'':
unreferencedformalparameter.”
所以,为了让编译器不必检测你的警告,就使用UNREFERENCED_PARAMETER语句。
比如:
intSomeFunction(intarg1,intarg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
我看到过一些C++代码针对没有使用过的参数用UNREFERENCED_PARAMETER,例如:
intSomeFunction(intarg1,intarg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
我还看到过这样的代码:
intSomeFunction(intarg1,int/*arg2*/)
{
...
}
你能解释它们的差别吗?
哪一种用法更好?
JudyMcGeough
是啊!
为什么呢?
让我们从UNREFERENCED_PARAMETER开始吧。
这个宏在winnt.h中定义如下:
#defineUNREFERENCED_PARAMETER(P)(P)
换句话说UNREFERENCED_PARAMETER展开传递的参数或表达式。
其目的是避免编译器关于未引用参数的警告。
许多程序员,包括我在内,喜欢用最高级别的警告Level4(/W4)进行编译。
Level4属于“能被安全忽略的事件”的范畴。
虽然它们可能使你难堪,但很少破坏你的代码。
例如,在你的程序中可能会有这样一些代码行:
intx=1;
但你从没用到过x。
也许这一行是你以前使用x时留下来的,只删除了使用它的代码,而忘了删除这个变量。
WarningLevel4能找到这些小麻烦。
所以,为什么不让编译器帮助你完成可能是最高级别的专业化呢?
用Level4编译是展示你工作态度的一种方式。
如果你为公众使用者编写库,Level4则是社交礼节上需要的。
你不想强迫你的开发人员使用低级选项清洁地编译他们的代码。
问题是,Level4实在是太过于注意细节,在Level4上,编译器连未引用参数这样无伤大雅的事情也要抱怨(当然,除非你真的有意使用这个参数,这时便相安无事)。
假设你有一个函数带来两个参数,但你只使用其中一个:
intSomeFunction(intarg1,intarg2)
{
returnarg1+5;
}
使用/W4,编译器抱怨:
“warningC4100:
''arg2'':
unreferencedformalparameter.”
为了骗过编译器,你可以加上UNREFERENCED_PARAMETER(arg2)。
现在编译器在编译你的引用arg2的函数时便会住口。
并且由于语句:
arg2;
实际上不做任何事情,编译器不会为之产生任何代码,所以在空间和性能上不会有任何损失。
细心的人可能会问:
既然你不使用arg2,那当初为何要声明它呢?
通常是因为你实现某个函数以满足某些API固有的署名需要,例如,MFC的OnSize处理例程的署名必须要像下面这样:
voidOnSize(UINTnType,intcx,intcy);
这里cx/cy是窗口新的宽/高,nType是一个类似SIZE_MAXIMIZED或SIZE_RESTORED这样的编码,表示窗口是否最大化或是常规大小。
一般你不会在意nType,只会关注cx和xy。
所以如果你想用/W4,则必须使用UNREFERENCED_PARAMETER(nType)。
OnSize只是上千个MFC和Windows函数之一。
编写一个基于Windows的程序,几乎不可能不碰到未引用参数。
说了这么多关于UNREFERENCED_PARAMETER内容。
Judy在她的问题中还提到了另一个C++程序员常用的并且其作用与UNREFERENCED_PARAMETER相同的诀窍,那就是注释函数署名中的参数名:
voidCMyWnd:
:
OnSize(UINT/*nType*/,
intcx,intcy)
{
}
现在nType是未命名参数,其效果就像你敲入OnSize(UINT,intcx,intcy)一样。
那么现在的关键问题是:
你应该使用哪种方法——未命名参数,还是UNREFERENCED_PARAMETER?
大多数情况下,两者没什么区别,使用哪一个纯粹是风格问题。
(你喜欢你的java咖啡是黑色还是奶油的颜色?
)但我认为至少有一种情况必须使用UNREFERENCED_PARAMETER。
假设你决定窗口不允许最大化。
那么你便禁用Maximize按钮,从系统菜单中删除,同时阻止每一个用户能够最大化窗口的操作。
因为你是偏执狂(大多数好的程序员都是偏执狂),你添加一个ASSERT(断言)以确保代码按照你的意图运行:
voidCMyWnd:
:
OnSize(UINTnType,intcx,intcy)
{
ASSERT(nType!
=SIZE_MAXIMIZE);
...//usecx,cy
}
质检团队竭尽所能以各种方式运行你的程序,ASSERT从没有弹出过,于是你认为编译生成Release版本是安全的。
但是此时_DEBUG定义没有了,ASSERT(nType!
=SIZE_MAXIMIZE)展开为((void)0),并且nType一下子成了一个未引用参数!
这样进入你干净的编译。
你无法注释掉参数表中的nType,因为你要在ASSERT中使用它。
于是在这种情况下——你唯一使用参数的地方是在ASSERT中或其它_DEBUG条件代码中——只有UNREFERENCED_PARAMETER会保持编译器在Debug和Release生成模式下都没有问题。
知道了吗?
结束讨论之前,我想还有一个问题我没有提及,就是你可以象下面这样用pragma指令抑制单一的编译器警告:
#pragmawarning(disable:
4100)
4100是未引用参数的出错代码。
pragma抑制其余文件/模块的该警告。
用下面方法可以重新启用这个警告:
#pragmawarning(default:
4100)
不管怎样,较好的方法是在禁用特定的警告之前保存所有的警告状态,然后,等你做完之后再回到以前的配置。
那样,你便回到的以前的状态,这个状态不一定是编译器的默认状态。
所以你能象下面这样在代码的前后用pragma指令抑制单个函数的未引用参数警告:
#pragmawarning(push)
#pragmawarning(disable:
4100)
voidSomeFunction(...)
{
}
#pragmawarning(pop)
当然,对于未引用参数而言,这种方法未免冗长,但对于其它类型的警告来说可能就不是这样了。
库生成者都是用#pragmawarning来阻塞警告,这样他们的代码可以用/W4进行清洁编译。
MFC中充满了这样的pragmas指令。
还有好多的#pragmawarning选项我没有在本文讨论。
有关它们的信息请参考相关文档。
我注意到一些应用程序,当右键单击其任务栏最小化按钮时,在弹出的上下文菜单中具备特殊的命令。
例如,WinAmp(一个流行的媒体播放器)有一个附加的“WinAmp”菜单项,其中是WinAmp特有的命令。
我如何在程序的任务栏按钮中添加我自己的菜单项?
JirairOsygian
我创建了一个简单的MFCSDI程序,该程序用表单视图(FormView)显示一个计数器。
我想通过右键单击任务栏上程序的最小化按钮来控制启动/停止这个计数器。
在表单视图上通过按钮控制的启动/停止功能运行正常,我也能将启动/停止命令加到系统菜单。
但我单击加入的系统菜单时没反应。
我如何处理这些定制的系统菜单消息?
MonicqueSharman
我两个问题一起回答,Jirair问题的答案很简单:
当你右键单击任务栏上应用程序的最小化按钮时,用户看到的菜单与用户单击左上角应用程序标题栏图标或按Alt+Space所看到的菜单一样。
如Figure1所示。
这个菜单被称为系统菜单,其中包括命令如:
还原、最小化、最大化和关闭等。
Figure1系统菜单
你可以调用:
:
GetSystemMenu来获得此系统菜单,然后可以添加、删除或修改菜单项。
你甚至可以通过关掉WS_SYSMENU窗口创建式样标志或PreCreateWindow虚函数来完全屏蔽掉这个系统菜单。
但不论你做什么,当用户右键单击任务栏上应用程序的最小化按钮时,这个系统菜单还是会显示出来的。
到了Monicque的问题:
如果在系统菜单中添加自己的命令,MFC是如何处理它们的呢?
如果按常规来做——在某个地方写一个ON_COMMAND处理器并将它加到消息映射中,你会发现你的处理器不起作用,怎么会这样呢?
那是因为Windows和MFC处理系统命令的方式与处理普通菜单命令的方式不一样。
当用户调用窗体中的常规菜单命令或按钮时,Windows向主窗口发送一个WM_COMMAND消息。
如果你使用MFC,那么其命令路由机制将捕获此消息并通过操作系统将它路由到该命令的ON_COMMAND命令处理器对象。
(有关MFC命令处理机制的详细内容,参见我在MSJ1995年7月发表的文章:
“MeanderingThroughtheMazeofMFCMessageandCommandRouting”)。
然而系统命令不属于WM_COMMAND消息范围。
而属于另外一个叫做——WM_SYSCOMMAND的消息。
不论命令ID是真正的系统命令如SC_MINIMIZE和SC_CLOSE,还是你自己添加的其它命令ID都是如此。
为了处理系统菜单命令,你必须处理显式处理WM_SYSCOMMAND并且要选择你自己的命令IDs。
这就需要你在主窗口消息映射中添加ON_WM_SYSCOMMAND,它有一个处理函数如下:
CMainFrame:
:
OnSysCommand(UINTnID,LPARAMlp)
{
if(nID==ID_MY_COMMAND){
...//处理它
return0;
}
//传递到基类:
这一步很重要!
returnCFrameWnd:
:
OnSysCommand(nID,lp);
}
如果该命令不是你的,不要忘了将它传递到你的基类处理——典型地,那就是CFrameWnd或CMDIFrameWnd。
否则,Windows将无法得到此消息,并且会破坏内建的命令。
在主框架中处理WM_SYSCOMMAND固然可以,但这样做感觉太业余。
为什么要用特殊的机制来处理呢?
就因为它们是系统菜单吗?
如果你想在视图或文档对象中处理系统命令会怎样呢?
有一个常见的命令放到了系统菜单中,它就是“关于”(ID_APP_ABOUT),大多数MFC程序都是在应用程序对象中处理ID_APP_ABOUT:
voidCMyApp:
:
OnAppAbout()
{
staticCAboutDialogdlg;
dlg.DoModal();
}
MFC一个真正很酷的特性是它的命令路由系统,它使得象CMyApp这样的非窗口对象也能处理菜单命令。
许多程序员甚至都不了解怎么会有这样的例外。
如果你已经在应用程序对象中处理ID_APP_ABOUT,那把ID_APP_ABOUT添加到系统菜单后,为什么还要去实现一套单独的机制?
处理外加系统命令的比较好的,或者说更MFC的方法应该是通过常规的命令路由机制传递它们。
然后按MFC常规方法编写ON_COMMAND处理例程来处理系统命令。
你甚至可以用ON_UPDATE_COMMAND_UI来更新你的系统菜单项,例如禁用某个菜单项或在菜单项旁边显示一个检讫标志。
Figure2 是我写的一个类,CSysCmdRouter,这个类将系统命令转成常规命令。
为了使用这个类,你要做的只是在主框架中实例化CSysCmdRouter,并从OnCreate中调用其Init方法即可:
intCMainFrame:
:
OnCreate(...)
{
//将我的菜单项添加到系统菜单
CMenu*pMenu=GetSystemMenu(FALSE);
pMenu->AppendMenu(..ID_MYCMD1..);
pMenu->AppendMenu(..ID_MYCMD2..);
//通过MFC路由系统命令
m_sysCmdHook.Init(this);
return0;
}
一旦你调用CSysCmdRouter:
:
Init,你便可以按常规方式处理ID_MYCMD1和ID_MYCMD2,为MFC命令路由机制中的任何对象编写ON_COMMAND处理例程——视图,文档,框架,应用程序或通过改写OnCmdMsg添加的任何其它命令对象。
CSysCmdRouter还让你用ON_UPDATE_COMMAND_UI处理器更新系统菜单。
唯一要注意的是确保命令IDs不要与其它菜单命令(除非他们确实代表相同的命令)或内建系统命令发生冲突,内建系统命令从SC_SIZE=0xF000开始。
VisualStudio.NET指定的命令IDs从0x8000=32768开始,所以如果你让VisualStudio来指定IDs,只要不超过0xF000-0x8000=0x7000个命令即可。
也就是十进制的28,762。
如果你的应用程序有超过28000个命令,那么你需要咨询编程精神病专家。
CSysCmdRouter是如何实现其魔法的呢?
简单:
它使用我那个以前专栏中无处不在的CSubclassWnd。
CSubclassWnd使你不用从其派生便能子类化MFC窗口对象。
CSysCmdRouter派生自CSubclassWnd并使用它子类化主框架。
尤其是它截获发送到框架的WM_SYSCOMMAND消息。
如果命令ID属于系统命令(大于SC_SIZE=0xF000),则CSysCmdRouter沿着Windows一路传递该消息;否则便吃掉WM_SYSCOMMAND并重新将它作为WM_COMMAND发送,于是MFC按照其常规路由过程,调用你的ON_COMMAND处理器。
很聪明,是不是?
那么ON_UPDATE_COMMAND_UI处理器呢?
CSysCmdRouter是如何让它处理系统菜单命令的呢?
很简单。
就在Windows显示菜单前,他向你的主窗口发送一个WM_INITMENUPOPUP消息。
这是你更新菜单项的最佳时机——启用或禁用它们,添加检讫标志等等。
MFC为每个菜单项创建一个CCmdUI对象并将它传递到你的消息映射中相应的ON_UPDATE_COMMAND_UI处理器。
以它为参数的MFC函数是CFrameWnd:
:
OnInitMenuPopup,这个函数是这样的:
voidCFrameWnd:
:
OnInitMenuPopup(CMenu*pMenu,UINTnIndex,BOOLbSysMenu)
{
if(bSysMenu)
return;//don''tsupportsystemmenu
...
}
MFC初始化系统菜单时不做任何事情。
为什么要去关心这种事呢?
万一你要让bSysMenu为FALSE,即使是系统菜单,那该怎么办?
这恰恰是CSysCmdRouter做的事情。
它截取WM_INITMENUPOPUP并清除bSysMenu标志,也就是LPARAM的HIWORD:
if(msg==WM_INITMENUPOPUP){
lp=LOWORD(lp);//(setHIWORD=0)
}
现在,当MFC获得WM_INITMENUPOPUP,它认为该菜单是常规菜单。
只要你的命令IDs与真正的系统菜单不冲突,一切都运行得很好。
如果你改写OnInitMenuPopup,唯一丢失的东西是不能从主窗口菜单中区分系统菜单。
嘿,你不能什么都想要!
通过改写CWnd:
:
WindowProc,你总是能处理WM_INITMENUPOPUP的,或你想要区分,就比较HMENUs。
但你确实不用关心命令来自何处。
Figure3任务栏菜单
为了展示所有的实践,我写了一个小测试程序:
TBMenu。
如图Figure3所示,当你右键单击任务栏上TBMenu的最小化按钮,便会显示出菜单。
你可以看到在菜单底部有两个额外的命令。
TBMenu的CMainFrame代码如 Figure4 所示。
便知道在OnCreate的什么地方添加命令并在CMainFrame的消息映射中用ON_COMMAND以及ON_UPDATE_COMMAND_UI处理器处理它们。
TBMenu在其应用程序类中处理ID_APP_ABOUT(代码未列出)。
CSysCmdRouter使系统命令的工作机制类似其它命令。
说到命令,我们来看看一个小资料:
在我一月份的专栏中,我问是否有人知道Ctrl+Alt+Del的由来。
显然,有几个读者知道如何使用Google,因为他们发给我的是相同的链接:
《今日美国》上的一篇文章:
“Thankthisguyfor'control-alt-delete'”,我在一月发问之前就发现了这篇文章。
Ctrl+Alt+Del是由一个名叫DavidJ.Bradley的人发现的,他在IBM工作过。
IBM觉得应该有一种方法不用关闭电源就能重置(reset)其新的PC机。
为什么要专门用Ctrl+Alt+Del这三个键呢?
从技术上来说,David需要使用两个修饰键。
他想要一种没有人可能意外敲入的键组合。
所以他选择了
Ctrl+Alt作为修饰键(比Shift用得少)和Delete,此键位于键盘的另一端,所以敲击Ctrl+Alt+Del需要两只手,至少在过去是这样做的。
当今现代键盘在右边也有Ctrl和Alt键。
重启特性的初衷是为IBM的人设计的秘密安全出口,但不可避免地,它已经成为一个不是秘密的秘密。
一旦开发人员知道了它,他们便开始告诉客户使用这个特性来解决机器挂起问题。
随着历史的发展,Ctrl+Alt+Del被人们亲切地成为“三指敬礼”,即便是在当今的Windows中仍然具有生命力,用它调出任务管理器,以便你能杀死挂起的任务或终止系统(更多有关类似Ctrl+Alt+Del安全键序列的内容,参见本月的 SecurityBriefs 专栏)。
那么,如果Ctrl+Alt+Del失败了怎么办?
为什么会失败,请按住它保持5秒钟。
DavidBradley是当初建立IBM个人计算机的12个工程师之一。
他编写了ROMBIOS,有关David的简介,参见 DavidJ.Bradley。
祝编程愉快!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- UNREFERENCEDPARAMETER 用处
![提示](https://static.bdocx.com/images/bang_tan.gif)