⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter08.html

📁 Thinking in c++ 2nd edition,c++编程思想(第2版)
💻 HTML
📖 第 1 页 / 共 5 页
字号:

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>f5(&#160;)</B> returns a
non-<B>const</B> <B>X</B> object, while <B>f6(&#160;)</B> returns a <B>const
X</B> object. Only the non-<B>const</B> return value can be used as an lvalue.
Thus, it&#8217;s important to use <B>const </B>when returning an object by value
if you want to prevent its use as an lvalue.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The reason <B>const</B> has no meaning
when you&#8217;re returning a built-in type by value is that the compiler
already prevents it from being an lvalue (because it&#8217;s always a value, and
not a variable). Only when you&#8217;re returning objects of user-defined types
by value does it become an issue.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The function <B>f7(&#160;)</B> takes its
argument as a non-<B>const</B> <I>reference</I> (an additional way of handling
addresses in C++ and the subject of Chapter 11). This is effectively the same as
taking a non-<B>const</B> pointer; it&#8217;s just that the syntax is different.
The reason this won&#8217;t compile in C++ is because of the creation of a
temporary.</FONT><BR></P></DIV>
<A NAME="Heading262"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Temporaries</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Sometimes, during the evaluation of an
expression, the compiler must create <I>temporary
objects<A NAME="Index1517"></A><A NAME="Index1518"></A></I>. These are objects
like any other: they require storage and they must be constructed and destroyed.
The difference is that you never see them &#8211; the compiler is responsible
for deciding that they&#8217;re needed and the details of their existence. But
there is one thing about temporaries: they&#8217;re automatically
<A NAME="Index1519"></A><B>const</B>. Because you usually won&#8217;t be able to
get your hands on a temporary object, telling it to do something that will
change that temporary is almost certainly a mistake because you won&#8217;t be
able to use that information. By making all temporaries automatically
<B>const</B>, the compiler informs you when you make that
mistake.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the above example, <B>f5(&#160;)</B>
returns a non-<B>const</B> <B>X</B> object. But in the
expression:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>f7(f5());</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">the compiler must manufacture a temporary
object to hold the return value of <B>f5(&#160;)</B> so it can be passed to
<B>f7(&#160;)</B>. This would be fine if <B>f7(&#160;)</B> took its argument by
value; then the temporary would be copied into <B>f7(&#160;)</B> and it
wouldn&#8217;t matter what happened to the temporary <B>X</B>. However,
<B>f7(&#160;)</B> takes its argument <I>by reference</I>, which means in this
example takes the address of the temporary <B>X</B>. Since <B>f7(&#160;)</B>
doesn&#8217;t take its argument by <B>const</B> reference, it has permission to
modify the temporary object. But the compiler knows that the temporary will
vanish as soon as the expression evaluation is complete, and thus any
modifications you make to the temporary <B>X</B> will be lost. By making all
temporary objects automatically <B>const</B>, this situation causes a
compile-time <STRIKE>error </STRIKE><U>message </U>so you don&#8217;t get caught
by what would be a very difficult bug to find.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, notice the expressions that are
legal:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>  f5() = X(1);
  f5().modify();</PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although these pass muster for the
compiler, they are actually problematic. <B>f5(&#160;)</B> returns an <B>X</B>
object, and for the compiler to satisfy the above expressions it must create a
temporary to hold that return value. So in both expressions the temporary object
is being modified, and as soon as the expression is over the temporary is
cleaned up. As a result, the modifications are lost so this code is probably a
<A NAME="Index1520"></A><A NAME="Index1521"></A>bug &#8211; but the compiler
doesn&#8217;t tell you anything about it. Expressions like these are simple
enough for you to detect the problem, but when things get more complex
it&#8217;s possible for a bug to slip through these cracks.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The way the <B>const</B>ness of class
objects is preserved is shown later in the
chapter.</FONT><A NAME="_Toc312373915"></A><A NAME="_Toc472654889"></A><BR></P></DIV>
<A NAME="Heading263"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Passing and returning
addresses<BR><A NAME="Index1522"></A><A NAME="Index1523"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you pass or return an address (either
a pointer or a reference), it&#8217;s possible for the client programmer to take
it and modify the original value. If you make the pointer or reference a
<B>const</B>, you prevent this from happening, which may save you some grief. In
fact, whenever you&#8217;re passing an address into a function, you should make
it a <B>const</B> if at all possible. If you don&#8217;t, you&#8217;re excluding
the possibility of using that function with anything that is a
<B>const</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The choice of whether to return a pointer
or reference to a <B>const</B> depends on what you want to allow your client
programmer to do with it. Here&#8217;s an example that demonstrates the use of
<B>const</B> pointers as function arguments and return values:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C08:ConstPointer.cpp</font>
<font color=#009900>// Constant pointer arg/return</font>

<font color=#0000ff>void</font> t(<font color=#0000ff>int</font>*) {}

<font color=#0000ff>void</font> u(<font color=#0000ff>const</font> <font color=#0000ff>int</font>* cip) {
<font color=#009900>//!  *cip = 2; // Illegal -- modifies value</font>
  <font color=#0000ff>int</font> i = *cip; <font color=#009900>// OK -- copies value</font>
<font color=#009900>//!  int* ip2 = cip; // Illegal: non-const</font>
}

<font color=#0000ff>const</font> <font color=#0000ff>char</font>* v() {
  <font color=#009900>// Returns address of static character array:</font>
  <font color=#0000ff>return</font> <font color=#004488>"result of function v()"</font>;
}

<font color=#0000ff>const</font> <font color=#0000ff>int</font>* <font color=#0000ff>const</font> w() {
  <font color=#0000ff>static</font> <font color=#0000ff>int</font> i;
  <font color=#0000ff>return</font> &amp;i;
}

<font color=#0000ff>int</font> main() {
  <font color=#0000ff>int</font> x = 0;
  <font color=#0000ff>int</font>* ip = &amp;x;
  <font color=#0000ff>const</font> <font color=#0000ff>int</font>* cip = &amp;x;
  t(ip);  <font color=#009900>// OK</font>
<font color=#009900>//!  t(cip); // Not OK</font>
  u(ip);  <font color=#009900>// OK</font>
  u(cip); <font color=#009900>// Also OK</font>
<font color=#009900>//!  char* cp = v(); // Not OK</font>
  <font color=#0000ff>const</font> <font color=#0000ff>char</font>* ccp = v(); <font color=#009900>// OK</font>
<font color=#009900>//!  int* ip2 = w(); // Not OK</font>
  <font color=#0000ff>const</font> <font color=#0000ff>int</font>* <font color=#0000ff>const</font> ccip = w(); <font color=#009900>// OK</font>
  <font color=#0000ff>const</font> <font color=#0000ff>int</font>* cip2 = w(); <font color=#009900>// OK</font>
<font color=#009900>//!  *w() = 1; // Not OK</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>

<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The function <B>t(&#160;)</B> takes an
ordinary non-<B>const</B> pointer as an argument, and <B>u(&#160;)</B> takes a
<B>const</B> pointer. Inside <B>u(&#160;)</B> you can see that attempting to
modify the destination of the <B>const</B> pointer is illegal, but you can of
course copy the information out into a non-<B>const</B> variable. The compiler
also prevents you from creating a non-<B>const</B> pointer using the address
stored inside a <B>const</B> pointer.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The functions <B>v(&#160;)</B> and
<B>w(&#160;)</B> test return value
semantics<A NAME="Index1524"></A><A NAME="Index1525"></A>. <B>v(&#160;)</B>
returns a <B>const</B> <B>char*</B> that is created from a character array
literal. This statement actually produces the address of the character array
literal, after the compiler creates it and stores it in the static storage area.
As mentioned earlier, this character array is technically a constant, which is
properly expressed by the return value of <B>v(&#160;)</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The return value of <B>w(&#160;)</B>
requires that both the pointer and what it points to must be <B>const</B>. As
with <B>v(&#160;)</B>, the value returned by <B>w(&#160;)</B> is valid after the
function returns only because it is
<A NAME="Index1526"></A><A NAME="Index1527"></A><B>static</B>. You never want to
return pointers to local stack variables because they will be invalid after the
function returns and the stack is cleaned up. (Another common pointer you might
return is the address of storage allocated on the heap, which is still valid
after the function returns.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, the functions are
tested with various arguments. You can see that <B>t(&#160;)</B> will accept a
non-<B>const</B> pointer argument, but if you try to pass it a pointer to a
<B>const</B>, there&#8217;s no promise that <B>t(&#160;)</B> will leave the
pointer&#8217;s destination alone, so the compiler gives you an error message.
<B>u(&#160;)</B> takes a <B>const</B> pointer, so it will accept both types of
arguments. Thus, a function that takes a <B>const</B> pointer is more general
than one that does not.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As expected, the return value of
<B>v(&#160;)</B> can be assigned only to a pointer to a <B>const</B>. You would
also expect that the compiler refuses to assign the return value of
<B>w(&#160;)</B> to a non-<B>const</B> pointer, and accepts a <B>const int*
const</B>, but it might be a bit surprising to see that it also accepts a
<B>const int*</B>, which is not an exact match to the return type. Once again,
because the value (which is the address contained in the pointer) is being
copied, the promise that the original variable is untouched is automatically
kept. Thus, the second <B>const</B> in <B>const</B> <B>int* const</B> is only
meaningful when you try to use it as an lvalue, in which case the compiler
prevents you.</FONT><BR></P></DIV>
<A NAME="Heading264"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Standard argument passing<BR><A NAME="Index1528"></A></H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In C it&#8217;s very common to pass by
value, and when you want to pass an address your only choice is to use a
pointer</FONT><A NAME="fnB43" HREF="#fn43">[43]</A><FONT FACE="Georgia">.
However, neither of these approaches is preferred in C++. Instead, your first
choice when passing an argument is to pass by reference, and by <B>const</B>
<A NAME="Index1529"></A>reference at that. To the client programmer, the syntax
is identical to that of passing by value, so there&#8217;s no confusion about
pointers &#8211; they don&#8217;t even have to think about
<A NAME="Index1530"></A>pointers. For the creator of the function, passing an
address is virtually always more efficient than passing an entire class object,
and if you pass by <B>const</B> reference it means your function will not change
the destination of that address, so the effect from the client
programmer&#8217;s point of view is exactly the same as pass-by-value (only more
efficient).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because of the syntax of references (it
looks like pass-by-value to the caller) it&#8217;s possible to pass a
temporary<A NAME="Index1531"></A><A NAME="Index1532"></A> object to a function
that takes a <B>const </B>reference, whereas you can never pass a temporary
object to a function that takes a pointer &#8211; with a pointer, the address
must be explicitly taken. So passing by reference produces a new situation that
never occurs in C: a temporary, which is always <B>const</B>, can have its
<I>address</I> passed to a function. This is why, to allow temporaries to be
passed to functions by reference, the argument must be a <B>const</B>
reference<A NAME="Index1533"></A><A NAME="Index1534"></A>. The following example
demonstrates this:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C08:ConstTemporary.cpp</font>
<font color=#009900>// Temporaries are const</font>

<font color=#0000ff>class</font> X {};

X f() { <font color=#0000ff>return</font> X(); } <font color=#009900>// Return by value</font>

<font color=#0000ff>void</font> g1(X&amp;) {} <font color=#009900>// Pass by non-const reference</font>
<font color=#0000ff>void</font> g2(<font color=#0000ff>const</font> X&amp;) {} <font color=#009900>// Pass by const reference</font>

<font color=#0000ff>int</font> main() {
  <font color=#009900>// Error: const temporary created by f():</font>
<font color=#009900>//!  g1(f());</font>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -