??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲国产婷婷香蕉久久久久久,国内精品久久久久影院薰衣草,欧美久久亚洲精品http://www.shnenglu.com/zhaoyg/category/7254.html记录Ҏ(gu)Q成未?/description>zh-cnWed, 02 May 2012 23:31:02 GMTWed, 02 May 2012 23:31:02 GMT60我的无知与C++的变?/title><link>http://www.shnenglu.com/zhaoyg/archive/2012/05/03/173536.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 02 May 2012 16:12:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2012/05/03/173536.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/173536.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2012/05/03/173536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/173536.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/173536.html</trackback:ping><description><![CDATA[<span style="font-size: 12pt;">此刻我本已在床上睡觉了,但就在我睡前ȝ《深度探索C++对象模型》时我看C一D内容,也就是这D内容得我写下了这笔记?/span><br /><br /><span style="font-size: 12pt;">q段内容大致讲的是关于模板中的命名决议:“scope of the template definition”?#8220;scopy of the template instantition”</span><span style="font-size: 12pt;">?/span><br /><br /><span style="font-size: 12pt;">坦白的说我是W一ơ看到这一知识点,在上机验证后又回惟뀊C++ Primer?4th中可曾有提及q,后来发现4th中没有,倒是3rd中有专门的一节专门陈述了下?br /><br />后来我在《深度探索C++对象模型》书中关于模板中命名册内容的旁边写下了q样一D话Q?br /><br /><em>             今天才发现还有这么个东西。我只能承认自己的无知与C++的变态,面对q一知识炏V?br />                                                                                     2012.5.2 ?br /><br /><br /><br /></em></span><img src ="http://www.shnenglu.com/zhaoyg/aggbug/173536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2012-05-03 00:12 <a href="http://www.shnenglu.com/zhaoyg/archive/2012/05/03/173536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】C++11中值得x的几大变?/title><link>http://www.shnenglu.com/zhaoyg/archive/2011/08/19/153882.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Fri, 19 Aug 2011 13:06:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2011/08/19/153882.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/153882.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2011/08/19/153882.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/153882.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/153882.html</trackback:ping><description><![CDATA[<div><div><div> <p>赖勇(http://laiyonghao.comQ?br /> 声明Q本文源?Danny Kalev ?2011 q?6 ?21 日发表的《The Biggest Changes in C++11(and Why You Should Care)》一文,几乎所有内定w搬了q来Q但不是全文照译Q有困惑之处Q请参详原文Q?a >http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/</a> Q?br /> 注:作?Danny Kalev 曾是 C++ 标准委员会成员?/p> <h2>Lambda 表达?/h2> <p>Lambda 表达式的形式是这L(fng)Q?br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>[capture](parameters)->return-type {body}  </span></li></ol></div> <br /> 来看个计数某个字W序列中有几个大写字母的例子Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>int main()  </span></li><li>{  </li><li>   <span>char s[]="Hello World!";  </span></li><li>   <span>int Uppercase = 0; //modified by the lambda  </span></li><li>   for_each(s, s+<span>sizeof(s), [&Uppercase] (char c) {  </span></li><li>    <span>if (isupper(c))  </span></li><li>     Uppercase++;  </li><li>    });  </li><li> cout<< Uppercase<<<span>" uppercase letters in: "<< s<<endl;  </span></li><li>}  </li></ol></div> <br /> 其中 [&Uppercase] 中的 & 的意义是 lambda 函数体要获取一?Uppercase 引用Q以便能够改变它的|如果没有 &Q那?Uppercase 以传值的形式传递过厅R?<h2>自动cd推导?decltype</h2> <p>?C++03 中,声明对象的同时必L明其cdQ其实大多数情况下,声明对象的同时也会包括一个初始|C++11 在这U情况下p够让你声明对象时不再指定cd了:<br /> </p><div bg_c-sharp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>auto x=0; //0 ?nbsp;int cdQ所?nbsp;x 也是 int cd  </span></li><li>auto c=<span>'a'; //char  </span></li><li>auto d=0.5; <span>//double  </span></li><li>auto national_debt=14400000000000LL;<span>//long long  </span></li></ol></div> <br /> q个Ҏ(gu)在对象的类型很大很长的时候很有用Q如Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>void func(const vector<int> &vi)  </span></li><li>{  </li><li>  vector<<span>int>::const_iterator ci=vi.begin();  </span></li><li>}  </li></ol></div> <br /> 那个q代器可以声明ؓQ?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>auto ci=vi.begin();  </span></li></ol></div> <br /> C++11 也提供了从对象或表达式中“俘获”cd的机Ӟ新的操作W?decltype 可以从一个表辑ּ?#8220;俘获”其结果的cdq?#8220;q回”Q?br /> <div bg_c-sharp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>const vector<int> vi;  </span></li><li>typedef decltype (vi.begin()) CIT;  </li><li>CIT another_const_iterator;  </li></ol></div> <h2>l一的初始化语法</h2> <p>C++ 最有 4 U不同的初始化Ş式,如括号内初始化,见:<br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>std::string s("hello");  </span></li><li><span>int m=int(); //default initialization  </span></li></ol></div> <br /> q有{号形式的:<br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>std::string s="hello";  </span></li><li><span>int x=5;  </span></li></ol></div> <br /> 对于 POD 集合Q又可以用大括号Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>int arr[4]={0,1,2,3};  </span></li><li><span>struct tm today={0};  </span></li></ol></div> <br /> 最后还有构造函数的成员初始化:<br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>struct S {  </span></li><li> <span>int x;  </span></li><li> S(): x(0) {} };  </li></ol></div> <br /> q么多初始化形式Q不仅菜鸟会搞得很头大,高手也吃不消。更惨的?C++03 中居然不能初始化 POD 数组的类成员Q也不能在?new[] 的时候初?POD 数组Q操蛋啊QC++11 q大括号一l天下了Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>class C  </span></li><li>{  </li><li><span>int a;  </span></li><li><span>int b;  </span></li><li><span>public:  </span></li><li> C(<span>int i, int j);  </span></li><li>};  </li><li>C c {0,0}; <span>//C++11 only. 相当?nbsp;C c(0,0);  </span></li><li><span>int* a = new int[3] { 1, 2, 0 }; /C++11 only  </span></li><li><span>class X {  </span></li><li>  <span>int a[4];  </span></li><li><span>public:  </span></li><li>  X() : a{1,2,3,4} {} <span>//C++11, 初始化数l成?nbsp; </span></li><li>};  </li></ol></div> <br /> q有一大好事就是对于容器来_l于可以摆脱 push_back() 调用了,C++11中可以直观地初始化容器了Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>// C++11 container initializer  </span></li><li>vector vs<string>={ <span>"first", "second", "third"};  </span></li><li>map singers =  </li><li>  { {<span>"Lady Gaga", "+1 (212) 555-7890"},  </span></li><li>    {<span>"Beyonce Knowles", "+1 (212) 555-0987"}};  </span></li></ol></div> <br /> 而类中的数据成员初始化也得到了支持:<br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>class C  </span></li><li>{  </li><li> <span>int a=7; //C++11 only  </span></li><li><span>public:  </span></li><li> C();  </li><li>};  </li></ol></div> <h2>deleted 函数?defaulted 函数</h2> <p>像以下Ş式的函数Q?br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>struct A  </span></li><li>{  </li><li> A()=<span>default; //C++11  </span></li><li> <span>virtual ~A()=default; //C++11  </span></li><li>};  </li></ol></div> <br /> 叫做 defaulted 函数Q?default; 指示~译器生成该函数的默认实现。这有两个好处:一是让E序员轻松了Q少敲键盘,二是有更好的性能?br /> ?defaulted 函数相对的就?deleted 函数Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>int func()=delete;  </span></li></ol></div> <br /> q货有一大用途就是实?noncopyabe 防止对象拯Q要想禁止拷贝,?=deleted 声明一下两个关键的成员函数可以了Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>struct NoCopy  </span></li><li>{  </li><li>    NoCopy & operator =( <span>const NoCopy & ) = delete;  </span></li><li>    NoCopy ( <span>const NoCopy & ) = delete;  </span></li><li>};  </li><li>NoCopy a;  </li><li>NoCopy b(a); <span>//~译错误Q拷贝构造函数是 deleted 函数  </span></li></ol></div> <h2>nullptr</h2> <p>nullptr 是一个新?C++ 关键字,它是I指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强cd的:<br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>void f(int); //#1  </span></li><li><span>void f(char *);//#2  </span></li><li><span>//C++03  </span></li><li>f(0); <span>//调用的是哪个 f?  </span></li><li><span>//C++11  </span></li><li>f(nullptr) <span>//毫无疑问Q调用的?nbsp;#2  </span></li></ol></div> <br /> 所有跟指针有关的地斚w可以?nullptrQ包括函数指针和成员指针Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>const char *pc=str.c_str(); //data pointers  </span></li><li><span>if (pc!=nullptr)  </span></li><li>  cout<<pc<<endl;  </li><li><span>int (A::*pmf)()=nullptr; //指向成员函数的指?nbsp; </span></li><li><span>void (*pmf)()=nullptr; //指向函数的指?nbsp; </span></li></ol></div> <h2>委托构造函?/h2> <p>C++11 中构造函数可以调用同一个类的另一个构造函敎ͼ<br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>class M //C++11 delegating constructors  </span></li><li>{  </li><li> <span>int x, y;  </span></li><li> <span>char *p;  </span></li><li><span>public:  </span></li><li> M(<span>int v) : x(v), y(0),  p(new char [MAX])  {} //#1 target  </span></li><li> M(): M(0) {cout<<<span>"delegating ctor"<<end;} //#2 delegating  </span></li></ol></div> <br /> #2 是所谓的委托构造函敎ͼ调用了真正的构造函?#1?<h2>叛_引?/h2> <p>?C++03 中的引用cd是只l定左值的QC++11 引用一个新的引用类型叫叛_引用类型,它是l定到右值的Q如临时对象或字面量?br /> 增加叛_引用的主要原因是ؓ了实?move 语义。与传统的拷贝不同,move 的意思是目标对象“H取”原对象的资源Qƈ源|于“I?#8221;状态。当拯一个对象时Q其实代h贵且无必要,move 操作可以替代它。如? string 交换的时候,使用 move 意义有巨大的性能提升Q如原方案是q样的:<br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>void naiveswap(string &a, string & b)  </span></li><li>{  </li><li> string temp = a;  </li><li> a=b;  </li><li> b=temp;  </li><li>}  </li></ol></div> <br /> q种Ҏ(gu)很傻很天真,很慢Q因为需要申请内存,然后拯字符Q?move 只需要交换两个数据成员,无须甌、释攑ֆ存和拯字符数组Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>void moveswapstr(string& empty, string & filled)  </span></li><li>{  </li><li><span>//pseudo code, but you get the idea  </span></li><li> <span>size_t sz=empty.size();  </span></li><li> <span>const char *p= empty.data();  </span></li><li><span>//move filled's resources to empty  </span></li><li> empty.setsize(filled.size());  </li><li> empty.setdata(filled.data());  </li><li><span>//filled becomes empty  </span></li><li> filled.setsize(sz);  </li><li> filled.setdata(p);  </li><li>}  </li></ol></div> <br /> 要实现支?move 的类Q需要声?move 构造函数和 move 赋值操作符Q如下:<br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>class Movable  </span></li><li>{  </li><li>Movable (Movable&&); <span>//move constructor  </span></li><li>Movable&& operator=(Movable&&); <span>//move assignment operator  </span></li><li>};  </li></ol></div> <br /> C++11 的标准库q泛使用 move 语义Q很多算法和容器都已l?move 语义优化q了?<h2>C++11 的标准库</h2> <p>?TR1 包含的新容器Qunordered_set, unordered_map, unordered_multiset, 和unordered_multimapQ,q有一些新的库Q如正则表达式,tupleQ函数对象封装器{。下面介l一?C++11 的标准库新特性:</p> <h3>U程?/h3> <p>从程序员的角度来看,C++11 最重要的特性就是ƈ发了。C++11 提供?thread c,也提供了 promise ?future 用以q发环境中的同步Q用 async() 函数模板执行q发dQ和 thread_local 存储声明为特定线E独占的数据Q这里(http://www.devx.com/SpecialReports/Article/38883Q有一个简??C++11 U程库教E(英文Q?/p> <h3>新的指针c?/h3> <p>C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr ?unique_ptr。它们都是标准库的其它组件兼容,可以安全地把指针存入标准容器Q也可以安全地用标准法“倒腾”它们?/p> <h3>新的法</h3> <p>主要?all_of()、any_of() ?none_of()Q下面是例子Q?br /> </p><div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>#include <algorithm>  </span></li><li><span>//C++11 code  </span></li><li><span>//are all of the elements positive?  </span></li><li>all_of(first, first+n, ispositive()); <span>//false  </span></li><li><span>//is there at least one positive element?  </span></li><li>any_of(first, first+n, ispositive());<span>//true  </span></li><li><span>// are none of the elements positive?  </span></li><li>none_of(first, first+n, ispositive()); <span>//false  </span></li></ol></div> <br /> q有一个新?copy_nQ?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>#include <algorithm>  </span></li><li><span>int source[5]={0,12,34,50,80};  </span></li><li><span>int target[5];  </span></li><li><span>//?nbsp;source 拯 5 个元素到 target  </span></li><li>copy_n(source,5,target);  </li></ol></div> <br /> iota() 法可以用来创徏递增序列Q它先把初D值给 *firstQ然后用前置 ++ 操作W增长初值ƈ赋值到l下一个P代器指向的元素,如下Q?br /> <div bg_cpp=""><div><div><a title="view plain">view plain</a></div></div><ol start="1"><li><span>#include <numeric>  </span></li><li><span>int a[5]={0};  </span></li><li><span>char c[3]={0};  </span></li><li>iota(a, a+5, 10); <span>//changes a to {10,11,12,13,14}  </span></li><li>iota(c, c+3, <span>'a'); //{'a','b','c'}  </span></li></ol></div> <br /> 是的QC++11 仍然~少一些很有用的库?XML APIQsocketQGUI、反?#8212;—以及自动垃圾攉。然而现有特性已l让 C++ 更安全、高效(是的Q效率更高了Q可以参?Google ? 基准试l果http://www.itproportal.com/2011/06/07/googles-rates-c-most- complex-highest-performing-language/Q以及更加易于学?fn)和使用?br /> 如果觉得 C++ 变化太大了,不必惊恐Q花Ҏ(gu)间来学习(fn)好了。可能在你融会诏通新Ҏ(gu)以后,你会同意 Stroustrup 的观点:C++11 是一门新的语a——一个更好的 C++?p><br /></p> </div></div></div><img src ="http://www.shnenglu.com/zhaoyg/aggbug/153882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2011-08-19 21:06 <a href="http://www.shnenglu.com/zhaoyg/archive/2011/08/19/153882.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】B(ti)OOST的编译选项http://www.shnenglu.com/zhaoyg/archive/2011/02/16/140148.htmlzhaoygzhaoygWed, 16 Feb 2011 03:35:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2011/02/16/140148.htmlhttp://www.shnenglu.com/zhaoyg/comments/140148.htmlhttp://www.shnenglu.com/zhaoyg/archive/2011/02/16/140148.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/140148.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/140148.html原文Q?a href="http://www.shnenglu.com/flyinghare/archive/2010/09/07/126078.aspx">http://www.shnenglu.com/flyinghare/archive/2010/09/07/126078.aspx

许多Ch对于~译B(ti)OOST感到无从下手Q甚臛_此而放弃用BOOSTQ那真的太可惜了Q下面我把一些常用的BOOST~译Ҏ(gu)贴于此,同时也作qW记?
首先下蝲bjam.exeQ复制到 $BOOST$ 目录下。或?span style="FONT-SIZE: 14pt">自己生成bjamQ打开Visual Studio 2008 命o提示H口$BOOST$\tools\jam\srcQ执?build.bat 会在$BOOST$\tools\jam\src\bin.ntx86 生成 bjam.exe 文g。复制文?bjam.exe  文g?$BOOST$\?/span>?

1.完全~译安装Q?
bjam --toolset=msvc install
完成后会生成一个bin.v2~译时的临时目录Q手动删除。生成另一个目录C:\boostQ里面ؓ所有的头文件和库文件。头文g目录为boost_1_34_1\boost目录复制q去的?br> 
2.只编译相应的库文?
bjam --toolset=msvc stage
完成后同样会生成bin.v2临时目录。另一个目录ؓstage文gQ里面有对应的库文g?

3.查看需要编译才能用的库列?
bjam --show-libraries

4.~译特定的库Q如只编译regex
bjam --toolset=msvc --with-regex stage
生成的库文g在stage目录中?

5.不编译某个库Q如不编译regex
bjam --toolset=msvc --without-regex stage
生成的库文g在stage目录中?

6.~译特定的库Q如只编译regexQ生成debugQ多U程Q共享连接版本,q保存在stage?
bjam --toolset=msvc --with-regex stage debug threading=multi link=shared

7.生成 mt-sgd 的静态库(runtime-link-static)
bjam "-sTOOLS=vc-8_0" --with-thread install debug release runtime-link=static

8.~译regex库?
bjam --toolset=msvc --with-regex stage debug release threading=multi threading=single link=shared link=static runtime-link=shared runtime-link=static

boost的安装方法:
对于DLL版本
bjam --toolset=msvc link=shared runtime-link=shared threading=multi stage debug release install

对于lib版本
bjam --toolset=msvc link=static runtime-link=shared threading=multi stage debug release install


另外Q在$BOOST$\tools\build\v2\user-config.jam扑ֈ下面的地?
# -------------------
# MSVC configuration.
# -------------------
# Configure msvc (default version, searched for in standard locations and PATH).
# using msvc ;
# Configure specific msvc version (searched for in standard locations and PATH).
# using msvc : 8.0 ;
#在这里添?vs2008 的配|?
using msvc : 9.0 : : /wd4819 /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE  /D_SECURE_SCL=0 ;
#在这里添?vs2005 的配|?
using msvc : 8.0 : : <compileflags>/wd4819 <compileflags>/D_CRT_SECURE_NO_DEPRECATE <compileflags>/D_SCL_SECURE_NO_DEPRECATE <compileflags>/D_SECURE_SCL=0 ;   
然后q入 $BOOST$ 目录Q执行bjam.exe ~译命o
//下面的命令的各选项的说明:
//prefix    boost安装到的路径Q生成的头文件和库文仉会放到该路径中)?
//重定义以下变量(利用-s讄Q:
//VC80_ROOT   vc2005的安装\径,如果未将vc2005安装到默认位|,你必L定该V?
//TOOLS         使用的编译工Pvc2005对应的是vc-8_0
//PYTHON_ROOT   ython的安装目录,如果未将BOOST安装到默认位|,你必L定该V?
//BUILD         ~译l果选项Q默认会生成可能多的版本,如调试版Q发行版Q静态库Q动态库Q单U程Q多U程?


zhaoyg 2011-02-16 11:35 发表评论
]]>
【{】function/bind的救赎(上)http://www.shnenglu.com/zhaoyg/archive/2011/01/20/138983.htmlzhaoygzhaoygThu, 20 Jan 2011 14:24:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2011/01/20/138983.htmlhttp://www.shnenglu.com/zhaoyg/comments/138983.htmlhttp://www.shnenglu.com/zhaoyg/archive/2011/01/20/138983.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/138983.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/138983.html转自Q?a >http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx
作者:孟岩
--------------------------------------------------------------------------------------
q是那篇C++0X的正文。太长,先写上半部分发了?/p>

Function/bind可以是一个很单的话题Q因为它其实不过是一个泛型的函数指针。但是如果那么来谈,没意思了Q也犯不上写q篇东西。在我看来,q个事情要讲的话Q就应该讲透,讲到回调QcallbackQ、代理(delegateQ、信PsignalQ和消息传递(messagingQ的层面Q因为它实是太重要了。这个话题不但与面向对象的核心思想密切相关Q而且是面向对象两大流z之间交锋的中心。围l这个问题的思考和争论Q几乎把20q来所有主的~程q_和编E语a都搅q来了。所以,如果详尽铺陈Q这个话题直接可以写一本书?/p>

写书我当然没那个水^Q但q个题目实一直想动一动。然而这个主题实在太大,我实在没有精力把它完整的写下来;q个主题也很深,特别是涉及到q发环境有关的话题,我的理解q非常肤,总觉得我认识的很多高手都比我更有资格写这个话题。所以犹豫了很久Q要不要现在写,该怎么写。最后我觉得Q确实不能把一博客文章写成一?0q面向对象技术史讎ͼ所以决定保留大的架构,但是对其中具体的技术细节点Cؓ止。我不会去详l地列D代码Q分析对象的内存布局Q画C意图,但是会把最重要的结论和观点写下来,说得好听一Ҏ(gu)提纲挈领Q说的不好听是语焉不详。但无论如何Q我惌样一东西,一是谈谈我对这个事情的看法Q二?#8220;抛砖引玉”Q引来高手的xQ引出更深刻和完整的叙述?/p>

下面开始?/p>

0. E序设计有一个范式(paradigmQ问题。所谓范式,是l织E序的基本思想Q而这个基本思想Q反映了E序设计者对E序的一个基本的哲学观,也就是说Q他认ؓE序的本质是什么,他认Z个大的程序是׃么组成的。而这Q又跟他对于现实世界的看法有兟뀂显Ӟq样的看法不可能有很多种。编E作Z门行业,独立存在?0q了Q但是所出现的范式不q三U——过E范式、函数范式、对象范式。其中函数范式与现实世界差距比较大,在这里不讨论。而过E范式和对象范式可以视ؓ对程序本质的两种Ҏ(gu)不同的看法,而且能够分别在现实世界中扑ֈ相应的映?br>q程范式认ؓQ程序是׃个又一个过E经q顺序、选择和@环的l构l合而成。反映在现实世界Q过E范式体C力_分工之前“全能?#8221;的工作特点——所有的事情都能qԌ所有的资源都是我的Q只不过得具体的事情得一步步地来做?
对象范式则反映了力_分工之后的团队协作的工作特点——每个h各有所长,各司其职Q有各自的私有资源,工g和信息在Z之间彼此传递,最后完成工作。因此,对象范式也就形成了自己对E序的看法——程序是׃l对象组成,q些对象各有所能,通过消息传递实现协作?

对象范式与过E范式相比,有三个突出的优势Q第一Q由于实C逻辑上的分工Q降低了大规模程序的开发难度。第二,灉|性更好——若q对象在一P可以灉|l合Q可以以不同的方式协作,完成不同的Q务,也可以灵zȝ替换和升U。第三,对象范式更加适应囑Ş化、网l化、消息驱动的C计算环境?/p>

所以,较之于过E范式,对象范式Q或者说“面向对象”Q确实是更具优势的编E范式。最q看C些文章抨击面向对象,说面向对象是胡扯Q我认ؓ要具体分析。对面向对象的一部分批评Q是冲着L?#8220;面向对象”语言ȝQ这实是有道理的,我在下面也会谈到Q而且会骂得更狠。而另一个批评的声音Q主要而来自STL之父Alex StepanovQ他说的当然有他的道理,不过要知道该牛h是前苏联莫斯U国立罗蒙诺索夫大学数学pd士,你只要翻d苏联的大学数学教材就知道了,能够在莫大拿到数学博士的Q根本就是披着人皮的外星高{智慧。而我们编写地球上的程序,可能q是应该以地球h的观点ؓ丅R?/p>

1. 重复一遍对象范式的两个基本观念Q?br>E序是由对象l成的;
对象之间互相发送消息,协作完成dQ?

h意,q两个观念与后来我们熟知的面向对象三要素“装、ѝ多?#8221;Ҏ(gu)不在一个层面上Q倒是与再后来?#8220;lg、接?#8221;合?/p>

2. 世界上第一个面向对象语a是Simula-67Q第二个面向对象语言是Smalltalk-71。Smalltalk受到了Simula-67的启发,基本出发点相同,但也有重大的不同。先说相同之处,Simula和Smalltalk都秉承上q对象范式的两个基本观念Qؓ了方便对象的构造,也都引入了类、承等概念。也是_cR承这些机制是Z实现对象范式原则而构造出来的W二位的、工h的机制Q那么ؓ什么后来这些第二位的东西篡了主位,后面我会再来分析。而Simula和Smalltalk最重大的不同,是Simula用方法调用的方式向对象发送消息,而Smalltalk构造了更灵zd更纯_的消息发送机制?/p>

具体的说Q向一个Simula对象中发送消息,是调用q个对象的一个方法,或者称成员函数。那么你怎么知道能够在这个对象上调用q个成员函数呢?或者说Q你怎么知道能够向这个对象发送某个消息呢Q这p求你必须保q个对象h合适的cdQ也是_你得先知道哦q个对象是什么,才能向它发消息。而消息的实现方式被直接处理ؓ成员函数调用Q或虚函数调用?/p>

而Smalltalk在这一点上做了一个历史性的跨越Q它实现了一个与目标对象无关的消息发送机Ӟ不管那个对象是谁Q也不管它是不是能正的处理一个消息,作ؓ发送消息的对象来说Q可以毫无顾忌地抓住一个对象就发消息过厅R接到消息的对象Q要试理解q个消息Qƈ最后调用自qq程来处理消息。如果这个消息能被处理,那个对象自然会处理好Q如果不能被处理QSmalltalkpȝ会向消息的发送者回传一个doesNotUnderstand消息Q予以通知。对象不用关心消息是如何传递给另一个对象的Q传递过E被分离出来Q而不是像Simula那样明确地被以成员函数调用的方式实现Q,可以是在内存中复Ӟ也可以是q程间通讯。到了Smalltalk-80Ӟ消息传递甚臛_以跨网l?/p>

Z方便后面的讨论,不妨把源自Simula的消息机制称?#8220;静态消息机?#8221;Q把源自Smalltalk的消息机制称?#8220;动态消息机?#8221;?/p>

Simula与Smalltalk之间对于消息机制的不同选择Q主要是因ؓ两者于用途。前者是用于仿真E序开发,而后者用于图形界面环境构建,看上d自合情合理。然而,是q么一点简单的区别Q却造成了巨大的历史后果?/p>

3. C1980q代QC++出现了。Bjarne Stroustrup在博士期间深入研I过SimulaQ非常欣赏其思想Q于是就在C语言语法的基之上Q几乎把Simula的思想照搬q来QŞ成了最初的C++。C++问世以之初,主要用于解决规模E大的传l类型的~程问题Q迅速取得了巨大的成功,也证明了对象范式本n所h的威力?/p>

大约在同期,Brad CoxҎ(gu)Smalltalk的思想设计了Objective-CQ可是由于其语法怪异Q没有流行v来。只有Steve Jobsq种h宗学鉴赏力的世外高hQ把它奉为瑰宝,?988q连锅把Objective-C的团队和产品一口气C下来?/p>

4. 在同一时期QGUI成ؓ热门。虽然GUI的本质是对象范型的,但是当时Q?980q代中期Q的面向对象语言Q包括C++语言Q还q不成熟Q因此最初的GUIpȝ无一例外是用C和汇~语a开发的。或者说Q最初的GUI开发者硬是用抽象U别更低的语a构造了一个面向对象系l。熟(zhn)Win32 SDK开发的人,应该知道我在说什么?/p>

5. 当时很多Z为,如果C++更成熟些Q直接用C++来构造Windowspȝ会大大地Ҏ(gu)。也有h觉得Q尽Windowspȝ本n使用C写的Q但是其面向对象的本质与C++更契合,所以在其基上包装一个C++的GUI framework一定是轻而易举。可是一动手Z发玎ͼ完全不是那么回事。用C++开发Windows框架隑־要死。ؓ什么呢Q主要就是Windowspȝ中的消息机制实际上是动态的Q与C++的静态消息机制根本配合不C起去。在Windows里,你可以向M一个窗口发送消息,q个H口自己会在自己的wndproc里来处理q个消息Q如果它处理不了Q就交给default window/dialog procd理。而在C++里,你要向一个窗口发消息Q就得确保这个窗口能处理q个消息Q或者说Q具有合适的cd。这样一来的话,׃D一个错l复杂的H口cdơ结构,无法实现。而如果你要让所有的H口c都能处理所有可能的消息Q且不论q样在逻辑上就行不通(用户定义的消息怎么处理Q)Q单在实C׃可接受——ؓ一个小的不同得创造一个新的窗口类Q每一个小的H口c都要背上一个多达数N的v-tableQ而其中可?9%的项都是费Q不要说在当Ӟ是在今天,内存数量非常丰富的时候,如果每一个GUIE序都这么搞Q用户也吃不消?/p>

6. 实际上C++的静态消息机制还引v了更׃重的问题——扭曲了Z寚w向对象的理解。既然必要先知道对象的cdQ才能向对象发消息,那么“c?#8221;q个概念q别重要了Q而对象只不过是类q个模子里造出来的东西Q反而不重要。渐渐的Q?#8220;面向对象~程”变成?#8220;面向cȝE?#8221;Q?#8220;面向cȝE?#8221;变成?#8220;构造类l承?#8221;。放在眼前的鲜活的对象活动不重要了,反而是其背后的静态类型系l成为关键?#8220;装、?#8221;q些W二{的Ҏ(gu),喧宾ZQ俨然成了面向对象的要素。每个程序员g都要先成为领域专Ӟ然后成ؓ领域分类学专Ӟ然后构造一个完整的l承?wi),然后才能new出对象,让程序跑h。正是因个过E太漫长Q太困难Q再加上C++本n的复杂度很大,所以C++出现q么多年Q真正堪U经典的面向对象cd和框Ӟ几乎屈指可数。很多流行的库,比如MFC、iostreamQ都暴露Z问题。一般程序员总觉得是自己的水q不够,于是下更大功夫去l剑。殊不知Ҏ(gu)上是方向错了Q脱M对象范式的本质,企图用静态分cL来对现实世界建模Q去ȝ变化万千的动态世界。这么难的事Q你水^再高也很隑ց好?/p>

可以从一个具体的例子来理解这个道理,比如在一个GUIpȝ里,一?Push Button 的设计问题。事实上在一个实际的E序里,一?push button 到底“是不?#8221;一?buttonQ进而是不是一?window/widgetQƈ不重要,本质上我Ҏ(gu)不关心它是什么,它从属于哪一个类Q在l承?wi)里处于什么位|,只要那里有这么一个东西,我可以点它,点完了可以发生相应的效果Q就可以了。可是Simula –> C++ 所鼓励的面向对象设计风|非要上来想清楚Qa Push Button is-a Button, a Button is-a Command-Target Control, a Command-Target Control is-a Control, a Control is-a Window. 把这一圈都想透彻之后Q才?new 一?Push ButtonQ然后才能让它工作。这Ş而上学了Q这pd际了。所以很隑ց好。你看到 MFC 的类l承?wi),觉得设计者太牛了Q能把这些层ơ概念都x楚,自己的水q不够Q还得修点{实际上呢,q个设计是经q数不清的失败和q出来、砸出来的,MFC的前w?Afx 不是失败了吗?1995q还有一个叫?Taligent 的大目Q召集了包括 Eric Gamma 在内的一大堆牛hQ要用C++做一个一l天下的application frameworkQ最后也以惨败告l,q公叔R倒闭了,CEO车祸w亡Q牛Z(zhn)数遣散。附带说一下,q个Taligent目是ؓ了跟NextSTEP和Microsoft Cairo竞争Q前者用Objective-C~写Q后来发展ؓCocoaQ后者用传统的Win32 + COM作ؓ基础架构Q后来发展ؓWindows NT。而Objective-C和COMQ恰恰就在动态消息分z方面,与C++q然不同。后面还会谈到?/p>

客观地说Q?#8220;面向cȝ设计”q不是没有意义。来源于实践又高于实늚抽象和概念,往往能更有力地把握住现实世界的本质,比如MVC架构Q就是这L(fng)有力的抽象。但是这U抽象,应该是来源于长期最?jng)_늚ȝ和提高,而不是面寚w题时主要的解x\。过于强调这U抽象,无异于假定程序员各个都是哲学Ӟh对现实世界准而深ȝ抽象能力Q当然是不符合实际情늚。结果呢Q刚学习(fn)面向对象没几天的E序员,对眼前鲜zȝ对象世界视而不见,一个个都煞有介事地L哲学冥想Q企图越q现实世界,L象出其背后本质,当然败得很惨?/p>

其实C++问世之后不久Q这个问题就暴露出来了。第一个C++~译?Cfront 1.0 是单l承Q而到?Cfront 2.0Q加入了多ѝؓ什么?是因ؓ使用中h们发现逻辑上似乎完的静态单l承关系Q碰到复杂灵zȝ现实世界Q就破熾癑և——蝙蝠是鸟也是兽Q水上飞飞也能游Q它们该如何归类呢?本来q应该促使大家反思承这个机制本w,但是那个时候全世界陷入l承狂热Q于是就开始给l承打补丁,加入多承,q而加入虚l承Q。到了虚l承Q明gh一看便知,q只是一个语法补丁,是ؓ了逃避职责而制造的一块无用的遮羞布,它已l完全已l脱d践了——有谁在事前能够判断是否应该对基c进行虚l承呢?

C1990q代中期Q问题已l十分明显。UML中有一个对象活动图Q其描述的就是运行时对象之间怺传递消息的模型?994qRobert C. Martin在《Object-Oriented C++ Design Using Booch Method》中Q曾面向对象设计从对象活动图入手Q而不是从cd入手。?995q出版的l典作品《Design Patterns》中Q徏议优先考虑l合而不是承,q也是尽人皆知的事情。这些迹象表明,在那个时候,面向对象C֌里的思想领袖们,已经意识?#8220;面向cȝ设计”q不好用。只可惜他们的革命精还不够?/p>

7. 你可能要问,Java ?NET也是用承关pȝl类库,q进行设计的啊,怎么那么成功呢?q里有三点应该注意。第一QC++的难不仅仅在于其静态结构体p,q有很多源于语言设计上的包袱Q比如对C的兼容,比如没有垃圾攉机制Q比如对效率的强调,{等。一旦把q些包袱丢掉Q设计的隑ֺ实可以大大下降。第二,Java?NET的核心类库是在C++十几q成功和p|的经验教训基之上Q结合COM体系优点设计实现的,自然要好上一大块。事实上Q在Java?NET核心cd的设计中很多地方Q体现的是基于接口的设计Q和真正的基于对象的设计。有了这两个主角站台Q?#8220;面向cȝ设计”不能喧宾ZQ也能发挥一些好的作用。第三,如后文指出,Java?NET中分别对C++最大的问题——缺对象别的delegate机制做出了自q回应Q这大大I补了原来的问题?/p>

管如此QJavaq是沾染上了“面向c设?#8221;的癌症,基础cd里就有很多架床叠屋的设计Q而J2EE/Java EE当中Q这UŞ而上学的设计也很普遍Q所以也引发了好几次轻量化的q动。这斚w我ƈ不是太懂Q可能需要真正的Java高手出来现n说法。我对Java的看法以前就讲过——^台和语言核心非常好,但风气不好,崇尚华丽J复的设计,装牛逼的人太多?/p>

至于.NETQ我听陈榕介l过Q在设计.NET的时候,微Y内部对于是否允许l承爆发了非常激烈的争论。很多资深高人都强烈反对l承。至于最后引入承,很大E度上是营销需要压倒了技术理性。尽如此,׃有COM的基Q又实现了非常彻底的delegateQ所?.NET 的设计水q是很高的。它的主要问题不在这Q在于太急于求胜Q更新速度太快Q基不牢。当ӞҎ(gu)问题q是微Y没有能够在Web和Mobile领域里占到多大的优势Q也׃?NET没有用武之地?/p>

8. COM。COM的要义是QY件是由COM Componentsl成Qcomponents之间彼此通过接口怺通讯。这是否让你回想h文开所提出的对象范型的两个基本原则Q有的是,在COM的术语里Q?#8220;COM Component ” ?#8220;object ”通假Q这׃COM的心思昭然若揭了。Don Box在Essential COM里开就_COM是更好的C++Q事实上是告诉大家QŞ而上学的“面向c设?#8221;不好使,q是回到对象吧?/p>

用COM开发的时候,一个组?#8220;是什?#8221;不重要,它具有什么接口,也就是说Q能够对它发什么消息,才是重要的。你可以用IUnknown::QueryInterface问组件能对哪一l消息作出反应。向lg分派消息也不一定要被绑定在Ҏ(gu)调用上,如果实现?IDispatchQ还可以实现“自动?#8221;调用Q也是COM术语里的 AutomationQ而通过 列集QmashalQ,可以跨进E、跨|络向另一lg发送消息,通过 monikerQ可以在分布式系l里定位和发现组件。如果你q“对象——消?#8221;的观念去看COM的设计,׃意识刎ͼ整个COM体系是用规范如何做对象Q如何发消息的。或者更直白一点,COM是用C/C++是模拟Z个Smalltalk。而且COM的概念世界里没有l承Q就其纯z性而言Q比SmalltalkqSmalltalk。在对象泛型上,COM辑ֈ了一个高峎ͼ领先于那个时代,甚至于比它的lQ.NETq要U洁?/p>

COM的主要问题是它的学习(fn)隑ֺ和安全问题,而且Q它q于q求U洁性,完全攑ּ?#8220;面向c设?#8221; 的机Ӟ昑־有点q?/p>

9. 好像有点扯远了,其实q是在说正事。上面说到由于C++的静态消息机ӞD了Ş而上学的“面向cȝ设计”Q祸xI但实际上,C++是有一个补救机会的Q那是实现对象U别的delegate机制。学q?NET的hQ一听delegateq个词就知道是什么意思,但Java里没有对应机制。在C++的术语体p里Q所谓对象别delegateQ就是一个对象回调机制。通过delegateQ一个对象A可以把一个特定工作,比如处理用户的鼠标事Ӟ委托l另一个对象B的一个方法来完成。A不必知道B的名字,也不用知道它的类型,甚至都不需要知道B的存在,只要求B对象h一个签名正的Ҏ(gu)Q就可以通过delegate把工作交lB的这个方法来执行。在C语言里,q个机制是通过函数指针实现的,所以很自然的,在C++里,我们希望通过指向成员函数的指针来解决cM问题?/p>

然而就在这个问题上QC++让hD痛惜?/p>

zhaoyg 2011-01-20 22:24 发表评论
]]>
什么时候自动合成的赋值操作符不能?/title><link>http://www.shnenglu.com/zhaoyg/archive/2010/05/08/114852.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sat, 08 May 2010 08:21:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2010/05/08/114852.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/114852.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2010/05/08/114852.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/114852.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/114852.html</trackback:ping><description><![CDATA[前一D|间写了类gq样的一D代?br> <div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> A<br>{<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br>    </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"> ptr;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>    A():ptr(NULL)<br>    {<br>    }<br>};<br><br>A one;<br>A two;<br>two </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> one;</span></div> ~译时发玎ͼ~译器提C默认赋值操作符不能用,而当我去掉ptr成员的const关键字后Q便好了。当时很U闷q是怎么搞的?br>后来在好心h的帮助下Q才意识到ptr是个constQ所以默认的赋值操作符不能对ptrq行赋倹{?br><br>唉,当时知道是这个原因后真的是羞涩难当,自己明知道const不能被赋值是常识问题Q但当时却死zL能找出原因?br>而更要h命的是我之前?a href="http://www.shnenglu.com/zhaoyg/archive/2009/10/07/98041.html"><font color=#1a8bc8>{偶尔学习(fn)C++标准} ?[对象的复?- 复制构造与赋值操作符] </font></a>中就已经对赋值操作符做了一些笔讎ͼ但我竟然把一些内容给忘了Q也可能是压根就没注意)?br><br>现在重新p动合成的赋值操作符何时不能使用再单独做一ơ笔记?br><br>当类中拥有以下一U或几种情况Ӟ隐式定义的赋值操作符是不能用的Q?br>1.非静态的const成员变量<br>2.非静态的引用cd成员变量<br>3.非静态的cȝ型的成员变量的赋值操作符不可讉K<br>4.Q如果有Q基cM的赋值操作符不可讉K<br><br>注,对于拥有以上情况中的一U或多种的类Q标准中的用词是Qill-formed。我不知道ؓ什么要用ill-formedQ字面意思:不规范)Q但不管怎样Q对于ill-formedQ编译器的行为就是,无法~译通过?br><br>后记Q希望这ơ不要再忘记q些内容了? <img src ="http://www.shnenglu.com/zhaoyg/aggbug/114852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-05-08 16:21 <a href="http://www.shnenglu.com/zhaoyg/archive/2010/05/08/114852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>枚D的插?/title><link>http://www.shnenglu.com/zhaoyg/archive/2010/04/29/114004.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 29 Apr 2010 15:01:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2010/04/29/114004.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/114004.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2010/04/29/114004.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/114004.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/114004.html</trackback:ping><description><![CDATA[<p>今天Q我无意中将鼠标攑֜一个在cM定义的枚举成员后Q弹Z一个含有该成员原型的提CZ息,看到q个提示后我时愣住?..<br>如图Q?br><img border=0 src="http://www.shnenglu.com/images/cppblog_com/zhaoyg/enum.png"></p> 当时看到__unnamed_0000_1直接愣住了,哪来的__unnamed_0000_1Q?br>正当我想问别人的时候,H然惛_了答案,原来{案很简单。因为TET是enum的一个成员,既然是成员,那么必然有一个作用域了,而我定义的是匿名的枚举,所以编译器׃__unnamed_0000_1来命名了? <img src ="http://www.shnenglu.com/zhaoyg/aggbug/114004.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-04-29 23:01 <a href="http://www.shnenglu.com/zhaoyg/archive/2010/04/29/114004.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 再次U结于友元声明是否向外围引入名字http://www.shnenglu.com/zhaoyg/archive/2010/03/07/109091.htmlzhaoygzhaoygSun, 07 Mar 2010 02:49:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2010/03/07/109091.htmlhttp://www.shnenglu.com/zhaoyg/comments/109091.htmlhttp://www.shnenglu.com/zhaoyg/archive/2010/03/07/109091.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/109091.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/109091.html
3. A function first declared in a friend declaration hasexternal linkage (3.5). Otherwise, the function retains its previous linkage (7.1.1).

按照3中说的,下面代码是应该通过?
class X
{
    friend 
void f(); 
};

int main()
{
    f(); 
//因ؓcX中的友元函数f的声明有外部链接Q所以,f可见
}
void f() { /* ok to define friend function in the class body */ }

~译试Q?
VC2005/2010通过 Q而GCC 4.4.3报错Q无法找到名字f


5. A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8),the function name is unqualified, and the function has namespace scope.
[Example:
class M {
    friend void f() { } // definition of global f, a friend of M,
                        // not the definition of a member function
};
—end example]
Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).

按照5中说的,既然在类中定义友元函数fQ是"definition of global f"Q那么也可以理解ؓ下面的代码是可以通过~译?
class X
{
    friend 
void f(){ /* ok to define friend function in the class body */ }
};

int main()
{
    f(); 


然而VC2005/2010和GCC4.4.3都不能通过

TCPL中对友元有如下说明:
  “一个友元声明不会给外围的作用域引进一个名字”?
  “一个友元函数或者需要在某个外围作用域里昑ּ声明Q或者以它的cLpcL生的cMZ个参?13Q??Q否则就无法调用q个友元了。?

C++ primer有如下:
  “友元声明将已命名的cL非成员函数引入到外围作用域中。此外,友元函数可以在类的内部定义,该函数的作用域扩展到包围该类定义的作用域。”?
  “用友元引入的类名和函数Q定义或声明Q,可以像预先声明的一样用?
C++ primer中的例子Q?
class X {
        friend class Y;
        friend void f() { /* ok to define friend function in the class body */ }
};
class Z {
        Y *ymem; // ok: declaration for class Y introduced by friend in X
        void g() { return ::f(); } // ok: declaration of f introduced by X
};


标准中说的和C++ PRIMER一P而和TCPL不一栗?
至于~译器,GCC做的和TCPL说的一P而VC和标准说的部分一P对于在友元声明出定义函数处有出入Q?

个hȝ
两个~译器行Z同,所以尽量提前声明友元所声明的名?

以上只是个h的看法,如果有不对的地方Q?span style="COLOR: #ff0000">各位大大指正



zhaoyg 2010-03-07 10:49 发表评论
]]>
[转] 对象安全自杀的条?/title><link>http://www.shnenglu.com/zhaoyg/archive/2010/02/11/107718.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 11 Feb 2010 10:53:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2010/02/11/107718.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/107718.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2010/02/11/107718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/107718.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/107718.html</trackback:ping><description><![CDATA[<p>对象如果要安全的自杀Q得保证以下条gQ?nbsp;</p> <ul> <li>this对象是必ȝ new操作W分?而不是用new[]Q也不是用placement newQ也不是局部对象,也不是global对象)?</li> <li>delete this后,不能讉K该对象Q何的成员变量及虚函数(delete this回收的是数据Q这包括对象的数据成员以及vtableQ不包括函数代码)?nbsp;</li> <li>delete this后,不能再访问this指针。换句话_你不能去查它、将它和其他指针比较、和 NULL比较、打印它、{换它Q以及其它的M事情?</li> </ul> <img src ="http://www.shnenglu.com/zhaoyg/aggbug/107718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-02-11 18:53 <a href="http://www.shnenglu.com/zhaoyg/archive/2010/02/11/107718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对右值类的一点小认识http://www.shnenglu.com/zhaoyg/archive/2010/02/10/107129.htmlzhaoygzhaoygWed, 10 Feb 2010 13:35:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2010/02/10/107129.htmlhttp://www.shnenglu.com/zhaoyg/comments/107129.htmlhttp://www.shnenglu.com/zhaoyg/archive/2010/02/10/107129.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/107129.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/107129.htmlclass T{ /**/ };

T fun(){  
return T();  }

int main()
{
    fun() 
= T();
    
return 0;
}


当我看到上面q样的代码时Q我便认个代码无法编译通过的,因ؓ按我所知道的,函数fun所q回的是一个(f)时对象,而(f)时对象是不能被修改的Q然而fun()=T()语句便恰恰是在对临时对象q行修改。但是当我编译之后却?c)了,居然通过了,我很U闷?br>后来在网友的帮助下,在标准中扑ֈ了答案:

3.10.5 
The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.

13.5.7
The identities among certain predefined operators applied to basic types (for example, ++a ≡ a+=1) need not hold for operator functions. Some predefined operators, such as +=, require an operand to be an lvalue when applied to basic types; this is not required by operator functions.

3.10
An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]



也就是说Q在对内|类型进行赋值操作时Q将调用内置的赋值操作符Q而这U内|的要求左操作数必须是左|而当对类cd对象q行赋值时Q所调用的是重蝲的赋值操作符Q但重蝲的操作符q没有要求必M用左|也就是说Q赋值操作符的左操作数可以是叛_{?br>后来得知Q在C++中右值可以是一个对象,?#8220;对象”指的是“一D内存存贮区?#8221;Q但C中的叛_则不是一个对象,他只是一个倹{?br>
以上内容如有不对之处Q还望不惜指正?br>
对lvalue和rvalue的较详细介绍L(fng)文章<Lvalues and Rvalues>Q?a href="http://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.html">http://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.html



zhaoyg 2010-02-10 21:35 发表评论
]]>
[转] Lvalues and Rvalueshttp://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.htmlzhaoygzhaoygSat, 06 Feb 2010 14:41:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.htmlhttp://www.shnenglu.com/zhaoyg/comments/107405.htmlhttp://www.shnenglu.com/zhaoyg/archive/2010/02/06/107405.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/107405.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/107405.htmlC and C++ enforce subtle differences on the expressions to the left and right of the assignment operator

If you've been programming in either C or C++ for a while, it's likely that you've heard the terms lvalue (pronounced "ELL-value") and rvalue (pronounced "AR-value"), if only because they occasionally appear in compiler error messages. There's also a good chance that you have only a vague understanding of what they are. If so, it's not your fault.

Most books on C or C++ do not explain lvalues and rvalues very well. (I looked in a dozen books and couldn't find one explanation I liked.) This may be due to of the lack of a consistent definition even among the language standards. The 1999 C Standard defines lvalue differently from the 1989 C Standard, and each of those definitions is different from the one in the C++ Standard. And none of the standards is clear.

Given the disparity in the definitions for lvalue and rvalue among the language standards, I'm not prepared to offer precise definitions. However, I can explain the underlying concepts common to the standards.

As is often the case with discussions of esoteric language concepts, it's reasonable for you to ask why you should care. Admittedly, if you program only in C, you can get by without understanding what lvalues and rvalues really are. Many programmers do. But understanding lvalues and rvalues provides valuable insights into the behavior of built-in operators and the code compilers generate to execute those operators. If you program in C++, understanding the built-in operators is essential background for writing well-behaved overloaded operators.


Basic concepts

--------------------------------------------------------------------------------

Kernighan and Ritchie coined the term lvalue to distinguish certain expressions from others. In The C Programming Language (Prentice-Hall, 1988), they wrote "An object is a manipulatable region of storage; an lvalue is an expression referring to an object....The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression."

In other words, the left and right operands of an assignment expression are themselves expressions. For the assignment to be valid, the left operand must refer to an object-it must be an lvalue. The right operand can be any expression. It need not be an lvalue. For example:

int n;

declares n as an object of type int. When you use n in an assignment expression such as:

n = 3;

n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue.

Suppose you switch the left and right operands around:

3 = n;

Unless you're a former Fortran programmer, this is obviously a silly thing to do. The assignment is trying to change the value of an integer constant. Fortunately, C and C++ compilers reject it as an error. The basis for the rejection is that, although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. It doesn't refer to an object; it just represents a value.

I don't know where the term rvalue comes from. Neither edition of the C Standard uses it, other than in a footnote stating "What is sometimes called 'rvalue' is in this standard described as the 'value of an expression.'"

The C++ Standard does use the term rvalue, defining it indirectly with this sentence: "Every expression is either an lvalue or an rvalue." So an rvalue is any expression that is not an lvalue.

Numeric literals, such as 3 and 3.14159, are rvalues. So are character literals, such as 'a'. An identifier that refers to an object is an lvalue, but an identifier that names an enumeration constant is an rvalue. For example:

enum color { red, green, blue };
color c;
...
c = green;    // ok
blue = green;    // error

The second assignment is an error because blue is an rvalue.

Although you can't use an rvalue as an lvalue, you can use an lvalue as an rvalue. For example, given:

int m, n;

you can assign the value in n to the object designated by m using:

m = n;

This assignment uses the lvalue expression n as an rvalue. Strictly speaking, a compiler performs what the C++ Standard calls an lvalue-to-rvalue conversion to obtain the value stored in the object to which n refers.


Lvalues in other expressions
-------------------------------------------------------------------------------

Although lvalues and rvalues got their names from their roles in assignment expressions, the concepts apply in all expressions, even those involving other built-in operators.

For example, both operands of the built-in binary operator + must be expressions. Obviously, those expressions must have suitable types. After conversions, both expressions must have the same arithmetic type, or one expression must have a pointer type and the other must have an integer type. But either operand can be either an lvalue or an rvalue. Thus, both x + 2 and 2 + x are valid expressions.

Although the operands of a binary + operator may be lvalues, the result is always an rvalue. For example, given integer objects m and n:

m + 1 = n;

is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:

(m + 1) = n;    // error

which is an error because m + 1 is an rvalue.

As another example, the unary & (address-of) operator requires an lvalue as its operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. Again, 3 does not refer to an object, so it's not addressable.

Although the unary & requires an lvalue as its operand, it's result is an rvalue. For example:

int n, *p;
...
p = &n;    // ok
&n = p;    // error: &n is an rvalue

In contrast to unary &, unary * produces an lvalue as its result. A non-null pointer p always points to an object, so *p is an lvalue. For example:

int a[N];
int *p = a;
...
*p = 3;     // ok

Although the result is an lvalue, the operand can be an rvalue, as in:

*(p + 1) = 4;    // ok


Data storage for rvalues
--------------------------------------------------------------------------------

Conceptually, an rvalue is just a value; it doesn't refer to an object. In practice, it's not that an rvalue can't refer to an object. It's just that an rvalue doesn't necessarily refer to an object. Therefore, both C and C++ insist that you program as if rvalues don't refer to objects.

The assumption that rvalues do not refer to objects gives C and C++ compilers considerable freedom in generating code for rvalue expressions. Consider an assignment such as:

n = 1;

where n is an int. A compiler might generate named data storage initialized with the value 1, as if 1 were an lvalue. It would then generate code to copy from that initialized storage to the storage allocated for n. In assembly language, this might look like:

one: .word 1
...
mov (one), n

Many machines provide instructions with immediate operand addressing, in which the source operand can be part of the instruction rather than separate data. In assembly, this might look like:

mov #1, n

In this case, the rvalue 1 never appears as an object in the data space. Rather, it appears as part of an instruction in the code space.

On some machines, the fastest way to put the value 1 into an object is to clear it and then increment it, as in:

clr n
inc n

Clearing the object sets it to zero. Incrementing adds one. Yet data representing the values 0 and 1 appear nowhere in the object code.


More to come
--------------------------------------------------------------------------------

Although it's true that rvalues in C do not refer to objects, it's not so in C++. In C++, rvalues of a class type do refer to objects, but they still aren't lvalues. Thus, everything I've said thus far about rvalues is true as long as we're not dealing with rvalues of a class type.

Although lvalues do designate objects, not all lvalues can appear as the left operand of an assignment. I'll pick up with this in my next column.

如下Q?/p>

 

--------------------------------------------------------------------------------
Non-modifiable Lvalues

--------------------------------------------------------------------------------


Lvalues actually come in a variety of flavors. If you really want to understand how compilers evaluate expressions, you'd better develop a taste.

const 限定W的含义Q?比如 int const m;
它ƈ不是说m的g能被修改, 而是?m 不能修改它引用的对象Q?br>e.g:
int m;
int const *p = &m;
m += 1;  //right
*p += 1; //wrong
 

An expression is a sequence of operators and operands that specifies a computation. That computation might produce a resulting value and it might generate side effects. An assignment expression has the form:

e1 = e2

where e1 and e2 are themselves expressions. The right operand e2 can be any expression, but the left operand e1 must be an lvalue expression. That is, it must be an expression that refers to an object. As I explained last month ("Lvalues and Rvalues," June 2001, p. 70), the "l" in lvalue stands for "left," as in "the left side of an assignment expression." For example:

int n;

declares n as an object of type int. When you use n in an assignment expression such as:

n = 3;

the n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue. On the other hand:

3 = n;

causes a compilation error, and well it should, because it's trying to change the value of an integer constant. Although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. An rvalue is simply any expression that is not an lvalue. It doesn't refer to an object; it just represents a value.

Although lvalue gets its name from the kind of expression that must appear to the left of an assignment operator, that's not really how Kernighan and Ritchie defined it. In the first edition of The C Programming Language (Prentice-Hall, 1978), they defined an lvalue as "an expression referring to an object." At that time, the set of expressions referring to objects was exactly the same as the set of expressions eligible to appear to the left of an assignment operator. But that was before the const qualifier became part of C and C++.

The const qualifier renders the basic notion of lvalues inadequate to describe the semantics of expressions. We need to be able to distinguish between different kinds of lvalues. And that's what I'm about to show you how to do. But first, let me recap.


A few key points
--------------------------------------------------------------------------------

The assignment operator is not the only operator that requires an lvalue as an operand. The unary & (address-of) operator requires an lvalue as its sole operand. That is, &n is a valid expression only if n is an lvalue. Thus, an expression such as &3 is an error. The literal 3 does not refer to an object, so it's not addressable.

Not only is every operand either an lvalue or an rvalue, but every operator yields either an lvalue or an rvalue as its result. For example, the binary + operator yields an rvalue. Given integer objects m and n:

m + 1 = n;

is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to:

(m + 1) = n; // error

which is an error because m + 1 is an rvalue.

An operator may require an lvalue operand, yet yield an rvalue result. The unary & is one such operator. For example:

int n, *p;
...
p = &n; // ok
&n = p; // error: &n is an rvalue


On the other hand, an operator may accept an rvalue operand, yet yield an lvalue result, as is the case with the unary * operator. A valid, non-null pointer p always points to an object, so *p is an lvalue. For example:

int a[N];
int *p = a;
...
*p = 3; // ok

Although the result is an lvalue, the operand can be an rvalue, as in:

*(p + 1) = 4; // ok

With this in mind, let's look at how the const qualifier complicates the notion of lvalues.


Lvalues and the const qualifier
--------------------------------------------------------------------------------

A const qualifier appearing in a declaration modifies the type in that declaration, or some portion thereof. For example: int const n = 127;

declares n as object of type "const int." The expression n refers to an object, almost as if const weren't there, except that n refers to an object the program can't modify. For example, an assignment such as:

n = 0; // error, can't modify n

produces a compile-time error, as does:

++n; // error, can't modify n

(I covered the const qualifier in depth in several of my earlier columns. See "Placing const in Declarations," June 1998, p. 19 or "const T vs. T const," February 1999, p. 13, among others.) How is an expression referring to a const object such as n any different from an rvalue? After all, if you rewrite each of the previous two expressions with an integer literal in place of n, as in:

7 = 0; // error, can't modify literal ++7; // error, can't modify literal

they're both still errors. You can't modify n any more than you can an rvalue, so why not just say n is an rvalue, too? The difference is that you can take the address of a const object, but you can't take the address of an integer literal. For example:

int const *p;
...
p = &n; // ok
p = &7; // error

Notice that p declared just above must be a "pointer to const int." If you omitted const from the pointer type, as in:

int *p;

then the assignment:

p = &n; // error, invalid conversion

would be an error. When you take the address of a const int object, you get a value of type "pointer to const int," which you cannot convert to "pointer to int" unless you use a cast, as in:

p = (int *)&n; // (barely) ok

Although the cast makes the compiler stop complaining about the conversion, it's still a hazardous thing to do. (See "What const Really Means," August 1998, p. 11.)

Thus, an expression that refers to a const object is indeed an lvalue, not an rvalue. However, it's a special kind of lvalue called a non-modifiable lvalue-an lvalue that you can't use to modify the object to which it refers. This is in contrast to a modifiable lvalue, which you can use to modify the object to which it refers.

Once you factor in the const qualifier, it's no longer accurate to say that the left operand of an assignment must be an lvalue. Rather, it must be a non-modifiable lvalue. In fact, every arithmetic assignment operator, such as += and *=, requires a modifiable lvalue as its left operand. For all scalar types:

x += y; // arithmetic assignment

is equivalent to:

x = x + y; // assignment

except that it evaluates x only once. Since the x in this assignment must be a modifiable lvalue, it must also be a modifiable lvalue in the arithmetic assignment. Not every operator that requires an lvalue operand requires a modifiable lvalue. The unary & operator accepts either a modifiable or a non-modifiable lvalue as its operand. For example, given:

int m;
int const n = 10;

&m is a valid expression returning a result of type "pointer to int," and &n is a valid expression returning a result of type "pointer to const int."


What it is that's really non-modifiable
--------------------------------------------------------------------------------
Earlier, I said a non-modifiable lvalue is an lvalue that you can't use to modify an object. Notice that I did not say a non-modifiable lvalue refers to an object that you can't modify-I said you can't use the lvalue to modify the object. The distinction is subtle but nonetheless important, as shown in the following example. Consider:

int n = 0;
int const *p;
...
p = &n;

At this point, p points to n, so *p and n are two different expressions referring to the same object. However, *p and n have different types. As I explained in an earlier column ("What const Really Means"), this assignment uses a qualification conversion to convert a value of type "pointer to int" into a value of type "pointer to const int." Expression n has type "(non-const) int." It is a modifiable lvalue. Thus, you can use n to modify the object it designates, as in:

n += 2;

On the other hand, p has type "pointer to const int," so *p has type "const int." Expression *p is a non-modifiable lvalue. You cannot use *p to modify the object n, as in:

*p += 2;

even though you can use expression n to do it. Such are the semantics of const in C and C++.


In summary
--------------------------------------------------------------------------------
Every expression in C and C++ is either an lvalue or an rvalue. An lvalue is an expression that designates (refers to) an object. Every lvalue is, in turn, either modifiable or non-modifiable. An rvalue is any expression that isn't an lvalue. Operationally, the difference among these kinds of expressions is this:

  • A modifiable lvalue is addressable (can be the operand of unary &) and assignable (can be the left operand of =).
  • A non-modifiable lvalue is addressable, but not assignable.
  • An rvalue is neither addressable nor assignable. 
  • Again, as I cautioned last month, all this applies only to rvalues of a non-class type. Classes in C++ mess up these concepts even further. 

Dan Saks is a high school track coach and the president of Saks & Associates, a C/C++ training and consulting company. You can write to him at dsaks@wittenberg.edu.

 

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/SeeSeaBee/archive/2007/09/08/1777120.aspx



zhaoyg 2010-02-06 22:41 发表评论
]]>
[转帖]C++模板元编E?/title><link>http://www.shnenglu.com/zhaoyg/archive/2009/11/15/100972.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 15 Nov 2009 06:25:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2009/11/15/100972.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/100972.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2009/11/15/100972.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/100972.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/100972.html</trackback:ping><description><![CDATA[作者:荣耀<br>原文出处Q?a target=_blank><u><font color=#800080>http://www.royaloo.com/articles/articles_2003/Metaprogramming.htm</font></u></a><br><br><br><!-- DETAILS --><strong>摘要</strong><br><br>本文描述了模板元~程技术的h、概念和机制Qƈ介绍了模板元~程技术在Blitz++和LokiE序库中的应用?nbsp;<br><br><strong>关键?/strong><br><br>~译期计?nbsp; 模板元编E?nbsp; Blitz++  Loki <br><br><strong>D </strong><br><br>1994q_C++标准委员会在圣P哥D行的一ơ会议期间Erwin Unruh展示了一D可以生质数的代码。这D代码的特别之处在于质数产生于编译期而非q行期,在编译器产生的一pd错误信息中间Ҏ(gu)着?到某个设定g间的所有质敎ͼ<br> <br><font color=#008000>// Prime number computation by Erwin Unruh</font> <br>template <int i> struct D { D(void*); operator int(); }; <br><br>template <int p, int i> struct is_prime { <br>    enum { prim = (p%i) && is_prime<(i > 2 ? p : 0), i -1> :: prim }; <br>}; <br><br>template < int i > struct Prime_print { <br>    Prime_print<i-1> a; <br>    enum { prim = is_prime<i, i-1>::prim }; <br>    void f() { D<i> d = prim; } <br>}; <br><br>struct is_prime<0,0> { enum {prim=1}; }; <br>struct is_prime<0,1> { enum {prim=1}; }; <br>struct Prime_print<2> { enum {prim = 1}; void f() { D<2> d = prim; } }; <br>#ifndef LAST <br>#define LAST 10 <br>#endif <br>main () { <br>    Prime_print<LAST> a; <br>} <br><br>cL板D只有一个参Cؓvoid*的构造器Q而只?才能被合法{换ؓvoid*?994q_Erwin Unruh采用Metaware ~译器编译出错信息如下(以及其它一些信息,短v见,它们被删除了Q: <br>| Type `enum{}′ can′t be converted to txpe `D<2>′ ("primes.cpp",L2/C25). <br>| Type `enum{}′ can′t be converted to txpe `D<3>′ ("primes.cpp",L2/C25). <br>| Type `enum{}′ can′t be converted to txpe `D<5>′ ("primes.cpp",L2/C25). <br>| Type `enum{}′ can′t be converted to txpe `D<7>′ ("primes.cpp",L2/C25). <br>如今Q上面的代码已经不再是合法的C++E序了。以下是Erwin Unruh亲手l出的修订版Q可以在今天W合标准的C++~译器上q行~译Q?br> <br><font color=#008000>// Prime number computation by Erwin Unruh</font> <br><br>template <int i> struct D { D(void*); operator int(); }; <br><br>template <int p, int i> struct is_prime { <br>    enum { prim = (p==2) || (p%i) && is_prime<(i>2?p:0), i-1> :: prim }; <br>}; <br><br>template <int i> struct Prime_print { <br>Prime_print<i-1> a; <br>    enum { prim = is_prime<i, i-1>::prim }; <br>    void f() { D<i> d = prim ? 1 : 0; a.f();} <br>}; <br><br>template<> struct is_prime<0,0> { enum {prim=1}; }; <br>template<> struct is_prime<0,1> { enum {prim=1}; }; <br><br>template<> struct Prime_print<1> { <br>    enum {prim=0}; <br>    void f() { D<1> d = prim ? 1 : 0; }; <br>}; <br><br>#ifndef LAST <br>#define LAST 18 <br>#endif <br><br>main() { <br>    Prime_print<LAST> a; <br>    a.f(); <br>} <br>在GNU C++ (MinGW Special) 3.2中编译这D늨序时Q编译器会l出如下出错信息Q以及其它一些信息,短v见,它们被删除了Q: <br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 17]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 13]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 11]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 7]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 5]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 3]'<br>Unruh.cpp:12: initializing argument 1 of `D<i>::D(void*) [with int i = 2]' <br>q个例子展示了可以利用模板实例化机制于编译期执行一些计。这U通过模板实例化而执行的~译期计技术即被称为模板元~程?nbsp;<br><br><strong>一个可以运行的模板元编E例?/strong> <br><br>模板元编E(Template MetaprogrammingQ更准确的含义应该是“~?#8216;可以~程序的’E序”Q而模板元E序QTemplate MetaprogramQ则?#8220;‘可以~程序的’E序”。也是_我们l出代码的生规则,~译器在~译期解释这些规则ƈ生成C码来实现我们预期的功能?nbsp;<br>Erwin Unruh的那D늻总码ƈ没有执行Q它只是以编译出错信息的方式输出中间计算l果。让我们来看一个可以运行的模板元编E例??计算l定整数的指定次方: <br><br><font color=#008000>// xy.h</font><br><br><font color=#008000>//原始摸板</font><br>template<int Base, int Exponent><br>class XY<br>{<br>public:<br>    enum { result_ = Base * XY<Base, Exponent-1>::result_ };<br>};<br><br><font color=#008000>//用于l结递归的局部特化版</font><br>template<int Base><br>class XY<Base, 0> <br>{<br>public:<br>    enum { result_ = 1 };<br>};<br>模板元编E技术之Ҏ(gu)在于递归模板实例化。第一个模板实C一般情况下的递归规则。当用一Ҏ(gu)?lt;X, Y>来实例化模板Ӟ模板XY<X, Y>需要计其result_的|同一模板中针?lt;X, Y-1>实例化所得结果乘以X卛_。第二个模板是一个局部特化版本,用于l结递归?nbsp;<br>让我们看看用此模板来计?^4 Q通过实例化XY<5, 4>Q时发生了什么: <br><font color=#008000>// xytest.cpp</font><br><br>#include <iostream><br>#include "xy.h"<br><br>int main() <br>{<br>    std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;<br>}<br>首先Q编译器实例化XY<5, 4>Q它的result_? * XY<5, 3>::result_Q如此一来,又需要针?lt;5, 3>实例化同L(fng)模板Q后者又实例化XY<5, 2>…… 当实例化到XY<5, 0>的时候,result_的D计算?Q至此递归l束?nbsp;<br><strong><br>递归模板实例化的深度和终l条?/strong> <br><br>可以惌Q如果我们以非常大的Y值来实例化类模板XYQ那肯定会占用大量的~译器资源甚至会q速耗尽可用资源Q在计算l果溢出之前Q,因此Q在实践中我们应该有节制C用模板元~程技术?nbsp;<br>虽然 C++标准的最实例化深度只有17层,然而大多数~译器都能够处理臛_几十层,有些~译器允许实例化x癑ֱQ更有一些可达数千层Q直臌源耗尽?nbsp;<br>假如我们拿掉XY模板局部特化版本,情况会如何? <br><font color=#008000>// xy2.h<br><br>//原始摸板</font><br>template<int Base, int Exponent><br>class XY<br>{<br>public:<br>    enum { result_ = Base * XY<Base, Exponent-1>::result_ };<br>};<br>试E序不变Q?nbsp;<br><font color=#008000>// xytest2.cpp</font><br><br>#include <iostream><br>#include "xy2.h"<br><br>int main() <br>{<br>    std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;<br>}<br>执行如下~译命oQ?nbsp;<br>C:\>g++ -c xytest2.cpp <br>你将会看到递归实例化将一直进行下去,直到辑ֈ~译器的极限?nbsp;<br>GNU C++ (MinGW Special) 3.2的默认实例化极限深度?00层,你也可以手工调整实例化深度: <br>C:\>g++ -ftemplate-depth-3400 -c xytest2.cpp <br>事实上,g++ 3.2允许的模板实例化极限深度q可以再大一些(我的试l果是不过3450层)?nbsp;<br>因此Q在使用模板元编E技术时Q我们L要给出原始模板的特化版(局部特化版或完全特化版或兼而有之)Q以作ؓ递归模板实例化的l结准则?nbsp;<br><br><strong>利用模板元编E技术解开循环</strong> <br><br>模板元编E技术最早的实际应用之一是用于数D中的解循环。D个例子,对一个数l进行求和的常见Ҏ(gu)是: <br><font color=#008000>// sumarray.h</font><br><br>template <typename T><br>inline T sum_array(int Dim, T* a)<br>{<br>    T result = T();<br>    for (int i = 0; i < Dim; ++i) <br>    { <br>        result += a[i];<br>    }<br>    return result;<br>}<br>q当然可行,但我们也可以利用模板元编E技术来解开循环Q?nbsp;<br><font color=#008000>// sumarray2.h<br><br>// 原始模板</font><br>template <int Dim, typename T><br>class Sumarray <br>{<br>public:<br>    static T result(T* a)<br>    {<br>        return a[0] + Sumarray<Dim-1, T>::result(a+1);<br>    }<br>};<br><br><font color=#008000>// 作ؓl结准则的局部特化版</font><br>template <typename T><br>class Sumarray<1, T> <br>{<br>public:<br>    static T result(T* a)<br>    {<br>        return a[0];<br>    }<br>};<br><br>用法如下Q?nbsp;<br><br><font color=#008000>// sumarraytest2.cpp</font><br><br>#include <iostream><br>#include "sumarray2.h"<br><br>int main()<br>{<br>    int a[6] = {1, 2, 3, 4, 5, 6};<br>    std::cout << " Sumarray<6>(a) = " << Sumarray<6, int>::result(a);<br>}<br>当我们计Sumarray<6, int>::result(a)Ӟ实例化过E如下: <br>Sumarray<6, int>::result(a)<br>= a[0] + Sumvector<5, int>::result(a+1)<br>= a[0] + a[1] + Sumvector<4, int>::result(a+2)<br>= a[0] + a[1] + a[2] + Sumvector<3, int>::result(a+3)<br>= a[0] + a[1] + a[2] + a[3] + Sumvector<2, int>::result(a+4)<br>= a[0] + a[1] + a[2] + a[3] + a[4] + Sumvector<1, int>::result(a+5)<br>= a[0] + a[1] + a[2] + a[3] + a[4] + a[5] <br>可见Q@环被展开为a[0]  + a[1] + a[2] + a[3] + a[4] + a[5]。这U直截了当的展开q算几乎L比@环来得更有效率?nbsp;<br>也许拿一个有着600万个元素的数l来例证循环开解的优势可能更有说服力。生成这L(fng)数组很容易,有兴,你不妨测试、对比一下?nbsp;<br><br><strong>模板元编E在数D程序库中的应用</strong> <br><br>Blitz++之所?#8220;快如闪电(sh)”Q这正是blitz的字面含义)Q离不开模板元程序的功劳。Blitz++淋漓致C用了元编E技术,你可以到q些文g源代码中H探I竟Q?nbsp;<br> <ul> <li>dot.h<br> <li>matassign.h<br> <li>matmat.h<br> <li>matvec.h<br> <li>metaprog.h <br> <li> product.h <br> <li>sum.h <br> <li> vecassign.h <br></li> </ul> 让我们看看Blitz++E序库dot.h文g中的模板元程序:<br> <br>template<int N, int I><br>class _bz_meta_vectorDot {<br>public:<br>    enum { loopFlag = (I < N-1) ? 1 : 0 };<br><br>    template<class T_expr1, class T_expr2><br>    static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>    f(const T_expr1& a, const T_expr2& b)<br>    {<br>        return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);<br>    }<br><br>    template<class T_expr1, class T_expr2><br>    static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>    f_value_ref(T_expr1 a, const T_expr2& b)<br>    {<br>        return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);<br>    }<br><br>    template<class T_expr1, class T_expr2><br>    static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>    f_ref_value(const T_expr1& a, T_expr2 b)<br>    {<br>        return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);<br>    }<br><br>    template<class T_expr1, class P_numtype2><br>    static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, P_numtype2)<br>    dotWithArgs(const T_expr1& a, P_numtype2 i1, P_numtype2 i2=0,<br>                P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,<br>                P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)<br>    {<br>        return a[I] * i1 + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::dotWithArgs<br>                                                                                   (a, i2, i3, i4, i5, i6, i7, i8, i9);<br>    }<br>};<br><br>template<><br>class _bz_meta_vectorDot<0,0> {<br>public:<br>    template<class T_expr1, class T_expr2><br>    static inline _bz_meta_nullOperand f(const T_expr1&, const T_expr2&)<br>    { return _bz_meta_nullOperand(); }<br><br>    template<class T_expr1, class P_numtype2><br>    static inline _bz_meta_nullOperand <br>    dotWithArgs(const T_expr1& a, P_numtype2 i1, P_numtype2 i2=0,<br>                P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,<br>                P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)<br>    {<br>        return _bz_meta_nullOperand(); <br>    }<br>};<br>q段代码q比它乍看上ȝ单。_bz_meta_vectorDotcL板用了一个(f)时变量loopFlag来存放每一步@环条件的评估l果Qƈ使用了一个完全特化版作ؓ递归l结的条件。需要说明的是,和几乎所有元E序一Pq个临时变量作用发挥于编译期Qƈ从q行代码中优化掉?nbsp;<br>Todd是在Blitz++数值数l库的主要作者。这个程序库Q以及MTL和POOMA{程序库Q例证了模板元程序可以ؓ我们带来更加高效的数D性能。Todd宣称Blitz++的性能可以和对应的FortranE序库媲?br> <br><strong>LokiE序库:zȝ模板元编E技术的典范</strong> <br><br>模板元编E的价g仅在于高性能数D吗Q不仅如此。LokiE序库以Ҏ(gu)型模式的开创性工作闻名于C++C。它很y妙地利用了模板元~程技术实CTypelistlg。Typelist是实现Abstract Factory、Visitor{泛型模式不可或~的基础设施?nbsp;<br>像C++标准库组件std::list提供对一l数值的操作一PTypelist可以用来操纵一l类型,其定义非常简单(摘自LokiE序库Typelist.h单元Q: <br>template <class T, class U><br>struct Typelist<br>{<br>    typedef T Head;<br>    typedef U Tail;<br>}; <br>昄QTypelist没有M状态,也未定义M操作Q其作用只在于携带类型信息,它ƈ未打被实例化,因此Q对于Typelist的Q何处理都必然发生于编译期而非q行期?nbsp;<br>Typelist可以被无限扩展,因ؓ模板参数可以是Q何类型(包括该模板的其他L(fng)体)。例如: <br>Typelist<char, Typelist<int, Typelist<float, NullType> > >  <br>是一个包含有char、int、float三种cd的Typelist?nbsp;<br>按照Loki的约定,每一个Typelist都必MNullTypel尾。NullType的作用类g传统C字符串的“\0”Q它被声明于LokiE序库的NullType.h文g中: <br>class NullType; <br>NullType只有声明Q没有定义,因ؓLokiE序库永q都不需要创Z个NullType对象?nbsp;<br>让我们看看IndexOf模板元程序,它可以在一个Typelist中查扄定类型的位置Q摘自LokiE序库的Typelist.h单元Q: <br>template <class TList, class T><br>struct IndexOf;<br><br>template <class T><br>struct IndexOf<NullType, T><br>{<br>    enum { value = -1 };<br>};<br><br>template <class T, class Tail><br>struct IndexOf<Typelist<T, Tail>, T><br>{<br>    enum { value = 0 };<br>};<br><br>template <class Head, class Tail, class T><br>struct IndexOf<Typelist<Head, Tail>, T><br>{<br>private:<br>    enum { temp = IndexOf<Tail, T>::value };<br>public:<br>    enum { value = (temp == -1 ? -1 : 1 + temp) };<br>}; <br><br>IndexOf提供了一个原始模板和三个局部特化版。算法非常简单:如果TListQ就是一个TypelistQ是一个NullTypeQ则value?1。如果TList的头部就是TQ则value?。否则将IndexOf施行于TList的尾部和TQƈ评估结果置于一个(f)时变量temp中。如果temp?1Q则value?1Q否则value? + temp?nbsp;<br>Z加深你对Typelist采用的模板元~程技术的认识Q我从LokiE序库剥d如下代码Q放入一个typelistlite.h文g中: <br><font color=#008000>// typelistlite.h<br><br>// 声明Nulltype </font><br>class NullType;<br><br><font color=#008000>// Typelist的定?/font><br>template <class T, class U><br>struct Typelist<br>{<br>    typedef T Head;<br>    typedef U Tail;<br>};<br><br><font color=#008000>// IndexOf的定?nbsp;<br><br>// IndexOf原始模板</font><br>template <class TList, class T> struct IndexOf;<br><br><font color=#008000>// 针对NullType的局部特化版</font><br>template <class T><br>struct IndexOf<NullType, T><br>{<br>    enum { value = -1 };<br>};<br><br><font color=#008000>// 针对“Tlist头部是我们要查扄T”的局部特化版</font><br>template <class T, class Tail><br>struct IndexOf<Typelist<T, Tail>, T><br>{<br>    enum { value = 0 };<br>};<br><br><font color=#008000>// 处理TlistN的局部特化版</font><br>template <class Head, class Tail, class T><br>struct IndexOf<Typelist<Head, Tail>, T><br>{<br>private:<br>    enum { temp = IndexOf<Tail, T>::value };<br>public:<br>    enum { value = (temp == -1 ? -1 : 1 + temp) };<br>}; <br><br>试E序如下Q?nbsp;<br><br><font color=#008000>// typelistlite_test.cpp</font><br><br>#include <iostream><br>#include "typelistlite.h"<br><br><font color=#008000>// 自定义类型Royal</font><br>class Royal {};<br><br><font color=#008000>// 定义一个包含有char、int、Royal和float的Typelist</font><br>typedef Typelist<char, Typelist<int, Typelist<Royal, Typelist<float, NullType> > > > CIRF;<br><br>int main()<br>{<br>    std::cout << "IndexOf<CIRF, int>::value = " << IndexOf<CIRF, int>::value << "\n";<br>    std::cout << "IndexOf<CIRF, Royal>::value = " << IndexOf<CIRF, Royal>::value << "\n";<br>    std::cout << "IndexOf<CIRF, double>::value = " << IndexOf<CIRF, double>::value << "\n";<br>} <br><br>E序输出如下Q?nbsp;<br><br>IndexOf<CIRF, int>::value = 1<br>IndexOf<CIRF, Royal>::value = 2<br>IndexOf<CIRF, double>::value = -1 <br><br><strong>l语</strong> <br><br>模板元编E技术ƈ非都是优点,比方_模板元程序编译耗时Q带有模板元E序的程序生成的代码寸要比普通程序的大,而且通常q种E序调试h也比常规E序困难得多。另外,对于一些程序员来说Q以cL板的方式描述法也许有点抽象?nbsp;<br>~译耗时的代h来的是卓的q行期性能。通常来说Q一个有意义的程序的q行ơ数Q或服役旉QLq远过~译ơ数Q或~译旉Q。ؓE序的用户带来更好的体验Q或者ؓ性能要求严格的数D换取更高的性能Q值得E序员付L(fng)代h(hun)?nbsp;<br>很难惌模板元编E技术会成ؓ每一个普通程序员的日常工P相反Q就像Blitz++和Loki那样Q模板元E序几乎L应该被封装在一个程序库的内部。对于库的用h_它应该是透明的。模板元E序可以Q也应该Q用作常规模板代码的内核Qؓ关键的算法实现更好的性能Q或者ؓ特别的目的实现特别的效果?nbsp;<br>模板元编E技术首ơ正式亮怺Todd Veldhuizen?em>Using C++ Template Metaprograms</em>论文之中。这文章首先发表于1995q?月的<em>C++ Report</em>期刊上,后来Stanley Lippman~辑<em>C++ Gems</em>一书时又收录了它。参考文献中l出了这文章的链接Q它q描qC许多本文没有描述到的内容?nbsp;<br>David Vandevoorde和Nicolai M. Josuttis合著?em>C++ Templates: The Complete Guide</em>一书花了一整章的篇q介l模板元~程技术,它同h本文的参考资料ƈ且也应该作ؓ你的补充阅读材料?nbsp;<br>Andrei Alexandrescu的天才著?em>Modern C++ Design: Generic Programming and Design Patterns Applied</em>的第3?em>Typelists</em>对Typelist有着更ؓ详尽的描q?br> <br><strong>参考文?/strong> <br><br>1. David Vandevoorde, Nicolai M. Josuttis, <em>C++ Templates: The Complete Guide</em>, Addison Wesley, 2002<o:p>. <br>2.  Andrei Alexandrescu, <em>Modern C++ Design: Generic Programming and Design Patterns Applied</em>, Addison Wesley, 2001.<br>3. 侯捷 於春?译,《C++设计新思维》,华中U技大学出版C,2003?<br>4. Todd Veldhuizen, <em>Template Metaprograms,</em> <a target=_blank><u><font color=#0000ff>http://osl.iu.edu/~tveldhui/papers/Template-Metaprograms/meta-art.html </font></u></a>. <br>5. Todd Veldhuizen, <em>C++ templates as partial evaluation</em> (PEPM99), <a ><u><font color=#0000ff>http://osl.iu.edu/~tveldhui/papers/pepm99/</font></u></a>. <br>6. Erwin Unruh, Prime numbers(Primzahlen - <a ><u><font color=#0000ff>Original</font></u></a>), <a ><u><font color=#0000ff>http://www.erwin-unruh.de/primorig.html </font></u></a>. <br>7. Erwin Unruh, Prime numbers(Primzahlen), <a ><u><font color=#0000ff>http://www.erwin-unruh.de/Prim.html </font></u></a>. <br>8. Blitz++, <a ><u><font color=#800080>http://www.oonumerics.org/blitz </font></u></a>. <br>9. Loki, <a ><u><font color=#0000ff>http://sourceforge.net/projects/loki-lib </font></u></a>. <br>10. POOMA, <a ><u><font color=#0000ff>http://www.pooma.com</font></u></a>. <br>11. MinGW - Minimalist GNU for Windows, <a ><u><font color=#0000ff>http://sourceforge.net/projects/mingw</font></u></a>. </o:p> <img src ="http://www.shnenglu.com/zhaoyg/aggbug/100972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-11-15 14:25 <a href="http://www.shnenglu.com/zhaoyg/archive/2009/11/15/100972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ȝ型承中的虚函数问题http://www.shnenglu.com/zhaoyg/archive/2009/10/28/99648.htmlzhaoygzhaoygWed, 28 Oct 2009 04:51:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/10/28/99648.htmlhttp://www.shnenglu.com/zhaoyg/comments/99648.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/10/28/99648.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/99648.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/99648.html问题1Q?/span>
class Base1
{
public:
    
virtual void f()
    
{
        cout 
<< "Base1::f" << endl;
    }

    
virtual void g()
    
{
        cout 
<< "Base1::g" << endl;
    }

}
;
class Base2 :public virtual Base1
{
public:

    
virtual void f()
    
{
        cout 
<< "Base2::f" << endl;
    }

    
virtual void g()
    
{
        cout 
<< "Base2::g" << endl;
    }

}
;

class Base3:public virtual Base1
{
public:

    
virtual void f()
    
{
        cout 
<< "Base3::f" << endl;
    }

    
virtual void g()
    
{
        cout 
<< "Base3::g" << endl;
    }

}
;

class Derive : public Base2, public Base3 {
public:
virtual void g() { cout << "Derive::g1" << endl; }
}
;

以上代码是在论坛中遇见的Q当时我q没有立d应上来这是什么缘故,思烦一阵后Q才弄明白?br>原来Q在Derive中没有重写f函数Q又因ؓ当派生类没有重写基类的虚函数Ӟzcd象对该虚函数的调用,会调用其基cM的版本,而Derive又是多承,于是在Derivel承时就不知道Base1中的虚函数表应该记录哪个版本的f函数Q是Base2Q还是Base3?br>因ؓDerive中已重定义g函数QBase1的虚函数表记录的是Derive::g?/p>

==============================================================================

问题2Q?br>代码来源<effective C++>2nd

class Lottery
{
public:
  
virtual int draw();
};

class GraphicalObject 
{
public:
  
virtual int draw();
};

class LotterySimulation: public Lottery,
                         
public GraphicalObject 
{
  
// 没有声明draw
};

LotterySimulation 
*pls = new LotterySimulation;

pls
->draw();   // 错误! ---- 二义

因ؓLotterySimulation中存在两个名为draw的函敎ͼ于是调用存在二义性。同Ӟ即便更改其中一个draw的访问性也不能避免q种二义性,因ؓ改变一个类成员的访问权限不应该改变E序的含义?br>对于如下代码Q仍然存在二义?br>
class SpecialLotterySimulation: public LotterySimulation 
{
public:
  
virtual int draw();
};

pls 
= new SpecialLotterySimulation;

pls
->draw();     // 错误!  q是有二?br>
因ؓQpls的静态类型是LotterySimulationQ而名字的查找是向上进行的Q所以即便SpecialLotterySimulation中定义了一个drawQ对pls来说他是不会查看SpecialLotterySimilation中的名称的?/span>

zhaoyg 2009-10-28 12:51 发表评论
]]>
{偶尔学习(fn)C++标准} ?[对象的复?- 复制构造与赋值操作符] http://www.shnenglu.com/zhaoyg/archive/2009/10/07/98041.htmlzhaoygzhaoygWed, 07 Oct 2009 12:25:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/10/07/98041.htmlhttp://www.shnenglu.com/zhaoyg/comments/98041.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/10/07/98041.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/98041.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/98041.html阅读全文

zhaoyg 2009-10-07 20:25 发表评论
]]>
[ ?] VC++Q掀起你的盖头来——谈VC++对象模型http://www.shnenglu.com/zhaoyg/archive/2009/09/14/96157.htmlzhaoygzhaoygMon, 14 Sep 2009 12:35:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/09/14/96157.htmlhttp://www.shnenglu.com/zhaoyg/comments/96157.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/09/14/96157.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/96157.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/96157.html阅读全文

zhaoyg 2009-09-14 20:35 发表评论
]]>
初识"q回g?http://www.shnenglu.com/zhaoyg/archive/2009/05/24/85658.htmlzhaoygzhaoygSun, 24 May 2009 15:11:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/05/24/85658.htmlhttp://www.shnenglu.com/zhaoyg/comments/85658.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/05/24/85658.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/85658.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/85658.html?lt;C++~程思想>2th中文?W?2.3.3.2?#8220;q回g?#8221;中说Q对于处?#8220;return Integer(left.i + left.i);”q种的返回时Q编译器直接在目的内存中创徏Q且因ؓ不是创徏局部对象,故可直接调用普通构造函敎ͼ而不需要复制构造函敎ͼ但,对于

Integer temp;
return temp;

q样的返回值Ş式,是需要调用复制构造函数来在目标内存中创徏对象的?br>
我在VC2005中试了如下的函敎ͼ
X f()
{
    X one(
5);
    
//return one; //因ؓVC中默认情况下debug模式优化被禁?release模式优化可用Q所以在release模式下直接将one的定义目标内存中;debug则是调用复制构造在目标内存中构?br>    //return X(4); // release & debug 都直接在目标内存中构造对?/span>
}

int main()
{
   X test 
= f();
}

对于Q?br>Integer temp;
return temp;
q种形式Q在VC2005中,如果没有用优化Q则不要求复制构造函数可讉KQ也是说复制构造函数都不会被调用?br>但标准中_“Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created. [ Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11), shall be satisfied.]”Q所以还是保留复制构造函数的可访问性吧?br>
P.S. : 后来了解到VC?或者说标准允许)对命名对象的q回采用“命名q回g?NRVO)”来进行优化,但是对于q种优化只有在某些编译器选项开启后才得以实玎ͼ臛_VC是这L(fng)?br>

2009.6.18 更新


zhaoyg 2009-05-24 23:11 发表评论
]]>
{偶尔学习(fn)C++标准} ?[初识临时对象生命期]http://www.shnenglu.com/zhaoyg/archive/2009/05/24/85538.htmlzhaoygzhaoygSun, 24 May 2009 07:53:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/05/24/85538.htmlhttp://www.shnenglu.com/zhaoyg/comments/85538.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/05/24/85538.html#Feedback1http://www.shnenglu.com/zhaoyg/comments/commentRss/85538.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/85538.html阅读全文

zhaoyg 2009-05-24 15:53 发表评论
]]>
09q之学习(fn)计划http://www.shnenglu.com/zhaoyg/archive/2009/05/17/83178.htmlzhaoygzhaoygSun, 17 May 2009 04:43:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2009/05/17/83178.htmlhttp://www.shnenglu.com/zhaoyg/comments/83178.htmlhttp://www.shnenglu.com/zhaoyg/archive/2009/05/17/83178.html#Feedback0http://www.shnenglu.com/zhaoyg/comments/commentRss/83178.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/83178.html其实我ƈ不想写这个所谓的“学习(fn)计划”Q可以说是有些害怕写q东西,因ؓ我常常是“计划赶不上变?#8221;Q例?8q上半年说是要将C++学完Q但后来因ؓp四的原因而不得不搁浅Q毕竟四U不能小视呀Q,而后来又因ؓ一些其他因素致使我直到今年4月䆾才学完C++。可以说Q所?#8220;计划”是Ҏ(gu)的讽刺。希望这ơ不要再“讽刺”了,已经没时间了?br>
2009q之内要完成如下内容Q?br>   初步学会    SQL Server 2005
   初步学会    VC++~程
   看完《深入详解VC++?br>   看完《SQL Server ~程入门l典?br>   看完《Effective C++?br>   看完《Exceptional C++?br>   多学些算法及数据l构
   量多看些《深入理解计机pȝ》中的内?br>
但愿q次能按时完成Q务?/span>

zhaoyg 2009-05-17 12:43 发表评论
]]>
[转脓(chung)]void及void指针深层探烦http://www.shnenglu.com/zhaoyg/archive/2008/06/06/52312.htmlzhaoygzhaoygThu, 05 Jun 2008 16:21:00 GMThttp://www.shnenglu.com/zhaoyg/archive/2008/06/06/52312.htmlhttp://www.shnenglu.com/zhaoyg/comments/52312.htmlhttp://www.shnenglu.com/zhaoyg/archive/2008/06/06/52312.html#Feedback2http://www.shnenglu.com/zhaoyg/comments/commentRss/52312.htmlhttp://www.shnenglu.com/zhaoyg/services/trackbacks/52312.html1.概述
  许多初学者对C/C++语言中的void及void指针cd不甚理解Q因此在使用上出C一些错误。本文将对void关键字的深刻含义q行解说Qƈ详述void及void指针cd的用方法与技巧?
  2.void的含?
  void的字面意思是“无类?#8221;Qvoid *则ؓ“无类型指?#8221;Qvoid *可以指向Mcd的数据?
  void几乎只有“注释”和限制程序的作用Q因Z来没有h会定义一个void变量Q让我们试着来定义:

void a;
  q行语句~译时会出错Q提C?#8220;illegal use of type 'void'”。不q,即void a的编译不会出错,它也没有M实际意义?
  void真正发挥的作用在于:
  Q?Q?对函数返回的限定Q?
  Q?Q?对函数参数的限定?
  我们在W三节对以上二点q行具体说明?
  众所周知Q如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋|如果p1和p2指向不同的数据类型,则必M用强制类型{换运符把赋D符双的指针类型{换ؓ左边指针的类型?
  例如Q?
float *p1;
int *p2;
p1 = p2;
  其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”Q必L为:
p1 = (float *)p2;
 而void *则不同,Mcd的指针都可以直接赋值给它,无需q行强制cd转换Q?
void *p1;
int *p2;
p1 = p2;
  但这q不意味着Qvoid *也可以无需强制cd转换地赋l其它类型的指针。因?#8220;无类?#8221;可以包容“有类?#8221;Q?#8220;有类?#8221;则不能包?#8220;无类?#8221;。道理很单,我们可以?#8220;男h和女人都是h”Q但不能?#8220;人是男h”或?#8220;人是女h”。下面的语句~译出错Q?
void *p1;
int *p2;
p2 = p1;
  提示“'=' : cannot convert from 'void *' to 'int *'”?
3.void的?
  下面l出void关键字的使用规则Q?
  规则一 如果函数没有q回|那么应声明ؓvoidcd
  在C语言中,凡不加返回值类型限定的函数Q就会被~译器作回整型值处理。但是许多程序员却误以ؓ其ؓvoidcd。例如:
add ( int a, int b )
{
return a + b;
}
int main(int argc, char* argv[])
{
printf ( "2 + 3 = %d", add ( 2, 3) );
}
  E序q行的结果ؓ输出Q?
  2 + 3 = 5
  q说明不加返回D明的函数的确为int函数?
  林锐博士《高质量C/C++~程》中提到Q?#8220;C++语言有很严格的类型安全检查,不允怸q情况(指函C加类型声明)发生”。可是编译器q不一定这么认定,譬如在Visual C++6.0中上qadd函数的编译无错也无警告且q行正确Q所以不能寄希望于编译器会做严格的类型检查?
  因此Qؓ了避免؜乱,我们在编写C/C++E序Ӟ对于M函数都必M个不漏地指定其类型。如果函数没有返回|一定要声明为voidcd。这既是E序良好可读性的需要,也是~程规范性的要求。另外,加上voidcd声明后,也可以发挥代码的“自注?#8221;作用。代码的“自注?#8221;即代码能自己注释自己?
规则?如果函数无参敎ͼ那么应声明其参数为void
  在C++语言中声明一个这L(fng)函数Q?
int function(void)
{
return 1;
}
  则进行下面的调用是不合法的:
function(2);
  因ؓ在C++中,函数参数为void的意思是q个函数不接受Q何参数?
  我们在Turbo C 2.0中编译:
#include "stdio.h"
fun()
{
return 1;
}
main()
{
printf("%d",fun(2));
getchar();
}
  ~译正确且输?Q这说明Q在C语言中,可以l无参数的函C送Q意类型的参数Q但是在C++~译器中~译同样的代码则会出错。在C++中,不能向无参数的函C送Q何参敎ͼ出错提示“'fun' : function does not take 1 parameters”?
  所以,无论在Cq是C++中,若函C接受M参数Q一定要指明参数为void?
  规则?心使用void指针cd
  按照ANSI(American National Standards Institute)标准Q不能对void指针q行法操作Q即下列操作都是不合法的Q?
void * pvoid;
pvoid++; //ANSIQ错?
pvoid += 1; //ANSIQ错?
//ANSI标准之所以这栯定,是因为它坚持Q进行算法操作的指针必须是确定知道其指向数据cd大小的?
//例如Q?
int *pint;
pint++; //ANSIQ正?
  pint++的结果是使其增大sizeof(int)?
  但是大名鼎鼎的GNU(GNU's Not Unix的羃?则不q么认定Q它指定void *的算法操作与char *一致?
因此下列语句在GNU~译器中皆正:
pvoid++; //GNUQ正?
pvoid += 1; //GNUQ正?
  pvoid++的执行结果是其增大了1?
  在实际的E序设计中,合ANSI标准Qƈ提高E序的可UL性,我们可以q样~写实现同样功能的代码:
void * pvoid;
(char *)pvoid++; //ANSIQ正;GNUQ正?
(char *)pvoid += 1; //ANSIQ错误;GNUQ正?
  GNU和ANSIq有一些区别,M而言QGNU较ANSI?#8220;开?#8221;Q提供了Ҏ(gu)多语法的支持。但是我们在真实设计Ӟq是应该可能地q合ANSI标准?
  规则?如果函数的参数可以是Lcd指针Q那么应声明其参Cؓvoid *
  典型的如内存操作函数memcpy和memset的函数原型分别ؓQ?
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );
  q样QQ何类型的指针都可以传入memcpy和memset中,q也真实CC内存操作函数的意义,因ؓ它操作的对象仅仅是一片内存,而不片内存是什么类型。如果memcpy和memset的参数类型不是void *Q而是char *Q那才叫真的奇怪了Q这L(fng)memcpy和memset明显不是一?#8220;Ua的,q低񔭑味?#8221;函数Q?
下面的代码执行正:
//CZQmemset接受Lcd指针
int intarray[100];
memset ( intarray, 0, 100*sizeof(int) ); //intarray?
//CZQmemcpy接受Lcd指针
int intarray1[100], intarray2[100];
memcpy ( intarray1, intarray2, 100*sizeof(int) ); //intarray2拯lintarray1
  有趣的是Qmemcpy和memset函数q回的也是void *cdQ标准库函数的编写者是多么地富有学问啊Q?
  规则?void不能代表一个真实的变量
  下面代码都企图让void代表一个真实的变量Q因此都是错误的代码Q?
void a; //错误
function(void a); //错误
  void体现了一U抽象,q个世界上的变量都是“有类?#8221;的,譬如一个h不是男h是女hQ还有h妖?Q?
  void的出现只是ؓ了一U抽象的需要,如果你正地理解了面向对象中“抽象基类”的概念,也很Ҏ(gu)理解void数据cd。正如不能给抽象基类定义一个实例,我们也不能定义一个voidQ让我们cL的称void?#8220;抽象数据cd”Q变量?
4.ȝ
  小的void蕴藏着很丰富的设计哲学Q作Z名程序设计h员,寚w题进行深一个层ơ的思考必然我们受益匪浅?br>

来源Q弘?nbsp;   http://www.bansun.com/bbs/thread-4880-1-5.html



zhaoyg 2008-06-06 00:21 发表评论
]]>
[转蝲]内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局?/title><link>http://www.shnenglu.com/zhaoyg/archive/2008/05/30/51587.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Fri, 30 May 2008 05:25:00 GMT</pubDate><guid>http://www.shnenglu.com/zhaoyg/archive/2008/05/30/51587.html</guid><wfw:comment>http://www.shnenglu.com/zhaoyg/comments/51587.html</wfw:comment><comments>http://www.shnenglu.com/zhaoyg/archive/2008/05/30/51587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zhaoyg/comments/commentRss/51587.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zhaoyg/services/trackbacks/51587.html</trackback:ping><description><![CDATA[     摘要:   <a href='http://www.shnenglu.com/zhaoyg/archive/2008/05/30/51587.html'>阅读全文</a><img src ="http://www.shnenglu.com/zhaoyg/aggbug/51587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zhaoyg/" target="_blank">zhaoyg</a> 2008-05-30 13:25 <a href="http://www.shnenglu.com/zhaoyg/archive/2008/05/30/51587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.eurocake.com.cn" target="_blank">99ȾþֻоƷ68</a>| <a href="http://www.icaew.com.cn" target="_blank">þþþþþþþ</a>| <a href="http://www.duanchu.cn" target="_blank">þҹӰ</a>| <a href="http://www.ccbaocheng.cn" target="_blank">þü¶</a>| <a href="http://www.fnmqw.cn" target="_blank">þۺϾþùɫ</a>| <a href="http://www.zzhysh.cn" target="_blank">˾þۺ</a>| <a href="http://www.hkrczp.cn" target="_blank">99þֻƷ</a>| <a href="http://www.smxqw.cn" target="_blank">ۺŮþþ30p</a>| <a href="http://www.yadangxiawa.cn" target="_blank">ŷþþXXX</a>| <a href="http://www.ywck.net.cn" target="_blank">Ʒþþþþþ </a>| <a href="http://www.cdz8.cn" target="_blank">þþþþþþ뾫Ʒպ </a>| <a href="http://www.ahlmnet.cn" target="_blank">þøݾƷԴվ</a>| <a href="http://www.eehqv.cn" target="_blank">wwwþ</a>| <a href="http://www.glkk.net.cn" target="_blank">Ʒ˾þþ</a>| <a href="http://www.fonwf.cn" target="_blank">Ʒþþþþþ</a>| <a href="http://www.sunwebs.cn" target="_blank">ҹҹþݺ</a>| <a href="http://www.ranhuman.cn" target="_blank">ŷ˼Ծþ</a>| <a href="http://www.shqidao.cn" target="_blank">Ʒ׾þAAAƬ69 </a>| <a href="http://www.nanling888.cn" target="_blank">þۺ϶</a>| <a href="http://www.6talent.cn" target="_blank">þ99þ99Ʒӿ</a>| <a href="http://www.byvet.com.cn" target="_blank">ݺɫݺɫۺϾþ</a>| <a href="http://www.ddvxo.cn" target="_blank">˾Ʒþ޸岻</a>| <a href="http://www.pcadmin.cn" target="_blank">ŷպƷþ </a>| <a href="http://www.a13859701381.cn" target="_blank">99þùۺϾƷӰԺ </a>| <a href="http://www.163hy.cn" target="_blank">ھƷþþþӰԺ </a>| <a href="http://www.haihuasuye.cn" target="_blank">vavavaþ</a>| <a href="http://www.songyuan163.net.cn" target="_blank">˾ƷۺϾþþ</a>| <a href="http://www.net901.cn" target="_blank">2021¾þþӾƷ</a>| <a href="http://www.duanchu.cn" target="_blank">LƷþ</a>| <a href="http://www.cn0513.cn" target="_blank">þùɫAVѹۿ</a>| <a href="http://www.9r6.com.cn" target="_blank">þþþ޹</a>| <a href="http://www.bo2.com.cn" target="_blank">˾þۺ</a>| <a href="http://www.gnjb.net.cn" target="_blank">þҹƵ</a>| <a href="http://www.tzfxw.cn" target="_blank">þþþ</a>| <a href="http://www.wjjj8.cn" target="_blank">þþþһ</a>| <a href="http://www.pc345.cn" target="_blank">97þóƷɰ</a>| <a href="http://www.ugmx.cn" target="_blank">鶹AVһþ</a>| <a href="http://www.hefxxw.cn" target="_blank">ҹþþƷ</a>| <a href="http://www.thenorthface.net.cn" target="_blank">þ̳</a>| <a href="http://www.grayhound.cn" target="_blank">þþþƷþþþþ</a>| <a href="http://www.nutiao.cn" target="_blank">ƷëٸAVѾþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>