<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程序员实验室 &#187; C++</title>
	<atom:link href="http://www.prglab.com/blog/p/category/c/feed" rel="self" type="application/rss+xml" />
	<link>http://www.prglab.com/blog</link>
	<description>多读书，读好书</description>
	<lastBuildDate>Thu, 08 Oct 2009 21:01:18 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.3</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>VC6.0中重载操作符函数无法访问类的私有成员</title>
		<link>http://www.prglab.com/blog/p/52</link>
		<comments>http://www.prglab.com/blog/p/52#comments</comments>
		<pubDate>Sat, 13 Oct 2007 19:24:41 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://www.prglab.com/blog/p/52</guid>
		<description><![CDATA[在 C++ 中，操作符(运算符）可以被重载以改写其实际操作。同时我们可以定义一个函数为类的朋友函数(friend function)以便使得这个函数能够访问类的私有成员，这个定义通常在头文件中完成。
在Visual C++中定义一般的函数为朋友函数通常是没有问题的。然而对某些重载操作符的函数，即使我们将它们定义为类的朋友函数，VC的编译器仍然会显示出错信息，认为这些朋友函数无权访问类的私有成员。我认为这应该是VC6.0的bug。
以下代码就是个例子:



// 头文件 "Sample.h"
#include&#60;iostream&#62;
using namespace std;

class Sample {
public:
	Sample();
	friend ostream &#38;operator&#60;&#60;(ostream &#38;out, const Sample s);
	friend istream &#38;operator&#62;&#62;(istream &#38;in, Sample &#38; s);

private:
	int x;
};







// 实现文件 "Sample.cpp"
#include "Sample.h"

Sample::Sample() {
	x=0;
}

istream &#38;operator&#62;&#62;(istream &#38;in, Sample &#38; s) {
	cout&#60;&#60;"Please enter a value"&#60;&#60;endl;
	in &#62;&#62; s.x ;
	return in;
}

ostream &#38;operator&#60;&#60;(ostream &#38;out, const Sample s) {
	cout &#60;&#60; s.x &#60;&#60; endl;
	return out;
}



以上代码在gnuc++中编译运行毫无问题。但是在VC++6.0中编译的时候就会出现以下的编译错误：


Compiling&#8230;
Sample.cpp
c:\temp\sample.cpp(8) : error C2248: [...]]]></description>
			<content:encoded><![CDATA[<p>在 C++ 中，操作符(运算符）可以被重载以改写其实际操作。同时我们可以定义一个函数为类的朋友函数(friend function)以便使得这个函数能够访问类的私有成员，这个定义通常在头文件中完成。</p>
<p>在Visual C++中定义一般的函数为朋友函数通常是没有问题的。然而对某些重载操作符的函数，即使我们将它们定义为类的朋友函数，VC的编译器仍然会显示出错信息，认为这些朋友函数无权访问类的私有成员。我认为这应该是VC6.0的bug。<span id="more-52"></span></p>
<p>以下代码就是个例子:</p>
<table style="border: 1px solid #aaaaaa" border="0">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// 头文件 "Sample.h"</span>
#include&lt;iostream&gt;
using namespace std;

class Sample {
public:
	Sample();
	friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s);
	friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s);

private:
	int x;
};</pre>
</td>
</tr>
</table>
<pre></pre>
<table style="border: 1px solid #aaaaaa" border="0" height="100" width="464">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// 实现文件 "Sample.cpp"</span>
<span style="color: green"></span>#include "Sample.h"

Sample::Sample() {
	x=0;
}

istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s) {
	cout&lt;&lt;"Please enter a value"&lt;&lt;endl;
	in &gt;&gt; s.x ;
	return in;
}

ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s) {
	cout &lt;&lt; s.x &lt;&lt; endl;
	return out;
}</pre>
</td>
</tr>
</table>
<p>以上代码在gnuc++中编译运行毫无问题。但是在VC++6.0中编译的时候就会出现以下的编译错误：</p>
<table style="border: 1px solid #aaaaaa" border="0">
<tr>
<td style="background-color: #eeeeee">Compiling&#8230;<br />
Sample.cpp<br />
c:\temp\sample.cpp(8) : error C2248: &#8216;x&#8217; : cannot access private member declared in class &#8216;Sample&#8217;<br />
c:\temp\sample.h(19) : see declaration of &#8216;x&#8217;<br />
c:\temp\sample.cpp(13) : error C2248: &#8216;x&#8217; : cannot access private member declared in class &#8216;Sample&#8217;<br />
c:\temp\sample.h(19) : see declaration of &#8216;x&#8217;<br />
Error executing cl.exe.Sample.obj &#8211; 2 error(s), 0 warning(s)</td>
</tr>
</table>
<p>在VC++ 6.0中解决这个问题有以下几种方法：</p>
<ul type="1">
<li>在头文件中实现作为朋友函数的操作符函数的重载，也就是说在实现文件&#8221;Sample.cpp&#8221;中将函数重载的实现去掉，而将头文件修改如下：<br />
<table style="border: 1px solid #aaaaaa" border="0">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// 修改后的头文件 1 "Sample.h"</span>
#include&lt;iostream&gt;
using namespace std;

class Sample {
public:
	Sample();
	friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s);
	friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s) {
	    cout &lt;&lt; s.x &lt;&lt; endl;
	    return out;
	}

	friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s);
	friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s) {
		cout&lt;&lt;"Please enter a value"&lt;&lt;endl;
	    in &gt;&gt; s.x ;
	    return in;
	}
private:
	int x;
};</pre>
</td>
</tr>
</table>
<pre></pre>
</li>
<li>在头文件中类定义之前将类和朋友操作符函数的原型特别声明一下，也就是将头文件修改如下(实现文件&#8221;Sample.cpp&#8221;不用作任何修改)：<br />
<table style="border: 1px solid #aaaaaa" border="0">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// 修改后的头文件 2 "Sample.h"</span>
#include&lt;iostream&gt;
using namespace std;

<span style="color: green">// 以下3行代码为新加入</span>
class Sample;
ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s);
istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s);

class Sample {
public:
	Sample();
	friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s);
	friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s);

private:
	int x;
};</pre>
</td>
</tr>
</table>
<pre></pre>
</li>
<li>第三种方法是对I/O名空间的使用实行明确声明，也就是说在头文件&#8221;Sample.h&#8221;中直接写：<br />
#include&lt;iostream&gt;<br />
using std::ostream;<br />
using std::istream<br />
&#8230;.<br />
取代 &#8220;using namespace std;&#8221;<br />
<strong> 注意：</strong>在这个例子里我们在实现文件 &#8220;Sample.cpp&#8221;中包含 &#8220;using namespace std;&#8221;这句话，否则在实现中就不能使用 &#8220;cout&#8221; , &#8220;cin&#8221;, &#8220;&lt;&lt; &#8220;, &#8220;&gt;&gt;&#8221; 和 endl 这些关键字和符号。修改后的完整代码如下：</p>
<table style="border: 1px solid #aaaaaa" border="0">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// Sample.h</span>
#include&lt;iostream&gt;

using std::istream;
using std::ostream;

class Sample {
public:
	Sample();
	friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s);
	/*friend ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s) {
	    cout &lt;&lt; s.x &lt;&lt; endl;
	    return out;
	}*/
    friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s);
	/*friend istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s) {
		cout&lt;&lt;"Please enter a value"&lt;&lt;endl;
	    in &gt;&gt; s.x ;
	    return in;
	}*/
private:
	int x;
};</pre>
</td>
</tr>
</table>
<table style="border: 1px solid #aaaaaa" border="0" height="265" width="486">
<tr>
<td style="background-color: #eeeeee">
<pre><span style="color: green">// "Sample.cpp"</span>
#include "Sample.h"
using namespace std;

Sample::Sample() {
	x=5;
}
istream &amp;operator&gt;&gt;(istream &amp;in, Sample &amp; s) {
		cout&lt;&lt;"Please enter a value"&lt;&lt;endl;
	    in &gt;&gt; s.x ;
	    return in;
	}

ostream &amp;operator&lt;&lt;(ostream &amp;out, const Sample s) {
	cout &lt;&lt; s.x &lt;&lt; endl;
	return out;
}</pre>
</td>
</tr>
</table>
<pre></pre>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/52/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>以指针或reference为参数的C++函数定义与调用对照表</title>
		<link>http://www.prglab.com/blog/p/51</link>
		<comments>http://www.prglab.com/blog/p/51#comments</comments>
		<pubDate>Sat, 13 Oct 2007 17:40:06 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://www.prglab.com/blog/p/51</guid>
		<description><![CDATA[初学者不易建立清晰的指针和reference概念，总结这个表以供快速参考：
假设有对象定义:
MyObjectType obj1;
1. 值传递:
如果函数定义为:
void myFunction( MyObjectType obj);
函数调用:
myFunction(obj); //函数以外对象obj的值不会 改变
2. reference传递:
如果函数定义为:
void myFunction( MyObjectType &#38;obj);
函数调用:
myFunction(obj); //函数以外对象obj的值会 改变
3. 指针传递:
如果函数定义为:
void myFunction( MyObjectType *obj);
函数调用:
myFunction(&#38;obj); //需要dereference(&#38;), 函数以外对象obj的值会 改变
要了解更多关于C++指针的概念请阅读C++基础教程指针篇
]]></description>
			<content:encoded><![CDATA[<p>初学者不易建立清晰的指针和reference概念，总结这个表以供快速参考：</p>
<p>假设有对象定义:<br />
MyObjectType obj1;</p>
<p><span style="color: blue">1. 值传递:</span><br />
如果函数定义为:<br />
void myFunction( MyObjectType obj);</p>
<p>函数调用:<br />
myFunction(obj); <span style="color: green">//函数以外对象obj的值<strong>不会</strong> 改变</span></p>
<p><span style="color: blue">2. reference传递:</span><br />
如果函数定义为:<br />
void myFunction( MyObjectType &amp;obj);</p>
<p>函数调用:<br />
myFunction(obj); <span style="color: green">//函数以外对象obj的值<strong>会</strong> 改变</span></p>
<p><span style="color: blue">3. 指针传递:</span><br />
如果函数定义为:<br />
void myFunction( MyObjectType *obj);</p>
<p>函数调用:<br />
myFunction(&amp;obj); <span style="color: green">//需要dereference(&amp;), 函数以外对象obj的值<strong>会</strong> 改变</span></p>
<p>要了解更多关于C++指针的概念请阅读<a href="http://www.prglab.com/cms/pages/c-tutorial/advanced-data/pointers.php" target="_blank">C++基础教程指针篇</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/51/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>从Java到C++ &#8212; 对比Java与C++编程的不同</title>
		<link>http://www.prglab.com/blog/p/48</link>
		<comments>http://www.prglab.com/blog/p/48#comments</comments>
		<pubDate>Wed, 15 Aug 2007 03:40:15 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://prglab.com/blog/p/48</guid>
		<description><![CDATA[原作：Cay Horstmann      英文原文
翻译：Aqua  prglab.com
注：很多程序员包括本人在内都是先学会的Java，然后才学的C++，其实C++与Java有很多相似和互通之处，有相当的Java知识可以对应转化到C++概念，从而帮助我们快速上手。这篇文章介绍的内容就对从Java向C++转变很有帮助，所以翻译推荐给有同样需要的朋友。翻译中加入了本人的理解，不完全是全文照搬。有不明或异议，请参考原文，或留言讨论。还是那句话，推荐多读原版资料。
学习怎样从一种编程语言转移到另一种编程语言是今日的程序员必须面对的现实，还好，C++和Java有很多共同的特点，所以从Java转到C++就容易许多。C++比Java复杂很多，本文并没打算涵盖所有C++的功能。但是如果你能够熟练掌握本文中的所有知识点，也足够有效的使用C++了。
这里我们只讲Java与C++的不同之处。像流程控制(if, while, for)这些在C++与Java中完全一样的内容这里就不讲了。
本文是基于ANSI C++标准的，一些老的C++ 编译器可能不支持这里讲到的一些重要功能。要使用这些编译器，你需要更多学习C++中从C继承来的部分。那些都超出了本文的范畴，也就不介绍了。
1. 数据类型和变量
C++ 中的变量类型与Java很相似。像Java一样，C++ 有int 和 double 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上，例如运行DOS 或Windows 3.x的PC机上，int 是双字节(2-byte)的，取值范围比Java的4-byte的int 要小很多。在这些机器上，如果 int 不够用的话，你需要使用长整型long。
C++ 有 short 和 unsigned 类型来更有效的存储数字。（我认为所谓有效是指更高的空间利用率。） 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。
在C++中布尔型用 bool 表示，而不像在Java中用boolean。
C++ 中字符串类型用 string 表示。它与Java中的 String 类型非常相似，但是，还是要逐一以下几点不同之处：
1. C++ 字符串存储ASCII 码字符，而不是标准码Unicode 字符
2. C++ 字符串是可以被修改的，而Java字符串的内容是不可修改的(immutable)。
3. 取子字符串的操作在 C++ 中叫做 substr，这个命令s.substr(i, n) 从字符串s中取得从位置 i 开始长度为n的子字符串。
4. 在C++中，你只能够将字符串与其它字符串对象相串联(concatenate)，而不能够与任意的对象相串联。
5. C++中可以直接使用关系操作符 ==、 [...]]]></description>
			<content:encoded><![CDATA[<p>原作：<a href="http://www.horstmann.com/" target="_blank">Cay Horstmann</a>      <a href="http://www.horstmann.com/ccj2/ccjapp3.html" target="_blank">英文原文</a></p>
<p>翻译：Aqua  <a href="http://prglab.com/blog/p/48" title="全文链接">prglab.com</a></p>
<blockquote><p>注：很多程序员包括本人在内都是先学会的Java，然后才学的C++，其实C++与Java有很多相似和互通之处，有相当的Java知识可以对应转化到C++概念，从而帮助我们快速上手。这篇文章介绍的内容就对从Java向C++转变很有帮助，所以翻译推荐给有同样需要的朋友。翻译中加入了本人的理解，不完全是全文照搬。有不明或异议，请参考原文，或留言讨论。还是那句话，推荐多读原版资料。</p></blockquote>
<p>学习怎样从一种编程语言转移到另一种编程语言是今日的程序员必须面对的现实，还好，C++和Java有很多共同的特点，所以从Java转到C++就容易许多。C++比Java复杂很多，本文并没打算涵盖所有C++的功能。但是如果你能够熟练掌握本文中的所有知识点，也足够有效的使用C++了。<span id="more-48"></span></p>
<p>这里我们只讲Java与C++的不同之处。像流程控制(if, while, for)这些在C++与Java中完全一样的内容这里就不讲了。</p>
<p>本文是基于ANSI C++标准的，一些老的C++ 编译器可能不支持这里讲到的一些重要功能。要使用这些编译器，你需要更多学习C++中从C继承来的部分。那些都超出了本文的范畴，也就不介绍了。</p>
<h2>1. 数据类型和变量</h2>
<p>C++ 中的变量类型与Java很相似。像Java一样，C++ 有<tt>int</tt> 和 <tt>double</tt> 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上，例如运行DOS 或Windows 3.x的PC机上，<tt>int</tt> 是双字节(2-byte)的，取值范围比Java的4-byte的<tt>int</tt> 要小很多。在这些机器上，如果 int 不够用的话，你需要使用长整型long。</p>
<p>C++ 有 <tt>short</tt> 和 <tt>unsigned</tt> 类型来更有效的存储数字。（我认为所谓有效是指更高的空间利用率。） 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。</p>
<p>在C++中布尔型用 <tt>bool</tt> 表示，而不像在Java中用boolean。</p>
<p>C++ 中字符串类型用 <tt>string</tt> 表示。它与Java中的 <tt>String</tt> 类型非常相似，但是，还是要逐一以下几点不同之处：</p>
<p>1. C++ 字符串存储ASCII 码字符，而不是标准码Unicode 字符</p>
<p>2. C++ 字符串是可以被修改的，而Java字符串的内容是不可修改的(immutable)。</p>
<p>3. 取子字符串的操作在 C++ 中叫做 <tt>substr</tt>，这个命令<tt>s.substr(i, n)</tt> 从字符串s中取得从位置 i 开始长度为n的子字符串。</p>
<p>4. 在C++中，你只能够将字符串与其它字符串对象相串联(concatenate)，而不能够与任意的对象相串联。</p>
<p>5. C++中可以直接使用关系操作符 <tt>==、 !=、 &lt;、 &lt;=、       &gt;、 &gt;= </tt>来进行字符串比较，其中后面四个操作符是按字母顺序进行比较的。 这比Java中使用函数equals和compareTo来比较要方便很多。</p>
<h2>2. 变量和常量</h2>
<p>在C++中，本地变量的定义看起来与Java中相同，例如：</p>
<p><tt>int n = 5;</tt></p>
<p>实际上这正是C++和Java的一个重要不同之处。C++编译器不对本地变量进行初始化检验，所以在C++中很容易忘记初始化一个变量，这种情况下，变量的值该变量所占内存区域中刚好当前存在随机值。这显然是很容易产生程序出错的地方。</p>
<p>与Java一样， C++中类可以有数据域和静态变量。不同的是，C++中变量可以在函数甚至是类的外面定义，这些所谓的全局变量可以在程序的任何函数中被访问，因而不易被很好的管理。所C++中应该尽量避免使用全局变量。</p>
<p>在C++中，常量可以在任何地方被定义（记得在Java中，常量必须是类的静态数据static data)。 C++ 使用关键字 <tt>const</tt> 来定义常量，而Java中是 <tt>final</tt>。例如：</p>
<p><tt>const int DAYS_PER_YEAR = 365;</tt></p>
<h2>3. 类</h2>
<p>C++ 中对类的定义与Java有些不同，这里是一个例子：一个C++ 版本的 <tt>Point</tt> 类:</p>
<p><tt>class Point /* C++ */</tt></p>
<p><tt>{</tt></p>
<p><tt>    public:</tt></p>
<p><tt>        Point();</tt></p>
<p><tt>        Point(double xval, double yval);</tt></p>
<p><tt>        void move(double dx, double dy);</tt></p>
<p><tt>        double getX() const;</tt></p>
<p><tt>          double getY() const;</tt></p>
<p><tt>    private:</tt></p>
<p><tt>        double x;</tt></p>
<p><tt>        double y;</tt></p>
<p><tt>};</tt></p>
<p>这里几点重要的不同是：</p>
<p>1. C++的类定义中分为公共和私有部分，分别以关键字 <tt>public</tt> 和 <tt>private</tt>开始。而在Java中，每一个元素都必须标明 <tt>public</tt> 或 <tt>private</tt>。</p>
<p>2. C++中类的定义只包含函数的声明，真正的实现另外单独列出。</p>
<p>3. 访问函数(accessor methods)标有关键字 <tt>const</tt> ，表明这个函数不会改变本对象的元素值。</p>
<p>4. 类定义的结尾处有分号</p>
<p>类中函数的实现跟在类的定义之后。因为函数是在类外面定义的，所以每一个函数的名字前面要加类名称作为前缀，并使用操作符双冒号::来分割类的名称和函数的名称。不改变隐含参数值（即当前对象的值）的访问函数用       <tt>const</tt>标明。如下所示是上面类定义中的函数的实现：</p>
<p><tt>Point::Point() { x = 0; y = 0; }</tt></p>
<p><tt>void Point::move(double dx, double dy) </tt></p>
<p><tt>{</tt></p>
<p><tt>  x = x + dx;</tt></p>
<p><tt>  y = y + dy;</tt></p>
<p><tt>}</tt></p>
<p><tt>double Point::getX() const</tt></p>
<p><tt>{  </tt></p>
<p><tt>    return x;</tt></p>
<p><tt>}</tt></p>
<h2>4. 对象</h2>
<p>Java 与 C++ 最主要的不同在于对象变量的使用。在 C++中，对象变量存储的是真正的对象的值，而不是对象引用(<a href="http://www.prglab.com/blog/p/28" title="关于reference的文章">reference</a>)。注意在C++中构造一个对象的时候是不使用关键字new的，只需要在变量的名字后面直接赋予构造函数的参数就可以了，例如：</p>
<p><tt>Point p(1, 2); /* 构造对象 p */</tt></p>
<p>如果不跟参数赋值，则使用默认构造函数，例如：</p>
<p><tt>Time now; /* 默认使用构造函数 Time::Time() */</tt></p>
<p>这一点与Java很不同。在Java中，这个命令仅仅生成一个没有初始化的reference，而在C++中，它生成一个实际的对象。</p>
<p>当一个对象被赋给另一个对象变量的时候，实际的值将被拷贝。而在Java中，拷贝一个对象变量只不过是建立了另外一个指向对象的reference。拷贝一个C++的对象就像在Java中调用clone这个函数一样，而修改拷贝的值不会改变原对象的值。例如：</p>
<p><tt>Point q = p; /* 拷贝p到q */</tt></p>
<p><tt>q.move(1, 1); /* 移动q而p不动，即q的值变了，而p的不变*/</tt></p>
<p>多数情况下，C++中这种对象直接对值操作的特性使用起来很方便，但是也有些时候不尽如人意：</p>
<p>1. 当需要一个函数中修改一个对象的值，必须记住要使用按引用调用call       by reference (参见下面函数部分)</p>
<p>2. 两个对象变量不能指向同一个对象实体。如果你要在C++中实现这种效果，必须使用指针pointer（参见下面指针部分）</p>
<p>3. 一个对象变量只能存储一种特定的类型的值，如果你想要使用一个变量来存储不同子类的对象的值（多态ploymorphism)，则需要使用指针。</p>
<p>4. 如果你想在C++中使用一个变量来或者指向null或者指向一个实际的对象，则需要使用指针</p>
<h2>5. 函数</h2>
<p>在Java中，每一个函数必须或者是对象函数(instance method)，或者是静态函数(static function)或称类函数。C++同样支持对象函数和静态函数（类函数），但同时C++也允许定义不属于任何类的函数，这些函数叫做全局函数<em>（global functions）</em>。</p>
<p>特别的是，每一个C++ 程序都从一个叫做 <tt>main</tt>的全局函数开始执行：</p>
<p><tt>int main()</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<p>还有另外一个格式的main函数可以用来捕捉命令行参数，类似于Java的main函数，但是它要求关于C格式的数组和字符串的知识，这里就不介绍了。</p>
<p>按照习惯，通常如果程序执行成功， <tt>main</tt> 函数返回0，否则返回非零整数。</p>
<p>同Java一样，函数参数是通过值传递的(passed by value)。在Java中，函数无论如何都是可以修改对象的值的。然而在C++中，因为对象直接存储的是实际的值，而不是指向值的reference，也就是说传入函数的是一个实际值的拷贝，因此也就无法修改原来对象的值。</p>
<p>所以，C++ 有两种参数传递机制，同Java一样的按值调用(<em>call by value) </em>，以及按地址调用(<em>call by reference</em>)。当一个参数是按reference传递时，函数可以修改其原始值。Call by reference 的参数前面有一个地址号 <tt>&amp;</tt> 跟在参数类型的后面，例如：</p>
<p><tt>void raiseSalary(Employee&amp; e, double by)</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<p>下面是一个典型的利用call by reference的函数，在Java中是无法实现这样的功能的。</p>
<p><tt>void swap(int&amp; a, int&amp; b)</tt></p>
<p><tt>{  int temp = a;</tt></p>
<p><tt>  a = b;</tt></p>
<p><tt>  b = temp;</tt></p>
<p><tt>}</tt></p>
<p>如果使用 <tt>swap(x, y)</tt>来调用这个函数，则reference参数 <tt>a</tt> 和 <tt>b</tt> 指向原实际参数<tt>x</tt> 和 <tt>y</tt>的位置，而不是它们的值的拷贝，因此这个函数可以实现实际交换这两个参数的值。</p>
<p>在 C++中，每当需要实现修改原参数的值时你就可以使用按地址调用 call by reference 。</p>
<h2>6. 向量Vector</h2>
<p>C++ 的向量结构结合了Java中数组和向量两者的优点。一个C++ 的向量可以方便的被访问，其容量又可以动态的增长。如果 <tt>T</tt> 是任意类型，则 <tt>vector&lt;T&gt;</tt>       是一个元素为 <tt>T</tt> 类型的动态数组。下面的语句</p>
<p><tt>vector&lt;int&gt; a;</tt></p>
<p>产生一个初始为空的向量。而语句</p>
<p><tt>vector&lt;int&gt; a(100);</tt></p>
<p>生成一个初始有100个元素的向量。你可以使用<tt>push_back</tt> 函数来添加元素：</p>
<p><tt>a.push_back(n);</tt></p>
<p>调用 <tt>a.pop_back()</tt> 从<tt>a</tt>中取出最后一个元素（操作后这个元素被从a中删掉)，       使用函数<tt>size</tt> 可以得到当前a中的元素个数。</p>
<p>你还可以通过我们熟悉的 <tt>[]</tt> 操作符来访问向量中元素，例如：</p>
<p><tt>for (i = 0; i &lt; a.size(); i++) {<br />
</tt></p>
<p><tt>  sum = sum + a[i];</tt></p>
<p><tt>}</tt></p>
<p>同Java中一样，数组索引必须为 0 和 <tt>a.size() - 1</tt>之间的值。但是与Java不同的是，C++中没有runtime的索引号合法性检验。试图访问非法的索引位置可能造成非常严重的出错。</p>
<p>就像所有其它 C++ 对象一样，向量也是值。如果你将一个向量赋值给另外一个向量变量，所有的元素都会被拷贝过去。</p>
<p><tt>vector&lt;int&gt; b = a; /* 所有的元素都被拷贝了 */</tt></p>
<p>对比Java中的情况，在Java中，一个数组变量是一个指向数组的reference。拷贝这个变量仅仅产生另外一个指向同一数组的reference，而不会拷贝每一个元素的值。</p>
<p>正因如此，如果一个C++函数要实现修改向量的值，必须使用reference参数：</p>
<p><tt>void sort(vector&lt;int&gt;&amp; a)</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<h2>7. 输入和输出</h2>
<p>在C++中，标准的输入输出流用对象       <tt>cin</tt> 和 <tt>cout</tt> 表示。我们使用 <tt>&lt;&lt;</tt>       操作符写输出，例如：</p>
<p><tt>cout &lt;&lt; "Hello, World!";</tt></p>
<p>也可以连着输出多项内容，例如：</p>
<p><tt>cout &lt;&lt; "The answer is " &lt;&lt; x &lt;&lt; "\n";</tt></p>
<p>我们使用 <tt>&gt;&gt;</tt>       操作符来读入一个数字或单词，例如：</p>
<p><tt>double x;</tt></p>
<p><tt>cout &lt;&lt; "Please enter x: ";</tt></p>
<p><tt>cin &gt;&gt; x;</tt></p>
<p><tt>string fname;</tt></p>
<p><tt>cout &lt;&lt; "Please enter your first name: ";</tt></p>
<p><tt>cin &gt;&gt; fname;</tt></p>
<p>函数<tt>getline</tt> 可以读入整行的输入，例如：</p>
<p><tt>string inputLine;</tt></p>
<p><tt>getline(cin, inputLine);</tt></p>
<p>如果到达输入的结尾，或者一个数字无法被正确的读入，这个流对象会被设置为 failed 状态，我们可以使用函数 <tt>fail</tt> 来检验这个状态，例如：</p>
<p><tt>int n;</tt></p>
<p><tt>cin &gt;&gt; n;</tt></p>
<p><tt>if (cin.fail()) cout &lt;&lt; "Bad input";</tt></p>
<p>一旦一个流的状态被设为failed，我们是很难重置它的状态的，所以如果你的程序需要处理错误输入的情况，应该使用函数 <tt>getline</tt> 然后人工处理得到的输入数据。</p>
<h2>8. 指针pointer</h2>
<p>我们已经知道在C++中，对象变量直接存储的是对象的值。这是与Java不同的，在Java中对象变量存储的是一个地址，该地址指向对象值实际存储的地方。有时在C++中也需要实现这样的布置，这就用到了指针pointer。在 C++中，一个指向对象的变量叫做指针。如果T是一种数据类型，则 <tt>T*</tt> 是指向这种数据类型的指针。 这里重点介绍C++与Java的不同，要详细了解C++中指针的使用，请参考本站另一篇文章“<strong><a href="http://www.prglab.com/blog/p/28" rel="bookmark" title="Permanent Link to C++中Reference与指针（Pointer）的使用对比">C++中Reference与指针（Pointer）的使用对比</a></strong>”。</p>
<p>就像 Java中一样，一个指针变量可以被初始化为空值 <tt>NULL</tt>，另外一个指针变量的值，或者一个调用new生成的新对象：</p>
<p><tt>Employee* p = NULL;</tt></p>
<p><tt>Employee* q = new Employee("Hacker, Harry", 35000);</tt></p>
<p><tt>Employee* r = q;</tt></p>
<p>实际上在C++中还有第四种可能，那就是指针可以被初始化为另外一个对象的地址，这需要使用地址操作符 <tt>&amp;</tt> ：</p>
<p><tt>Employee boss("Morris, Melinda", 83000);</tt></p>
<p><tt>Employee* s = &amp;boss;</tt></p>
<p>这实际上并不是什么好主意。保险的做法还是应该直接让指针指向使用 <tt>new</tt>生成的新对象。</p>
<p>到目前为止，C++ 指针看起来非常像 Java 的对象变量。然而，这里有一个很重要的语法的不同。我们必须使用星号操作符 <tt>*</tt> 来访问指针指向的对象。如果 <tt>p</tt> 是一个指向<tt>Employee</tt>对象的指针，则 <tt>*p</tt> 才代表了这个对象：</p>
<p><tt>Employee* p = . . .;</tt></p>
<p><tt>Employee boss = <strong>*p</strong>;</tt></p>
<p>当我们需要执行对象的函数或访问对象的一个数据域时，也需要使用 <tt>*p</tt> ：</p>
<p><tt>(*p).setSalary(91000);</tt></p>
<p>*p外面的括号是必需的，因为 <tt>.</tt> 操作符比 * 操作符有更高的优先级。C的设计者觉得这种写法很难看，所以他们提供了另外一种替代的写法，使用 -&gt; 操作符来实现 <tt>*</tt> 和 <tt>.</tt> 操作符的组合功能。表达式</p>
<p><tt>p-&gt;setSalary(91000);</tt></p>
<p>可以调用对象*p的函数 <tt>setSalary</tt> 。你可以简单的记住 <tt>.</tt> 操作符是在对象上使用的，-&gt; 操作符是在指针上使用的。</p>
<p>如果你不初始化一个指针，或者如果一个指针为空值 NULL 或指向的对象不再存在，则在它上面使用 <tt>*</tt>       或 <tt>-&gt;</tt> 操作符就会出错。 不幸的是 C++ runtime 系统并不检查这个出错。如果你范了这个错误，你的程序可能会行为古怪或死机。</p>
<p>而在Java中，这些错误是不会发生的。所有的reference都必须初始化，所有的对象只要仍有reference指向它就不会被从内存中清除，因此你也不会有一个指向已被删除的对象的reference。Java的runtime      系统会检查reference是否为空，并在遇到空指针时抛出一个null pointer的例外(exception)。</p>
<p>C++ 和 Java还有一个显著的不同，就是 Java 有<em>垃圾回收</em>功能，能够自动回收被废弃的对象。而在C++中，需要程序员自己管理内存分配回收。</p>
<p>C++中当对象变量超出范围时可以自动被回收。但是使用new生成的对象必须用delete操作符手动删除，例如：</p>
<p><tt>Employee* p = new Employee("Hacker, Harry", 38000);</tt></p>
<p><tt>. . .</tt></p>
<p><tt>delete p; /* 不在需要这个对象 */</tt></p>
<p>如果你忘记删除一个对象，那么你的程序有可能最终用光所有内存。这就是我们常说的内存泄漏 (<em>memory leak</em>)。更重要的是，如果你如果删除了一个对象，然后又继续使用它，你可能覆盖不属于你的数据。如果你刚巧覆盖了用于处理内存回收的数据域，那么内存分配机制就可能运转失常而造成更严重的错误，而且很难诊断和修复。因此，在C++中最好尽量少用指针。</p>
<h2>9. 继承</h2>
<p>C++和Java中继承的基本语法是很相似的。在C++中，使用 <tt>: public</tt> 代替Java中的<tt>extends</tt> 来表示继承关系 。 (C++ 也支持私有继承的概念，但是不太有用。)</p>
<p>默认情况下，C++中的函数不是动态绑定的。如果你需要某个函数实现动态绑定，需要使用<tt>virtual</tt>声明它为虚函数，例如：</p>
<p><tt>class Manager : public Employee</tt></p>
<p><tt>{ </tt></p>
<p><tt>public:</tt></p>
<p><tt>   Manager(string name, double salary, string dept);</tt></p>
<p><tt>   <strong>virtual</strong> void print() const;</tt></p>
<p><tt>private:</tt></p>
<p><tt>   string department;</tt></p>
<p><tt>};</tt></p>
<p>同Java一样，构造函数中调用父类的构造函数有特殊的语法。 Java使用关键字 <tt>super</tt>。C++中必须在子类的构造函数体外调用父类的构造函数。下面是一个例子：</p>
<p><tt>Manager::Manager(string name, double salary, string dept)</tt></p>
<p><tt><strong>: Employee(name, salary) </strong>/* 调用父类的构造函数 */</tt></p>
<p><tt>{  department = dept;</tt></p>
<p><tt>}</tt></p>
<p>Java 中在子类函数中调用父类的函数时也使用关键字 <tt>super</tt> 。而在C++中是使用父类的名称加上操作符       <tt>::</tt>表示，例如：</p>
<p><tt>void Manager::print() const</tt></p>
<p><tt>{  <strong>Employee::print(); </strong>/* 调用父类的函数       */</tt></p>
<p><tt>   cout &lt;&lt; department &lt;&lt; "\n";</tt></p>
<p><tt>}</tt></p>
<p>一个 C++ 对象变量只能存储特定类型的对象值。要想在C++中实现多态(polymorphism)，必须使用指针。一个 <tt>T*</tt> 指针可以指向类型为 <tt>T</tt> 或 <tt>T</tt> 的任意子类的对象，例如：</p>
<p><tt>Employee* e = new Manager("Morris, Melinda", 83000, "Finance");</tt></p>
<p>你可以将父类和不同子类的对象混合收集到一个元素均为指针的向量中，然后调用动态绑定的函数，如下所示：</p>
<p><tt>vector&lt;Employee*&gt; staff;</tt></p>
<p><tt>. . .</tt></p>
<p><tt>for (i = 0; i &lt; staff.size(); i++)</tt></p>
<p><tt>   staff[i]-&gt;print();</tt></p>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/48/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>C++中Reference与指针（Pointer）的使用对比</title>
		<link>http://www.prglab.com/blog/p/28</link>
		<comments>http://www.prglab.com/blog/p/28#comments</comments>
		<pubDate>Tue, 22 May 2007 06:45:21 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://prglab.com/blog/p/28</guid>
		<description><![CDATA[引用(reference)和指针(pointer)是学C++过程中最令人头疼的问题，常常不知道什么时候用哪个合适，又常常弄混。找到Dan Saks的这篇文章，讲的很清楚，强烈推荐，所以翻译一下供大家参考。
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;
以下译自Dan Saks的文章 References vs. Pointers 英文原文
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;
了解引用reference与指针pointer到底有什么不同可以帮助你决定什么时候该用reference，什么时候该用pointer。
在C++ 中，reference在很多方面与指针(pointer)具有同样的能力。虽然多数C++程序员对于何时使用reference何时使用pointer 都会有一些直觉，但总还是会有些时候搞不清楚。如果你想要建立一个关于使用reference使用的清晰有理的概念， 又有必要了解到底reference和pointer有什么不同。
深层含义
与pointer 类似，一个reference是一个对象(object)，可以用来间接指向另一个对象。一个reference的声明与pointer的声明的实质语法结构是相同的。不同的是，声明pointer的时候使用星号操作符 * ， 而声明reference的时候使用地址操作符 &#38; 。 例如，我们有:
int i = 3;
则有:
int *pi = &#38;i;
声明 pi 为一个指针类型的对象，并且是一个&#8221;指向int整型的指针&#8221;，它的初始值为对象i的地址。而另一方面：
int &#38;ri = i;
声明 ri为一个reference类型的对象，并且也是一个指向整型的reference，它指向的是i。 我们可以看到pointer和reference的声明有显著的不同，但这并不是决定何时使用哪一个的根据。决定的真正依据是当它们被用在表达式中时其显 示的不同决定了使用哪一个合适
Pointer 和reference的最大不同是：pointer必须使用一个星号操作符 * 来去掉reference （英文叫做dereference，我不知道这里怎样翻译这个词合适，姑且就叫“去参考”吧）而reference不需要任何操作符来去参考。 例如， 有了上面例子中的定义， 间接表达式 *pi 将 pi 去参考为指向i。相反， 表达式ri-不需要任何操作符-自动将ri去参考为指向i。因此， 使用指针p，我们需要用赋值语句:
*p = 4; 
将i的值变为4； 而使用reference ri，我们只需要直接写：
ri = 4; 
就可以同样将i的值变为4 。
这个显示的不同在当你为函数的参数类型和返回值类型选择是使用pointer还是reference的时候就会显著起来，尤其是对于重载操作符的函数。
下面使用一个针对列举类型（enumeration）的++操作符例子来说明上面这点。在C++中， 内置的++操作符对列举类型无效，例如， 对下面定义：
enum [...]]]></description>
			<content:encoded><![CDATA[<p>引用(reference)和指针(pointer)是学C++过程中最令人头疼的问题，常常不知道什么时候用哪个合适，又常常弄混。找到Dan Saks的这篇文章，讲的很清楚，强烈推荐，所以翻译一下供大家参考。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
以下译自Dan Saks的文章 References vs. Pointers <a href="http://www.embedded.com/story/OEG20010311S0024" target="_blank" title="References vs. Pointers">英文原文</a><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p><strong>了解引用reference与指针pointer到底有什么不同可以帮助你决定什么时候该用reference，什么时候该用pointer。</strong></p>
<p>在C++ 中，reference在很多方面与指针(pointer)具有同样的能力。虽然多数C++程序员对于何时使用reference何时使用pointer 都会有一些直觉，但总还是会有些时候搞不清楚。如果你想要建立一个关于使用reference使用的清晰有理的概念， 又有必要了解到底reference和pointer有什么不同。<span id="more-28"></span></p>
<h2>深层含义</h2>
<p>与pointer 类似，一个reference是一个对象(object)，可以用来间接指向另一个对象。一个reference的声明与pointer的声明的实质语法结构是相同的。不同的是，声明pointer的时候使用星号操作符 * ， 而声明reference的时候使用地址操作符 &amp; 。 例如，我们有:<br />
<strong>int i = 3;</strong><br />
则有:<br />
<strong>int *pi = &amp;i;</strong><br />
声明 pi 为一个指针类型的对象，并且是一个&#8221;指向int整型的指针&#8221;，它的初始值为对象i的地址。而另一方面：<br />
<strong>int &amp;ri = i;</strong><br />
声明 ri为一个reference类型的对象，并且也是一个指向整型的reference，它指向的是i。 我们可以看到pointer和reference的声明有显著的不同，但这并不是决定何时使用哪一个的根据。决定的真正依据是当它们被用在表达式中时其显 示的不同决定了使用哪一个合适</p>
<p>Pointer 和reference的最大不同是：pointer必须使用一个星号操作符 * 来去掉reference （<font color="#00008b">英文叫做dereference，我不知道这里怎样翻译这个词合适，姑且就叫“去参考”吧</font>）而reference不需要任何操作符来去参考。 例如， 有了上面例子中的定义， 间接表达式 *pi 将 pi 去参考为指向i。相反， 表达式ri-不需要任何操作符-自动将ri去参考为指向i。因此， 使用指针p，我们需要用赋值语句:<br />
<strong>*p = 4; </strong><br />
将i的值变为4； 而使用reference ri，我们只需要直接写：<br />
<strong>ri = 4; </strong><br />
就可以同样将i的值变为4 。</p>
<p>这个显示的不同在当你为函数的参数类型和返回值类型选择是使用pointer还是reference的时候就会显著起来，尤其是对于重载操作符的函数。</p>
<p>下面使用一个针对列举类型（enumeration）的++操作符例子来说明上面这点。在C++中， 内置的++操作符对列举类型无效，例如， 对下面定义：</p>
<blockquote><p>enum day{<br />
Sunday, Monday, &#8230;<br />
};<br />
day x;</p></blockquote>
<p>表达式 ++x 不能编译。如果想让它通过编译，必须要定义一个名为operator++的函数，接受day为参数，并且调用 ++x 必须改变x的值。因此， 仅声明一个函数 operator++ ， 以类型day为参数， 如下:<br />
<strong>day operator++(day d);</strong><br />
并<strong>不</strong>能够得到想要得效果。 这个函数通过值传递参数(pass by value)，这就意味着函数内看到的是参数的一个拷贝，而不是参数本身。为了使函数能够改变其操作数(operand)的值，它必须通过指针或reference来传递其操作数。</p>
<p>通过指针传递参数(passing by pointer)，函数定义如下:<br />
<strong>day *operator++(day *d);</strong><br />
它通过将增加后的值存储到*d里面来使函数改变日期(day)的值。但是，这样你就必须使用像表达式++&amp;x这样来调用这个操作符，这看起来不太对劲儿。</p>
<p>正确的方法是定义operator++以reference为参数类型，如下:</p>
<blockquote><p>day &amp;operator++(day &amp;d)<br />
{<br />
d = (day)(d + 1);<br />
return d;<br />
}</p></blockquote>
<p>使用这个函数， 表达式 ++x 才有正确的显示以及正确的操作。</p>
<p>Passing by reference不仅仅是写operator++较好的方法，而是唯一的方法。 C++在这里并没有给我们选择的余地。 像下面的声明:<br />
<strong>day *operator++(day *d);</strong><br />
是不能 通过编译的。每个重载的操作符函数必须或者是一个类的成员， 或者使用类型T、 T &amp; 或 T const &amp; 为参数类型，这里T是一个类(class)或列举(enumeration)类型。 也就是说，每一个重载操作符必须以类或列举类型为参数类型。指针，即使是指向一个类或列举类型对象的指针，也不可以用。C++ 不允许在重载操作符时重新定义内置操作符的含义，包括指针类型。因此，我们不可以定义：<br />
<strong>int operator++(int i); // 错误</strong><br />
因为它试图对int重新定义操作符 ++ 的含义。 我们也不可以定义：<br />
<strong>int *operator++(int *i); // 错误</strong><br />
因为它试图对 int * 重新定义操作符 ++ 的含义。</p>
<h2>References vs. const pointers</h2>
<p>C++ 中不允许定义&#8221;const reference&#8221;， 因为一个reference天生就是const。也就是说，一旦将一个reference绑定到一个对象，就无法再将它重新绑定到另一个不同的对象。在声 明一个reference之后没有写法可以将它重新绑定到另外一个对象。例如：<br />
<strong>int &amp;ri = i;</strong><br />
将 ri 绑定到 i 。然后下面的赋值:<br />
<strong>ri = j;</strong><br />
并不是把 ri 绑定到 j ，而是将 j 中的值赋给 ri 指向的对象，也就是赋给 i 。</p>
<p>简而言之，一个pointer在它的有生之年可以指向许多不同的对象，而一个reference只能够指向一个对象。有些人认为这才是 reference和 pointer最大的不同。我并不赞成。也许这是reference与pointer的一点不同， 但并不是reference和const pointer的不同。在强调一遍，一旦一个reference与一个对象绑定，就不能再将它改指向另外的东西。既然不能再绑定reference之后再 改变， 一个reference就必须在一出生就被绑定。否则这个reference就永远不能被绑定到任何东西，也就毫无用处了。</p>
<p>上一段的讨论也同样完全适用于常量指针（const pointer）。（注意，我这里说的是常量指针（const pointer）， 而不是指向常量的指针 &#8220;pointers to const&#8221;。) 例如，一个reference声明必须同时带有一个初始化赋值，如下所示：</p>
<blockquote><p>void f()<br />
{<br />
int &amp;r = i;<br />
&#8230;<br />
}</p></blockquote>
<p>省略这个初始化赋值将产生一个编译错误：</p>
<blockquote><p>void f()<br />
{<br />
int &amp;r; //错误<br />
&#8230;<br />
}</p></blockquote>
<p>一个常量指针的声明也同样必须带有一个初始化赋值，如下所示：</p>
<blockquote><p>void f()<br />
{<br />
int *const p = &amp;i;<br />
&#8230;<br />
}</p></blockquote>
<p>省略这个初始化赋值同样会出错：</p>
<blockquote><p>void f(){<br />
int *const p; // 错误<br />
&#8230;<br />
}</p></blockquote>
<p>在我看来， 不能够对reference二次绑定作为reference与pointer的不同。并不比常量指针和非常量指针的不同更为显著。</p>
<h2>Null references</h2>
<p>除了显示的不同，常量指针与reference还有一点非常不同，那就是，一个有效的reference必须指向一个对象；而一个指针不需要。一个指针，即使是一个常量指针， 都可以有空值。 一个空指针不指向任何东西。</p>
<p>这点不同就暗示当你想要确信一个参数必须指向一个对象的时候，应该使用reference作为参数类型。 例如，交换函数(swap function)，它接受两个int参数，并将两个参数的数值对调，如下所示：</p>
<blockquote><p>int i, j;<br />
swap(i, j);</p></blockquote>
<p>将原本在 i 中的值放到 j 中， 并将原本在 j 中的值放到 i 中。我们可以这样写这个函数：</p>
<blockquote><p>void swap(int *v1, int *v2)<br />
{<br />
int temp = *v1;<br />
*v1 = *v2;<br />
*v2 = temp;<br />
}</p></blockquote>
<p>这种定义下，函数要像这样被调用: <strong>swap(&amp;i, &amp;j);</strong></p>
<p>这个接口暗示其中一个或两个参数都有可能为空(null)。而这个暗示是误导的。例如，调用<strong><br />
swap(&amp;i, NULL);<br />
</strong>的后果很可能是不愉快的。</p>
<p>而像下面这样定义reference为参数：</p>
<blockquote><p>void swap(int &amp;v1, int &amp;v2)<br />
{<br />
int temp = v1;<br />
v1 = v2;<br />
v2 = temp;<br />
}</p></blockquote>
<p>清晰的表明了调用swap应该提供两个对象，它们的值将被交换。 并且这样定义的另一个好处是，在调用这个函数的时候，不需要使用那些&amp;符号，看起来更顺眼：<br />
<strong>swap(i, j);</strong></p>
<h2>更安全?</h2>
<p>有些人认为既然reference不能够为空，那么它应该比指针更安全。 我认为reference可能要安全一点，但不会安全很多。虽然一个有效的reference不能为空，但是无效的可以呀。实际上，在很多情况下程序有可 能产生无效的reference，而不只是空的reference。 例如，你可以定义一个reference，使它绑定到一个指针指向的对象，如下所示：</p>
<blockquote><p>int *p;<br />
&#8230;<br />
int &amp;r = *p;</p></blockquote>
<p>如果指针*p在reference定义时刚好为空，则这个reference为空。 从技术上来说，这个错误并不在于将reference绑定到一个空值，而是在于对一个空指针去参考。 对一个空指针去参考产生了一个不确定的操作，也就意味着很多事都可能发生，而且大部分都不是什么好事。很有可能当程序将reference r 绑定到*p (p所指向的对象)的时候，p实际上没有被去参考，甚至程序只是将p的值拷贝给实现r的指针。而程序将会继续执行下去直到错误在后面的运行中更为明显的表 现出来，产生不可预知的危害。</p>
<p>下面的函数展示了另外一种产生无效reference的方法：</p>
<blockquote><p>int &amp;f()<br />
{<br />
int i;<br />
&#8230;<br />
return i;<br />
}</p></blockquote>
<p>这个函数返回一个指向本地变量 i 的reference。然而当函数返回时，本地变量 i 的存储空间也就消失了。因此这个函数实际返回了一个指向被回收了的空间的reference。这个操作与返回一个指向本地变量的指针的后果相同。有些编译 器可以在编译时发现这个错误，但也很有可能不会发现。</p>
<p>我喜欢reference，也有很好的理由使用它们代替pointer。但如果你期望使用reference来使你的程序健壮性显著增强，那么你多半会失望的。</p>
<h2>参考资料：</h2>
<ol>
<li>Saks, Dan. &#8220;<a href="http://www.embedded.com/story/OEG20010221S0094">Introduction to References</a>,&#8221; <em>Embedded Systems Programming,</em> January 2001, p. 81.</li>
<li>Saks, Dan. &#8220;<a href="http://www.embedded.com/story/OEG20010222S0050">References and const</a>&#8220;, <em>Embedded Systems Programming</em> February 2001, p. 73.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/28/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
