📄 ch02.htm
字号:
by the C++ Standard, usually to allow efficient and simple compiler implementation on various platforms. Conversely, undefined behavior is always undesirable and should never occur.</p> <h3> <a name="Heading11">The One Definition Rule</a></h3> <p>A class, an enumeration, an inline function with external linkage, a class template, a nonstatic function template, a member function template, a static data member of a class template, or a template specialization for which some template parameters are not specified can be defined more than once in a program -- provided that each definition appears in a different translation unit, and provided that the definitions meet the requirements that are detailed in the following sections.</p> <h4> Token-by-Token Identity</h4> <p>Each definition must contain the same sequence of tokens. For example</p> <pre><tt> //file fisrt.cpp</tt><tt>inline int C::getVal () { return 5; }</tt><tt> //file sec.cpp</tt><tt>typedef int I;</tt><tt>inline I C::getVal () { return 5; } // violation of ODR, </tt><tt> // I and int are not identical tokens</tt></pre> <p>On the other hand, white spaces and comments are immaterial:</p> <pre><tt> //file fisrt.cpp</tt><tt>inline int C::getVal () { return 5; }</tt><tt> //file sec.cpp</tt><tt>inline int C::getVal () { /*complies with the ODR*/</tt><tt>return 5; }</tt></pre> <h4> Semantic Equivalence</h4> <p>Each token in the identical sequences of the separate definitions has the same semantic contents. For example</p> <pre><tt>//file first.cpp</tt><tt>typedef int I;</tt><tt>inline I C::getVal () { return 5; }</tt><tt>//file second.cpp</tt><tt>typedef unsigned int I;</tt><tt>inline I C::getVal () { return 5; } //error; different semantic content for I</tt></pre> <h3> <a name="Heading12">Linkage Types</a></h3> <p>A name that refers to an object, reference, type, function, template, namespace, or value that is declared in another scope is said to have <i>linkage</i>. The linkage can be either <i>external </i>or <i>internal</i>. Otherwise, the name has no linkage.</p> <h4> External Linkage</h4> <p>A name that can be referred to from other translation units or from other scopes of the translation unit in which it was defined has <i>external linkage</i>. Following are some examples:</p> <pre><tt>void g(int n) {} //g has external linkage</tt><tt>int glob; //glob has external linkage</tt><tt>extern const int E_MAX=1024; //E_MAX has external linkage</tt><tt>namespace N</tt><tt>{</tt><tt> int num; //N::num has external linkage</tt><tt> void func();//N::func has external linkage</tt><tt>}</tt><tt>class C {}; //the name C has external linkage</tt></pre> <h4> Internal Linkage</h4> <p>A name that can be referred to by names from other scopes in the translation unit in which it was declared, but not from other translation units, has <i>internal linkage</i>. Following are some examples:</p> <pre><tt>static void func() {} //func has internal linkage</tt><tt>union //members of a non-local anonymous union have internal linkage</tt><tt>{</tt><tt> int n;</tt><tt> void *p;</tt><tt>};</tt><tt>const int MAX=1024; //non-extern const variables have internal linkage</tt><tt>typedef int I; //typedefs have internal linkage</tt></pre> <h4> Names With No Linkage</h4> <p>A name that can only be referred to from the scope in which it is declared has no linkage. For example</p> <pre><tt>void f()</tt><tt>{</tt><tt> int a; //a has no linkage</tt><tt> class B {/**/}; //a local class has no linkage</tt><tt>}</tt></pre> <h3> <a name="Heading13">Side effect</a></h3> <p>A <i>side effect</i> is a change in the state of the execution environment. Modifying an object, accessing a <tt>volatile</tt> object, invoking a library I/O function, and calling a function that performs any of these operations are all side effects.</p> <h2> <a name="Heading14">Addenda</a></h2> <p>This part details the new features -- and extensions to existing features -- that have been adopted by the C++ Standard in recent years.</p> <h3> <a name="Heading15">New Typecast Operators</a></h3> <p>C++ still supports C-style cast, as in</p> <pre><tt>int i = (int) 7.333;</tt></pre> <p>Nonetheless, C-style cast notation is problematic for several reasons. First, the operator <tt>()</tt> is already used excessively in the language: in a function call, in precedence reordering of expressions, in operator overloading, and in other syntactic constructs. Second, C-style cast carries out different operations in different contexts -- so different that you can hardly tell which is which. It can perform an innocuous standard cast, such as converting an <tt>enum</tt> value to an <tt>int</tt>; but it can also cast two nonrelated types to one another. In addition, a C-style cast can be used to remove the <tt>const</tt> or <tt>volatile</tt> qualifiers of an object (and in earlier stages of C++, the language was capable of performing dynamic casts as well).</p> <p>Because of these multiple meanings, the C-style cast is opaque. Sometimes it is very difficult for a reader to clearly understand the intent of the code author who uses a C-style cast operation. Consider the following example:</p> <pre><tt>#include <iostream></tt><tt>using namespace std;</tt><tt>void display(const unsigned char *pstr)</tt><tt>{</tt><tt> cout<<pstr<<endl;</tt><tt>}</tt><tt>void func()</tt><tt>{</tt><tt> const char * p = "a message";</tt><tt> display( (unsigned char*) p); //signed to unsigned cast is required</tt><tt> // but const is also removed. was that</tt><tt> // intentional or a programmer's oversight?</tt><tt>}</tt></pre> <p>The new cast operators make the programmer's intention clearer and self-documenting. In addition, they enable the compiler to detect mistakes that cannot be detected with C-style cast. The new cast operators are intended to replace C-style cast; C++ programmers are encouraged to use them instead of C-style cast notation. These operators are detailed in the following sections.</p> <h4> static_cast</h4> <p><tt>static_cast</tt> <<cite>Type</cite><tt>> (</tt><cite>Expr</cite><tt>)</tt> performs well-behaved and reasonably well-behaved casts. One of its uses is to indicate explicitly a type conversion that is otherwise performed implicitly by the compiler. </p> <p>For example</p> <pre><tt>class Base{};</tt><tt>class Derived : public Base {};</tt><tt>void func( Derived * pd)</tt><tt>{</tt><tt> Base * pb = static_cast<Base *> (pd); //explicit</tt><tt>}</tt></pre> <p>A pointer to a derived object can be automatically converted to a pointer to a public base. The use of an explicit cast makes the programmer's intent clearer.</p> <p><tt>static_cast</tt> can be used to document user-defined conversion. For example</p> <pre><tt>class Integer</tt><tt>{</tt><tt> public: operator int ();</tt><tt>};</tt><tt>void func(Integer& integer)</tt><tt>{</tt><tt> int num = static_cast<int> (integer); //explicit</tt><tt>}</tt></pre> <p>Narrowing conversions of numeric types can also be performed explicitly by a <tt>static_cast</tt>. For example</p> <pre><tt>void func()</tt><tt>{</tt><tt> int num = 0;</tt><tt> short short_num = static_cast<short> (num);</tt><tt>}</tt></pre> <p>This conversion is somewhat riskier than the previous conversions. A <tt>short</tt> might not represent all the values that an <tt>int</tt> can hold; an explicit cast is used here, instead of an implicit conversion, to indicate that a type conversion is performed. Casting an integral value to an <tt>enum</tt> is also a dangerous operation because there is no guarantee that the value of an <tt>int</tt> can be represented in an <tt>enum</tt>. Note that in this case, an explicit cast is necessary:</p> <pre><tt>void func()</tt><tt>{</tt><tt> enum status {good, bad};</tt><tt> int num = 0;</tt><tt> status s = static_cast<status> (num);</tt><tt>}</tt></pre> <p>You can use <tt>static_cast</tt> to navigate through class hierarchies. Unlike <tt>dynamic_cast</tt>, however, it relies solely on the information that is available at compile time -- so don't use it instead of <tt>dynamic_cast</tt>. Using <tt>static_cast</tt> for this purpose is safer than using C-style cast because it does not perform conversions between nonrelated classes. For example</p> <pre><tt>class A{};</tt><tt>class B{};</tt><tt>A *pa;</tt><tt>B * pb = static_cast<B *> (pa); //error, pointers are not related</tt></pre> <h4> const_cast</h4> <p><tt>const_cast <T> (Expr)</tt> removes only the <tt>const</tt> or <tt>volatile</tt> qualifiers of <tt>Expr</tt> and converts them to type <tt>T</tt>. <tt>T</tt> must be the same type of <tt>Expr</tt>, except for the missing <tt>const</tt> or <tt>volatile</tt> attributes. For example</p> <pre><tt>#include <iostream></tt><tt>using namespace std;</tt><tt>void print(char *p) //parameter should have been declared as const; alas,</tt><tt>{</tt><tt> cout<<p;</tt><tt>}</tt><tt>void f()</tt><tt>{</tt><tt> const char msg[] = "Hello World\n";</tt><tt> char * p = const_cast<char *> (msg); //remove constness</tt><tt> print(p);</tt><tt>}</tt></pre> <p><tt>const_cast</tt> can also convert an object to a <tt>const</tt> or <tt>volatile</tt> one:</p> <pre><tt>void read(const volatile int * p);</tt><tt>int *p = new int; </tt><tt>read( const_cast<const volatile int *> (p) ); //explicit </tt></pre> <p>Note that the removal of the <tt>const</tt> qualifier of an object does not guarantee that its value can be modified; it only guarantees that it can be used in a context that requires a non-<tt>const</tt> object. So that you understand these limitations, the following sections examines <tt>const</tt> semantics in further detail.</p> <p><b>(f)<tt>const</tt> Semantics</b></p> <p>There are actually two types of <tt>const</tt>: <i>true</i> <tt>const</tt> and <i>contractual</i> <tt>const</tt>. A true <tt>const</tt> object is an lvalue that was originally defined as <tt>const</tt>. For example</p> <pre><tt>const int cn = 5; // true const</tt><tt>const std::string msg("press any key to continue"); // true const</tt></pre> <p>On the other hand, an object with contractual <tt>const</tt> quality is one that was defined without the <tt>const</tt> qualifier, but that is treated as though it were <tt>const</tt>. For example</p> <pre><tt>void ReadValue(const int& num)</tt><tt>{</tt><tt> cout<<num; // num may not be modified in ReadValue()</tt><tt>}</tt><tt>int main()</tt><tt>{</tt><tt> int n =0;</tt><tt> ReadValue(n); //contractual const, n is treated as if it were const</tt><tt>}</tt></pre> <p>When a true <tt>const</tt> variable is explicitly cast to a non-<tt>const</tt> variable, the result of an attempt to change its value is undefined. This is because an implementation can store true <tt>const</tt> data in the read-only
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -