<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>学无止境</title>
    <description>I'm a java programer, and I like cpp.</description>
    <link>http://stonelet.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>析构函数 管理指针成员</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/164479" style="color:red;">http://stonelet.javaeye.com/blog/164479</a>&nbsp;
          发表时间: 2008年02月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>析构函数就是这样的一个特殊函数，它可以完成所需的资源回收，作为类构造函数的补充撤销类对象</p><p>时会自动调用析构函数</p><p>&nbsp;</p><p>动态分配的对象只有在指向该对象的指针被删除时才撤销</p><p>&nbsp;</p><p>当对象的引用或指针超出作用域时，不会运行析构函数，只有删除指向动态分配对象的指针或实际对</p><p>象（而不是对象的引用）超出作用域时，才会运行析构函数</p><p>&nbsp;</p><p>撤销一个容器（不管是标准库容器还是内置数组）时，也会运行容器中的类类型元素的析构函数</p><p><br />容器中的元素总是按逆序撤销：首先撤销下标为 size() - 1 的元素，然后是下标为 size() - 2 的</p><p>元素&hellip;&hellip;直到最后撤销下标为 [0] 的元素</p><p>&nbsp;</p><p>析构函数通常用于释放在构造函数或在对象生命期内获取的资源</p><p><br />如果类需要析构函数，则它也需要赋值操作符和复制构造函数，这是一个有用的经验法则，这个规则</p><p>常称为三法则，指的是如果需要析构函数，则需要所有这三个复制控制成员</p><p>&nbsp;</p><p>析构函数并不仅限于用来释放资源，一般而言，析构函数可以执行任意操作，该操作是类设计者希望</p><p>在该类对象的使用完毕之后执行的</p><p>&nbsp;</p><p>与复制构造函数或赋值操作符不同，编译器总是会为我们合成一个析构函数</p><p>&nbsp;</p><p>合成析构函数按对象创建时的逆序撤销每个非 static 成员，因此，它按成员在类中声明次序的逆序</p><p>撤销成员，对于类类型的每个成员，合成析构函数调用该成员的析构函数来撤销对象</p><p>&nbsp;</p><p>撤销内置类型成员或复合类型的成员没什么影响，尤其是，合成析构函数并不删除指针成员所指向的</p><p>对象</p><p>&nbsp;</p><p>析构函数是个成员函数，它的名字是在类名字之前加上一个代字号（~），它没有返回值，没有形参</p><p>，因为不能指定任何形参，所以不能重载析构函数，虽然可以为一个类定义多个构造函数，但只能提</p><p>供一个析构函数，应用于类的所有对象</p><p>&nbsp;</p><p>析构函数与复制构造函数或赋值操作符之间的一个重要区别是，即使我们编写了自己的析构函数，合</p><p>成析构函数仍然运行</p><p>&nbsp;</p><p>包含指针的类需要特别注意复制控制，原因是复制指针时只复制指针中的地址，而不会复制指针指向</p><p>的对象</p><p>&nbsp;</p><p>大多数 C++ 类采用以下三种方法之一管理指针成员:<br />1. 指针成员采取常规指针型行为。这样的类具有指针的所有缺陷但无需特殊的复制控制<br />2. 类可以实现所谓的&ldquo;智能指针&rdquo;行为，指针所指向的对象是共享的，但类能够防止悬垂指针<br />3. 类采取值型行为，指针所指向的对象是唯一的，由每个类对象独立管理</p><p>&nbsp;</p><p>具有指针成员且使用默认合成复制构造函数的类具有普通指针的所有缺陷，尤其是，类本身无法避免</p><p>悬垂指针</p><p>&nbsp;</p><p>建议：管理指针成员<br />具有指针成员的对象一般需要定义复制控制成员，如果依赖合成版本，会给类的用户增加负担，用户</p><p>必须保证成员所指向的对象存在，只要还有对象指向该对象</p><p>&nbsp;</p><p>为了管理具有指针成员的类，必须定义三个复制控制成员：复制构造函数、赋值操作符和析构函数。</p><p>这些成员可以定义指针成员的指针型行为或值型行为</p><p>&nbsp;</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
using namespace std;

class Apple{
    int i;    
public:
    Apple(int n):i(n){}
    ~Apple(){
        cout &lt;&lt; &quot;Aapple No.&quot; &lt;&lt; i &lt;&lt; &quot; destructed&quot; &lt;&lt; endl;       
    }  
}; 


// class that has a pointer member that behaves like a plain pointer
class HasPtr {
public:
    // copy of the values we're given
    HasPtr(int *p, int i): ptr(p), val(i) { }
    
    // const members to return the value of the indicated data member
    int *get_ptr() const { return ptr; }
    int get_int() const { return val; }
    
    // non const members to change the indicated data member
    void set_ptr(int *p) { ptr = p; }
    void set_int(int i) { val = i; }
    
    // return or change the value pointed to, so ok for const objects
    int get_ptr_val() const { return *ptr; }
    void set_ptr_val(int val) const { *ptr = val; }

private:
    int *ptr;
    int val;
};


int main()
{
    Apple a(0);   // 
    
    {             // new scope
        Apple a1(1);
    }             //exit local scope; destructor called on a1
    
    Apple *a2 = 0;
    {
        a2 = new Apple(2);
    }             //thouth exit local scope ,destructor not called 
    cout &lt;&lt; &quot;Apple a2 still exist &quot; &lt;&lt; endl;
    delete a2;    //ok  destructor called on a2
    
    cout &lt;&lt; &quot;===========================&quot; &lt;&lt; endl; 
    Apple a3(3);
    Apple a4(4);
    Apple a5(5);
    {
          Apple as[3] = {a3,a4,a5};  
    }             //destruct order: a5 a4 a3
    
    int obj = 0;
    HasPtr ptr1(&amp;obj, 42); // int* member points to obj, val is 42
    HasPtr ptr2(ptr1);     // int* member points to obj, val is 42

    ptr1.set_int(0); // changes val member only in ptr1
    cout &lt;&lt; ptr2.get_int() &lt;&lt; endl;  // returns 42
    cout &lt;&lt; ptr1.get_int() &lt;&lt; endl;  // returns 0

    ptr1.set_ptr_val(42); // sets object to which both ptr1 and ptr2 point
    cout &lt;&lt; ptr2.get_ptr_val() &lt;&lt; endl;   // returns 42

    int *ip = new int(42); // dynamically allocated int initialized to 42
    HasPtr ptr(ip, 10);    // Has Ptr points to same object as ip does
    delete ip;             // object pointed to by ip is freed
    ptr.set_ptr_val(0);    // disaster: The object to which Has Ptr points was freed!
    cout &lt;&lt; ptr.get_ptr_val() &lt;&lt; endl;

    int i = 42;
    HasPtr p1(&amp;i, 42);
    HasPtr p2 = p1;
    cout &lt;&lt; p2.get_ptr_val() &lt;&lt; endl;
    p1.set_ptr_val(0);
    cout &lt;&lt; p2.get_ptr_val() &lt;&lt; endl;
     
    return 0;   
}
</pre><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/164479#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 23 Feb 2008 20:10:49 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/164479</link>
        <guid>http://stonelet.javaeye.com/blog/164479</guid>
      </item>
      <item>
        <title>复制构造函数</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/163223" style="color:red;">http://stonelet.javaeye.com/blog/163223</a>&nbsp;
          发表时间: 2008年02月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>只有单个形参，而且该形参是对本类类型对象的引用(常用 const 修饰)，这样的构造函数称为复制</p><p>构造函数</p><p>&nbsp;</p><p>复制构造函数可用于:<br />1. 根据另一个同类型的对象显式或隐式初始化一个对象<br />2. 复制一个对象，将它作为实参传给一个函数<br />3. 从函数返回时复制一个对象<br />4. 初始化顺序容器中的元素<br />5. 根据元素初始化式列表初始化数组元素</p><p>&nbsp;</p><p>当用于类类型对象时，初始化的复制形式和直接形式有所不同：直接初始化直接调用与实参匹配的构</p><p>造函数，复制初始化总是调用复制构造函数</p><p>&nbsp;</p><p>对于类类型对象，只有指定单个实参或显式创建一个临时对象用于复制时，才使用复制初始化</p><p>&nbsp;</p><p>当形参或返回值为类类型时，由复制构造函数进行复制</p><p>&nbsp;</p><p>如果没有为类类型数组提供元素初始化式，则将用默认构造函数初始化每个元素</p><p>&nbsp;</p><p>如果我们没有定义复制构造函数，编译器就会为我们合成一个</p><p>&nbsp;</p><p>与合成的默认构造函数不同，即使我们定义了其他构造函数，也会合成复制构造函数</p><p>&nbsp;</p><p>合成复制构造函数的行为是，执行逐个成员初始化，将新对象初始化为原对象的副本</p><p>&nbsp;</p><p>虽然一般不能复制数组，但如果一个类具有数组成员，则合成复制构造函数将复制数组，复制数组时</p><p>合成复制构造函数将复制数组的每一个元素</p><p><br />逐个成员初始化最简单的概念模型是，将合成复制构造函数看作这样一个构造函数：其中每个数据成</p><p>员在构造函数初始化列表中进行初始化</p><p>&nbsp;</p><p>虽然也可以定义接受非 const 引用的复制构造函数，但形参通常是一个 const 引用</p><p>&nbsp;</p><p>因为用于向函数传递对象和从函数返回对象，该构造函数一般不应设置为 explicit</p><p>&nbsp;</p><p>有些类必须对复制对象时发生的事情加以控制，这样的类经常有一个数据成员是指针，或者有成员表</p><p>示在构造函数中分配的其他资源，而另一些类在创建新对象时必须做一些特定工作，这两种情况下，</p><p>都必须定义复制构造函数</p><p>&nbsp;</p><p>为了防止复制，类必须显式声明其复制构造函数为 private</p><p>&nbsp;</p><p>类的友元和成员仍可以进行复制，如果想要连友元和成员中的复制也禁止，就可以声明一个</p><p>(private)复制构造函数但不对其定义</p><p>&nbsp;</p><p>一般来说，最好显式或隐式定义默认构造函数和复制构造函数，只有不存在其他构造函数时才合成默</p><p>认构造函数。如果定义了复制构造函数，也必须定义默认构造函数</p><p>&nbsp;</p><pre name="code" class="cpp"> #include &lt;iostream&gt;
 #include &lt;string&gt;
 #include &lt;fstream&gt;
 #include &lt;vector&gt;
 using namespace std;
 
struct NoName {
       NoName(): pstring(new string), i(0), d(0) { }
       NoName(const NoName&amp; noName): i(noName.i), d(noName.d) 
       {
               *pstring = *(noName.pstring);              
       }
private:
        string *pstring;
        int    i;
        double d;
};

class Foo {
     public:
        Foo();           // default constructor
        Foo(const Foo&amp;); // copy constructor
        // ...
};

int main()
{
    string null_book = &quot;9-999-99999-9&quot;; // copy-initialization
    string dots(10, '.');               // direct-initialization
    
    string empty_copy = string();       // copy-initialization
    string empty_direct;                // direct-initialization

    ifstream file1(&quot;filename&quot;); // ok: direct initialization
    //ifstream file2 = &quot;filename&quot;;  error: copy constructor is private
    
    // default string constructor and five string copy constructors invoked
    vector&lt;string&gt; svec(5);

    return 0;
}
</pre><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/163223#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 19 Feb 2008 00:36:51 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/163223</link>
        <guid>http://stonelet.javaeye.com/blog/163223</guid>
      </item>
      <item>
        <title>友元 static 类成员</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/162854" style="color:red;">http://stonelet.javaeye.com/blog/162854</a>&nbsp;
          发表时间: 2008年02月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类</p><p>&nbsp;</p><p>友元的声明以关键字 friend 开始，它只能出现在类定义的内部</p><p>&nbsp;</p><p>友元声明可以出现在类中的任何地方：友元不是授予友元关系的那个类的成员，所以它们不受声明出现部分的访问控制影响</p><p><br />通常，将友元声明成组地放在类定义的开始或结尾是个好主意</p><p>&nbsp;</p><p>友元可以是普通的非成员函数，或前面定义的其他类的成员函数，或整个类</p><p>&nbsp;</p><p>将一个类设为友元，友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员</p><p>&nbsp;</p><p>一般地讲，必须先定义包含成员函数的类，才能将成员函数设为友元;另一方面，不必预先声明类和非成员函数来将它们设为友元</p><p>&nbsp;</p><p>友元声明将已命名的类或非成员函数引入到外围作用域中，此外，友元函数可以在类的内部定义，该函数的作用域扩展到包围该类定义的作用域</p><p><br />用友元引入的类名和函数（定义或声明），可以像预先声明的一样使用</p><p>&nbsp;</p><p>类必须将重载函数集中每一个希望设为友元的函数都声明为友元</p><p><br />static 类成员</p><p>&nbsp;</p><p>static 数据成员独立于该类的任意对象而存在；每个 static 数据成员是与类关联的对象，并不与该类的对象相关联</p><p>&nbsp;</p><p>static 成员函数没有 this 形参，它可以直接访问所属类的 static 成员，但不能直接使用非 static 成员</p><p><br />使用类的 static 成员的优点<br />1. static 成员的名字是在类的作用域中，因此可以避免与其他类的成员或全局对象名字冲突<br />2. 可以实施封装。static 成员可以是私有成员，而全局对象不可以<br />3. 通过阅读程序容易看出 static 成员是与特定类关联的，可清晰地显示程序员的意图</p><p>在成员声明前加上关键字 static 将成员设为 static，static 成员遵循正常的公有/私有访问规则</p><p>&nbsp;</p><p>可以通过作用域操作符从类直接调用 static 成员，或者通过对象、引用或指向该类类型对象的指针间接调用类的 static 成员</p><p>&nbsp;</p><p>类成员函数可以不用作用域操作符来引用类的 static 成员</p><p>&nbsp;</p><p>在类的外部定义 static 成员时，无须重复指定 static 保留字，该保留字只出现在类定义体内部的声明处</p><p>&nbsp;</p><p>static 成员函数不能被声明为 const</p><p>&nbsp;</p><p>static 成员函数也不能被声明为虚函数</p><p>&nbsp;</p><p>static 数据成员必须在类定义体的外部定义(正好一次)</p><p>&nbsp;</p><p>不像普通数据成员，static 成员不是通过类构造函数进行初始化，而是应该在定义时进行初始化</p><p>&nbsp;</p><p>保证对象正好定义一次的最好办法，就是将 static 数据成员的定义放在包含类非内联成员函数定义的文件中</p><p>&nbsp;</p><p>像使用任意的类成员一样，在类定义体外部引用类的 static 成员时，必须指定成员是在哪个类中定义的，然而，static 关键字只能用于类定义体内部的声明中，定义不能标示为 static</p><p>&nbsp;</p><p>只要初始化式是一个常量表达式，整型 const static 数据成员就可以在类的定义体中进行初始化</p><p>&nbsp;</p><p>const static 数据成员在类的定义体中初始化时，该数据成员仍必须在类的定义体之外进行定义，在类内部提供初始化式时，成员的定义不必再指定初始值</p><p><br />static 成员不是类对象的组成部分，独立于任何对象而存在</p><p>&nbsp;</p><p>static 数据成员的类型可以是该成员所属的类类型，非 static 成员被限定声明为其自身类对象的指针或引用</p><p>&nbsp;</p><p>static 数据成员可用作默认实参</p><p><br />非 static 数据成员不能用作默认实参，因为它的值不能独立于所属的对象而使用<br />ISO C++ forbids in-class initialization of non-const static member 'somemember' </p><p>&nbsp;</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

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
};

class Y {
      //todo...      
};

class Account {
public:
    // interface functions here
    void applyint() { amount += amount * interestRate; }
    static double rate() { return interestRate; }
    static void rate(double); //set interestRate
private:
    string owner;
    double amount;
    static const int period = 30; //ok!! period is const static data member of integral type 
    static double interestRate;
    static double initRate();
};
double Account::initRate()
{
     return 0.05;     
}
void Account::rate(double newRate)
{ 
     interestRate = newRate;
}
// define and initialize static class member
//interestRate is in the scope of the class 
//and hence has access to the private member initRate() of the class 
double Account::interestRate = initRate();

// definition of static member with no initializer;
// the initial value is specified inside the class definition
const int Account::period;

class Bar {
public:
 // ...
private:
     static Bar mem1; // ok
     Bar *mem2;       // ok
     // Bar mem3;       error
};

class Screen {
public:
    // bkground refers to the static member
    // declared later in the class definition
    Screen&amp; clear(char = bkground);
private:
    static const char bkground = '#';
};

     
int main()
{  
    
    Account ac1;
    Account *ac2 = &amp;ac1;
    // equivalent ways to call the static member rate function
    double rate;
    rate = ac1.rate();        // through an Account object or reference
    cout &lt;&lt; &quot;ac1.rate() --&gt; &quot; &lt;&lt; rate &lt;&lt; endl;
    rate = ac2-&gt;rate();       // through a pointer to an Account object
    cout &lt;&lt; &quot;ac2-&gt;rate() --&gt; &quot; &lt;&lt; rate &lt;&lt; endl;
    rate = Account::rate();   // directly from the class using the scope operator
    cout &lt;&lt; &quot;Account::rate() --&gt; &quot; &lt;&lt; rate &lt;&lt; endl;
     
    return 0;
}
</pre><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/162854#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 17 Feb 2008 21:09:40 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/162854</link>
        <guid>http://stonelet.javaeye.com/blog/162854</guid>
      </item>
      <item>
        <title>名字查找 构造函数 explicit</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/162679" style="color:red;">http://stonelet.javaeye.com/blog/162679</a>&nbsp;
          发表时间: 2008年02月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>每个类都定义了自己的新作用域和唯一的类型，两个不同的类具有两个的类作用域</p><p>&nbsp;</p><p>即使两个类具有完全相同的成员列表，它们也是不同的类型，每个类的成员不同于任何其他类（或任何其他作用域）的成员</p><p>&nbsp;</p><p>在类作用域之外，成员只能通过对象或指针分别使用成员访问操作符 . 或 -&gt; 来访问</p><p>.操作符左边的操作数是一个类对象</p><p>-&gt;操作符左边的操作数是指向类对象的指针</p><p>&nbsp;</p><p>形参表和函数体处于类作用域中<br />在定义于类外部的成员函数中，形参表和成员函数体都出现在成员名之后，这些都是在类作用域中定义，所以可以不用限定而引用其他成员</p><p>函数返回类型不一定在类作用域中</p><p><br />如果函数在类定义体之外定义，则用于返回类型的名字在类作用域之外</p><p>&nbsp;</p><p>一般名字查找<br />1. 在使用该名字的块中查找名字的声明，只考虑在该项使用之前声明的名字<br />2. 如果找不到该名字，则在包围的作用域中查找<br />3. 如果找不到任何声明，则程序出错，在 C++ 程序中，所有名字必须在使用之前声明</p><p>&nbsp;</p><p>类定义实际上是在两个阶段中处理：<br />1. 首先，编译成员声明<br />2. 只有在所有成员出现之后，才编译它们的定义本身</p><p>&nbsp;</p><p>类成员声明的名字查找<br />1. 检查出现在名字使用之前的类成员的声明<br />2. 如果第 1 步查找不成功，则检查包含类定义的作用域中出现的声明以及出现在类定义之前的声明</p><p>必须在类中先定义类型名字，才能将它们用作数据成员的类型，或者成员函数的返回类型或形参类型</p><p>一旦一个名字被用作类型名，该名字就不能被重复定义</p><p>类成员定义中的名字查找</p><p><br />1. 首先检查成员函数局部作用域中的声明<br />2. 如果在成员函数中找不到该名字的声明，则检查对所有类成员的声明<br />3. 如果在类中找不到该名字的声明，则检查在此成员函数定义之前的作用域中出现的声明</p><p>&nbsp;</p><p>如果类的成员被屏蔽了，可以通过用类名来限定成员名或显式使用 this 指针来使用它</p><p><br />构造函数</p><p>&nbsp;</p><p>构造函数不能是const 的，不管对象是否为 const，都用一个构造函数来初始化化该对象</p><p>&nbsp;</p><p>构造函数初始化列表以一个冒号开始，接着是一个以逗号分隔的数据成员列表，每个数据成员后面跟一个放在圆括号中的初始化式</p><p>&nbsp;</p><p>从概念上讲，可以认为构造函数分两个阶段执行:<br />1. 初始化阶段<br />2. 普通的计算阶段 计算阶段由构造函数函数体中的所有语句组成</p><p>不管成员是否在构造函数初始化列表中显式初始化，类类型的数据成员总是在初始化阶段初始化，初始化发生在计算阶段开始之前</p><p>&nbsp;</p><p>在构造函数初始化列表中没有显式提及的每个成员，使用与初始化变量相同的规则来进行初始化：运行该类型的默认构造函数，来初始化类类型的数据成员；内置或复合类型的成员的初始值依赖于对象的作用域：在局部作用域中这些成员不被初始化，而在全局作用域中它们被初始化为 0</p><p>&nbsp;</p><p>有些成员必须在构造函数初始化列表中进行初始化，对于这样的成员，在构造函数函数体中对它们赋值不起作用</p><p>&nbsp;</p><p>对非类类型的数据成员进行赋值或使用初始化式在结果和性能上都是等价的</p><p>&nbsp;</p><p>以初始化 const 对象或引用类型的对象，但不能对它们赋值，在开始执行构造函数的函数体之前，要完成初始化</p><p>初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中</p><p>&nbsp;</p><p>必须对任何 const 或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式</p><p>&nbsp;</p><p>构造函数初始化列表仅指定用于初始化成员的值，并不指定这些初始化执行的次序，成员被初始化的次序就是定义成员的次序</p><p>&nbsp;</p><p>初始化的次序常常无关紧要，然而，如果一个成员是根据其他成员而初始化，则成员初始化的次序是至关重要的</p><p>&nbsp;</p><p>按照与成员声明一致的次序编写构造函数初始化列表是个好主意，此外，尽可能避免使用成员来初始化其他成员</p><p>一般情况下，通过（重复）使用构造函数的形参而不是使用对象的数据成员，可以避免由初始化式的执行次序而引起的任何问题</p><p>&nbsp;</p><p>初始化式可以是任意表达式</p><p>&nbsp;</p><p>只有当一个类没有定义构造函数时，编译器才会自动生成一个默认构造函数</p><p>&nbsp;</p><p>如果类包含内置或复合类型的成员，则该类不应该依赖于合成的默认构造函数，它应该定义自己的构造函数来初始化这些成员</p><p>&nbsp;</p><p>类通常应定义一个默认构造函数</p><p>&nbsp;</p><p>实际上，如果定义了其他构造函数，则提供一个默认构造函数几乎总是对的，通常，在默认构造函数中给成员提供的初始值应该指出该对象是&ldquo;空&rdquo;的</p><p>&nbsp;</p><p>可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换</p><p>&nbsp;</p><p>可以通过将构造函数声明为 explicit，来防止在需要隐式转换的上下文中使用构造函数</p><p>&nbsp;</p><p>explicit 关键字只能用于类内部的构造函数声明上，在类的定义体外部所做的定义上不再重复它</p><p>&nbsp;</p><p>当构造函数被声明 explicit 时，编译器将不使用它作为转换操作符</p><p>&nbsp;</p><p>通常，除非有明显的理由想要定义隐式转换，否则，单形参构造函数应该为 explicit，将构造函数设置为 explicit 可以避免错误，并且当转换有用时，用户可以显式地构造对象</p><p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

class First {
public:
    int memi;
    double memd;
};

class Second {
public:
    int memi;
    double memd;
};


typedef double Money;
class Account {
public:
    Account(){}
    //explicit Account(Money money):bal(money){}
    Account(Money money):bal(money){}
    Money balance() { return bal; } // uses global definition of Money
    bool equals(Account a) const {
        return bal - a.balance();
    }
private:
 //typedef long double Money; error: cannot change meaning of Money
    Money bal;
 // ...
};

 
// Note: This code is for illustration purposes only and reflects bad practice
// It is a bad idea to use the same name for a parameter and a member
int height;
class Screen {
public:
    void dummy_fcn_1(int height) {
         cursor = width * height; // which height? The parameter
    }
     
    // bad practice: Don't hide names that are needed from surrounding scopes
    void dummy_fcn_2(int height) {
         cursor = width * ::height;// which height? The global one
    }
    //Screen() const{} error! constructors may not be `const' 
private:
    int cursor;
    int height, width;
};


class ConstRef {
 public:
     ConstRef(int ii);
 private:
     int i;
     const int ci;
     int &amp;ri;
};
 // no explicit constructor initializer: error ri is uninitialized
 ConstRef::ConstRef(int ii): ci(ii),ri(ii) {             
      // assignments
      i = ii;   // ok
      //ci = ii;   error: cannot assign to a const
      //ri = i;    assigns to ri which was not bound to an object
 }

class X {
     int i;
     int j;
 public:
     // run-time error: i is initialized before j
     X(int val): j(val), i(j) { 
           cout &lt;&lt; &quot;i = &quot; &lt;&lt; i &lt;&lt; endl;      
     }
 };


int main()
{    
    First obj1;
    //Second obj2 = obj1;e rror: obj1 and obj2 have different types

    X x(2);   // i = ? 
    
    Account a = Account(); //ok: create an unnamed, empty Account use to initialize a
    Account a1(98.1);
    bool b = a1.equals(99.9); //Implicit Class-Type Conversions if explicit this will be an error
    cout &lt;&lt; boolalpha &lt;&lt; b &lt;&lt; endl;

    return 0;
}
</pre><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/162679#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 16 Feb 2008 20:50:51 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/162679</link>
        <guid>http://stonelet.javaeye.com/blog/162679</guid>
      </item>
      <item>
        <title>成员函数 this指针 可变数据成员</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/162570" style="color:red;">http://stonelet.javaeye.com/blog/162570</a>&nbsp;
          发表时间: 2008年02月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>类的成员函数可以访问该类的 private 成员</p><p>&nbsp;</p><p>每个成员函数(除了static 成员函数外)都有一个额外的、隐含的形参 this，在调用成员函数时，形</p><p>参 this 初始化为调用函数的对象的地址</p><p>&nbsp;</p><p>使用 const 的函数称为常量成员函数，由于this 是指向 const 对象的指针，const 成员函数不能</p><p>修改调用该函数的对象</p><p>&nbsp;</p><p>const 对象、指向 const 对象的指针或引用只能用于调用其 const 成员函数，如果尝试用它们来调</p><p>用非 const 成员函数，则是错误的</p><p>&nbsp;</p><p>在成员函数中，不必显式地使用 this 指针来访问被调用函数所属对象的成员，对这个类的成员的任</p><p>何没有前缀的引用，都被假定为通过指针 this 实现的引用</p><p>&nbsp;</p><p>由于 this 指针是隐式定义的，因此不需要在函数的形参表中包含 this 指针，实际上，这样做也是</p><p>非法的</p><p>&nbsp;</p><p>在类的定义外面定义成员函数必须指明它们是类的成员，使用作用域操作符 ::</p><p>&nbsp;</p><p>对于成员函数，函数声明必须与其定义一致，如果函数被声明为 const 成员函数，那么函数定义时</p><p>形参表后面也必须有 const</p><p>&nbsp;</p><p>构造函数是特殊的成员函数，与其他成员函数不同，构造函数和类同名，而且没有返回类型</p><p>&nbsp;</p><p>一个类可以有多个构造函数，每个构造函数必须有与其他构造函数不同数目或类型的形参</p><p>&nbsp;</p><p>构造函数通常应确保其每个数据成员都完成了初始化</p><p>&nbsp;</p><p>构造函数也必须在类中声明，但是可以在类中或类外定义</p><p>&nbsp;</p><p>在冒号和花括号之间的代码称为构造函数的初始化列表，为类的一个或多个数据成员指定初值，它跟</p><p>在构造函数的形参表之后，以冒号开关</p><p>&nbsp;</p><p>构造函数的初始化式是一系列成员名，每个成员后面是括在圆括号中的初始值，多个成员的初始化用</p><p>逗号分隔</p><p>&nbsp;</p><p>如果没有为一个类显式定义任何构造函数，编译器将自动为这个类生成默认构造函数</p><p>&nbsp;</p><p>合成的默认构造函数不会自动初始化内置类型的成员</p><p>&nbsp;</p><p>合成的默认构造函数一般适用于仅包含类类型成员的类，而对于含有内置类型或复合类型成员的类，</p><p>则通常应该定义他们自己的默认构造函数初始化这些成员</p><p>&nbsp;</p><p>可以通过对 this 指针解引用来访问 this 指向的对象</p><p>&nbsp;</p><p>在普通的非 const 成员函数中，this 的类型是一个指向类类型的 const 指针</p><p>&nbsp;</p><p>在 const 成员函数中，this 的类型是一个指向 const 类类型对象的 const 指针</p><p>&nbsp;</p><p>不能从 const 成员函数返回指向类对象的普通引用，const 成员函数只能返回 *this 作为一个 </p><p>const 引用</p><p>&nbsp;</p><p>基于成员函数是否为 const，可以重载一个成员函数；同样地，基于一个指针形参是否指向 const，</p><p>可以重载一个函数，const 对象只能使用 const 成员，非 const 对象可以使用任一成员，但非 </p><p>const 版本是一个更好的匹配</p><p>&nbsp;</p><p>有时(但不是很经常)，我们希望类的数据成员(甚至在 const 成员函数内)可以修改，这可以通过将</p><p>它们声明为 mutable 来实现</p><p>&nbsp;</p><p>可变数据成员(mutable data member)永远都不能为 const，甚至当它是 const 对象的成员时也如此<br />const 成员函数可以改变 mutable 成员</p><p>&nbsp;</p><p>要将数据成员声明为可变的，必须将关键字 mutable 放在成员声明之前</p><p>&nbsp;</p><pre name="code" class="cpp">#include &lt;iostream&gt;
using namespace std;

class Screen {
private:
    char c;
    mutable int count;
    // single function to do the work of displaying a Screen,
    // will be called by the display operations
    void do_display(ostream &amp;os) const
    { 
     os &lt;&lt; &quot;count : &quot; &lt;&lt; ++count &lt;&lt; endl;
     os &lt;&lt; c &lt;&lt; endl;; 
    }
public:
    // interface member functions
    // display overloaded on whether the object is const or not
    Screen&amp; display(ostream &amp;os)
    { 
            os &lt;&lt; &quot;no const ...&quot; &lt;&lt; endl;
            do_display(os); 
            return *this; 
    }
    const Screen&amp; display(ostream &amp;os) const
    { 
          os &lt;&lt; &quot;const...&quot; &lt;&lt; endl;
          do_display(os); 
          return *this; 
    }
    Screen():c(' '),count(0) {}
    Screen(char ch):c(ch),count(0) {}
};

int main()
{    
    Screen sa('a');
    const Screen sb('b');
    sa.display(cout); // calls nonconst version
    sb.display(cout); // calls const version count 1
    sb.display(cout); // count 2
    
    return 0;
}
</pre><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/162570#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 16 Feb 2008 00:11:56 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/162570</link>
        <guid>http://stonelet.javaeye.com/blog/162570</guid>
      </item>
      <item>
        <title>类class（一） </title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/162363" style="color:red;">http://stonelet.javaeye.com/blog/162363</a>&nbsp;
          发表时间: 2008年02月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>最简单地说，类就是定义了一个新的类型和一个新作用域</p><p>每个类可以没有成员，也可以定义多个成员，成员可以是数据、函数或类型别名</p><p>一个类可以包含若干公有的、私有的和受保护的部分</p><p>创建一个类类型的对象时，编译器会自动使用一个构造函数来初始化该对象</p><p>构造函数一般就使用一个构造函数初始化列表来初始化对象的数据成员</p><p>在类内部，声明成员函数是必需的，而定义成员函数则是可选的，在类内部定义的函数默认为 inline</p><p>在类外部定义的成员函数必须指明它们是在类的作用域中</p><p>成员函数有一个附加的隐含实参，将函数绑定到调用函数的对象</p><p>将关键字 const 加在形参表之后，就可以将成员函数声明为常量</p><p>const 成员不能改变其所操作的对象的数据成员，const 必须同时出现在声明和定义中，若只出现在其中</p><p>一处，就会出现一个编译时错误</p><p>在 C++ 中，使用访问标号来定义类的抽象接口和实施封装</p><p>程序的所有部分都可以访问带有 public 标号的成员。类型的数据抽象视图由其 public 成员定义<br />使用类的代码不可以访问带有 private 标号的成员。private 封装了类型的实现细节</p><p>可以在任意的访问标号出现之前定义类成员，如果类是用 struct 关键字定义的，则在第一个访问标号之</p><p>前的成员是公有的；如果类是用 class 关键字是定义的，则这些成员是私有的</p><p>除了定义数据和函数成员之外，类还可以定义自己的局部类型名字</p><p>类所定义的类型名遵循任何其他成员的标准访问控制</p><p>成员函数可被重载，只能重载本类的其他成员函数</p><p>在类内部定义的成员函数，将自动作为 inline 处理</p><p>可以显式地将成员函数声明为 inline</p><p>可以在类定义体内部指定一个成员为inline，作为其声明的一部分，也可以在类定义外部的函数定义上指</p><p>定 inline，在声明和定义处指定 inline 都是合法的</p><p>像其他 inline 一样，inline 成员函数的定义必须在调用该函数的每个源文件中是可见的，不在类定义</p><p>体内定义的 inline 成员函数，其定义通常应放在有类定义的同一头文件中</p><p>在一个给定的源文件中，一个类只能被定义一次，如果在多个文件中定义一个类，那么每个文件中的定义</p><p>必须是完全相同的</p><p>可以声明一个类而不定义它：<br />class Screen; // declaration of the Screen class<br />这个声明，有时称为前向声明(forward declaraton)，类的前向声明一般用来编写相互依赖的类</p><p><br />不完全类型(incomplete type)只能以有限方式使用：不能定义该类型的对象，不完全类型只能用于定义</p><p>指向该类型的指针及引用，或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数</p><p>在创建类的对象之前，必须完整地定义该类</p><p>类不能具有自身类型的数据成员</p><p>类的数据成员可以是指向自身类型的指针或引用</p><p>只有当类定义已经在前面出现过，数据成员才能被指定为该类类型，如果该类型是不完全类型，那么数据</p><p>成员只能是指向该类类型的指针或引用</p><p>定义对象时，将为其分配存储空间，但(一般而言)定义类型时不进行存储分配</p><p>定义了一个类类型之后，可以按以下两种方式使用：<br />1.将类的名字直接用作类型名<br />2.指定关键字 class 或 struct，后面跟着类的名字</p><p>类的定义分号结束，分号是必需的，因为在类定义之后可以接一个对象定义列表</p><p>通常，将对象定义成类定义的一部分是个坏主意</p><pre name="code" class="cpp">#include &lt;string&gt;
#include &lt;iostream&gt;
using namespace std;

class Screen {
public:
     typedef string::size_type index;
     // implicitly inline when defined inside the class declaration
     char get() const { return contents[cursor]; }
     // explicitly declared as inline; will be defined outside the class declaration
     inline char get(index ht, index wd) const;
     // inline not specified in class declaration, but can be defined inline later
     index get_cursor() const;
     // constructor with height and width
     Screen():height(0),width(0){}
     Screen(index h,index w):height(h),width(w) {
          cout &lt;&lt;  height &lt;&lt; width &lt;&lt; endl;         
     }
private:
    string contents;
    index cursor;
    index height, width;
};
// inline declared in the class declaration; no need to repeat on the definition
char Screen::get(index r, index c) const
{
    index row = r * width;    // compute the row location
    return contents[row + c]; // offset by c to fetch specified character
}
// not declared as inline in the class declaration, but ok to make inline in definition
inline Screen::index Screen::get_cursor() const
{
    return cursor;
}

class LinkScreen {
    Screen window;
    LinkScreen *next;
    LinkScreen *prev;
};

class Y;  //forward declaraton 
class X{
      //Y y      error!
      Y* y;      
};

class Y{
  X x;    
};

int main()
{
    Screen s1;
    class Screen s2;  // inherited from C and is also valid in C++
    struct Screen s3; // inherited from C and is also valid in C++
    
    return 0;
}
</pre><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/162363#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 14 Feb 2008 23:30:29 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/162363</link>
        <guid>http://stonelet.javaeye.com/blog/162363</guid>
      </item>
      <item>
        <title>输入输出IO</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/161982" style="color:red;">http://stonelet.javaeye.com/blog/161982</a>&nbsp;
          发表时间: 2008年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>每一个 IO 头文件都定义了 char 和 wchar_t 类型的类和标准输入／输出对象。</p><p>IO 对象不可复制或赋值</p><p>由于流对象不能复制，因此不能存储在 vector（或其他）容器中。</p><p>形参或返回类型也不能为流类型。如果需要传递或返回 IO 对象，则必须传递或返回指向该对象的指针或引用</p><p>对 IO 对象的读写会改变它的状态，因此引用必须是非 const 的。</p><p>检测流是否用的最简单的方法是检查其真值</p><p>所有流对象都包含一个条件状态成员，该成员由 setstate 和 clear 操作管理。这个状态成员为 iostate 类型，这是由各个 iostream 类分别定义的机器相关的整型。</p><p>每个 IO 类还定义了三个 iostate 类型的常量值，分别表示特定的位模式:<br />1. badbit 标志着系统级的故障，如无法恢复的读写错误<br />2. failbit 标志可恢复的错误<br />3. eofbit 是在遇到文件结束符时设置的，此时同时还设置了 failbit</p><p>流的状态由 bad、fail、eof 和 good 操作提示。如果 bad、fail 或者 eof 中的任意一个为 true，则检查流本身将显示该流处于错误状态。类似地，如果这三个条件没有一个为 true，则 good 操作将返回 true。</p><p>clear 操作将条件重设为有效状态。</p><p>使用 setstate 操作可打开某个指定的条件，用于表示某个问题的发生。除了添加的标记状态，setstate 将保留其他已存在的状态变量不变。</p><p>rdstate 成员函数返回一个 iostate 类型值，该值对应于流当前的整个条件状态</p><p>常常会出现需要设置或清除多个状态二进制位的情况。此时，可以通过多次调用 setstate 或者 clear 函数实现。另外一种方法则是使用按位或（OR）操作符在一次调用中生成&ldquo;传递两个或更多状态位&rdquo;的值<br />如：<br />// sets both the badbit and the failbit<br />is.setstate(ifstream::badbit | ifstream::failbit);</p><p>每个 IO 对象管理一个缓冲区，用于存储程序读写的数据</p><p>下面几种情况将导致缓冲区的内容被刷新，即写入到真实的输出设备或者文件：<br />1. 程序正常结束。作为 main 返回工作的一部分，将清空所有输出缓冲区。<br />2. 在一些不确定的时候，缓冲区可能已经满了，在这种情况下，缓冲区将会在写下一个值之前刷新。<br />3. 用操纵符显式地刷新缓冲区，例如行结束符 endl。<br />4. 在每次输出操作执行完后，用 unitbuf 操作符设置流的内部状态，从而清空缓冲区。<br />5. 可将输出流与输入流关联（tie）起来。在这种情况下，在读输入流时将刷新其关联的输出缓冲区。</p><p>输出缓冲区的刷新<br />1. endl 用于输出一个换行符并刷新缓冲区<br />2. flush 用于刷新流，但不在输出中添加任何字符<br />3. ends 在缓冲区中插入空字符 null，然后后刷新它</p><p>如果需要刷新所有输出，最好使用 unitbuf 操纵符，这个操纵符在每次执行完写操作后都刷新流</p><p>nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。</p><p>警告：如果程序崩溃了，则不会刷新缓冲区</p><p>为了确保用户看到程序实际上处理的所有输出，最好的方法是保证所有的输出操作都显式地调用了 flush 或 endl。</p><p>当输入流与输出流绑在一起时，任何读输入流的尝试都将首先刷新其输出流关联的缓冲区</p><p>交互式系统通常应确保它们的输入和输出流是绑在一起的。这样做意味着可以保证任何输出，包括给用户的提示，都在试图读之前输出。</p><p>tie 函数可用 istream 或 ostream 对象调用，使用一个指向 ostream 对象的指针形参。</p><p>如果一个流调用 tie 函数将其本身绑在传递给 tie 的 ostream 实参对象上，则该流上的任何 IO 操作都会刷新实参所关联的缓冲区。</p><p>一个 ostream 对象每次只能与一个 istream 对象绑在一起。如果在调用 tie 函数时传递实参 0，则打破该流上已存在的捆绑。</p><p>fstream 头文件定义了三种支持文件 IO 的类型：<br />1. ifstream，由 istream 派生而来，提供读文件的功能。<br />2. ofstream，由 ostream 派生而来，提供写文件的功能。<br />3. fstream，由 iostream 派生而来，提供读写同一个文件的功能。</p><p>fstream 类型除了继承下来的行为外，还定义了两个自己的新操作&mdash;&mdash; open 和 close，以及形参为要打开的文件名的构造函数。</p><p>由于历史原因，IO 标准库使用 C 风格字符串而不是 C++ strings 类型的字符串作为文件名。</p><p>假设要使用的文件名保存在 string 对象中，则可调用 c_str 成员获取 C 风格字符串。</p><p>打开文件后，通常要检验打开是否成功，这是一个好习惯</p><p>这个条件与之前测试 cin 是否到达文件尾或遇到某些其他错误的条件类似。</p><p>在尝试打开新文件之前，必须先关闭(close)当前的文件流。</p><p>关闭流并不能改变流对象的内部状态。如果最后的读写操作失败了，对象的状态将保持为错误模式，直到执行 clear 操作重新恢复流的状态为止。调用 clear 后，就像重新创建了该对象一样。</p><p>如果打算重用已存在的流对象，那么 while 循环必须在每次循环进记得关闭（close）和清空（clear）文件流</p><p>如果程序员需要重用文件流读写多个文件，必须在读另一个文件之前调用 clear 清除该流的状态。</p><p>在打开文件时，无论是调用 open 还是以文件名作为流初始化的一部分，都需指定文件模式（file mode）。</p><p>out、trunc 和 app 模式只能用于指定与 ofstream 或 fstream 对象关联的文件；in 模式只能用于指定与 ifstream 或 fstream 对象关联的文件。所有的文件都可以用 ate 或 binary 模式打开。ate 模式只在打开时有效：文件打开后将定位在文件尾。以 binary 模式打开的流则将文件以字节序列的形式处理，而不解释流中的字符。</p><p>默认时，与 ifstream 流对象关联的文件将以 in 模式打开，该模式允许文件做读的操作：与 ofstream 关联的文件则以 out 模式打开，使文件可写。以 out 模式打开的文件会被清空：丢弃该文件存储的所有数据。</p><p>对于用 ofstream 打开的文件，要保存文件中存在的数据，唯一方法是显式地指定 app 模式打开</p><p>默认情况下，fstream 对象以 in 和 out 模式同时打开。当文件同时以 in 和 out 打开时不清空。</p><p>如果打开文件时指定了 trunc 模式，则无论是否同时指定了 in 模式，文件同样会被清空。</p><p>模式是文件的属性而不是流的属性</p><p>只要调用 open 函数，就要设置文件模式，其模式的设置可以是显式的也可以是隐式的。如果没有指定文件模式，将使用默认值。</p><p>添加 ate 只会改变文件打开时的初始化定位，在第一次读或写之前，将文件定位于文件末尾处。</p><p>标准库定义了三种类型的字符串流：<br />1. istringstream，由 istream 派生而来，提供读 string 的功能。<br />2. ostringstream，由 ostream 派生而来，提供写 string 的功能。<br />3. stringstream，由 iostream 派生而来，提供读写 string 的功能。</p><p>定义了名为 str 的成员，用来读取或设置 stringstream 对象所操纵的 string 值。</p><p>stringstream 对象的一个常见用法是，需要在多种数据类型之间实现自动格式化时使用该类类型。<br />相反，用 istringstream 读 string 对象，即可重新将数值型数据找回来。</p><p>为了读取 input_string，必须把该 string 对象分解为若干个部分。我们要的是数值型数据；为了得到它们，必须读取（和忽略）处于所需数据周围的标号。</p><p>一般情况下，使用输入操作符读 string 时，空白符将会忽略。于是，在读与 format_message 关联的 string 时，忽略其中的换行符。</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;sstream&gt;
#include &lt;stdexcept&gt;
#include &lt;string&gt;
using namespace std;

// opens in binding it to the given file
ifstream&amp; open_file(ifstream &amp;in, const string &amp;file)
{
    in.close();     // close in case it was already open
    in.clear();     // clear any existing errors
    // if the open fails, the stream will be in an invalid state
    in.open(file.c_str()); // open the file we were given
    return in; // condition state is good if open succeeded
}

// print function: parameter is copied
ofstream print(ofstream);

int main()
{
    ofstream out1, out2;
    //out1 = out2;  error: cannot assign stream objects
    
    //out2 = print(out2);  error: cannot copy stream objects

    int ival;
    cout &lt;&lt; &quot;input zero to exit&quot; &lt;&lt; endl;
    // read cin and test only for EOF; loop is executed even if there are other IO failures
    while (cin &gt;&gt; ival, !cin.eof()) {
        if(0 == ival)
        {
            break;
        }
        if (cin.bad())         // input stream is corrupted; bail out
            throw runtime_error(&quot;IO stream corrupted&quot;);
        if (cin.fail()) {                        // bad input
            cerr&lt;&lt; &quot;bad data, try again&quot; &lt;&lt; endl; // warn the user
            cin.clear();         // reset the stream
            cin.ignore(255,'\n');
            continue;                            // get next input
        }
        // ok to process ival
        cout &lt;&lt; &quot;your input is : &quot; &lt;&lt; ival &lt;&lt; endl;
    }
    
    // remember current state of cin
    istream::iostate old_state = cin.rdstate();
    cout &lt;&lt; &quot;the old_state is &quot; &lt;&lt; old_state &lt;&lt; endl;
    cin.clear();
    int i;
    cout &lt;&lt; &quot;plz input a integer &gt; &quot; &lt;&lt; endl;
    cin &gt;&gt; i;
    cout &lt;&lt; &quot;your input is &quot; &lt;&lt; i &lt;&lt; endl;
    cin.clear(old_state); // now reset cin to old state
    
    cout &lt;&lt; &quot;hi!&quot; &lt;&lt; flush;      // flushes the buffer; adds no data
    cout &lt;&lt; &quot;hi!&quot; &lt;&lt; ends;       // inserts a null, then flushes the buffer
    cout &lt;&lt; &quot;hi!&quot; &lt;&lt; endl;       // inserts a newline, then flushes the buffer

    cout &lt;&lt; unitbuf &lt;&lt; &quot;first&quot; &lt;&lt; &quot; second&quot; &lt;&lt; nounitbuf;  
    cout &lt;&lt; endl; 
    cout &lt;&lt; &quot;first&quot; &lt;&lt; flush &lt;&lt; &quot; second&quot; &lt;&lt; flush;  //the same as above
    cout &lt;&lt; endl;

    cin.tie(&amp;cout);   // illustration only: the library ties cin and cout for us
    ostream *old_tie = cin.tie();
    cin.tie(0); // break tie to cout, cout no longer flushed when cin is read
    cin.tie(&amp;cerr);   // ties cin and cerr, not necessarily a good idea!
    // ...
    cin.tie(0);       // break tie between cin and cerr
    cin.tie(old_tie); // restablish normal tie between cin and cout
    
    cin.tie(0);   // break tie to cout, cout no longer flushed when cin is read
    
    string ifile(&quot;in.h&quot;);
    string ofile(&quot;ofile.txt&quot;);
    // construct an ifstream and bind it to the file named ifile
    ifstream infile(ifile.c_str());
    
    // check that the open succeeded
    if (!infile) {
        cerr &lt;&lt; &quot;error: unable to open input file: &quot;
             &lt;&lt; ifile &lt;&lt; endl;
        return -1;
    }

    // ofstream output file object to write file named ofile
    ofstream outfile(ofile.c_str());

    ifstream infile2;    // unbound input file stream
    ofstream outfile2;   // unbound output file stream
    
    infile2.open(&quot;my.h&quot;);   // open file named &quot;in&quot; in the current directory
    outfile2.open(&quot;out2&quot;); // open file named &quot;out&quot; in the current directory
    
    //  output mode by default; truncates file named &quot;file1&quot;
    ofstream outfile3(&quot;file1&quot;);
    // equivalent effect: &quot;file1&quot; is explicitly truncated
    ofstream outfile4(&quot;file1&quot;, ofstream::out | ofstream::trunc);
    //  append mode; adds new data at end of existing file named &quot;file2&quot;
    ofstream appfile(&quot;file2&quot;, ofstream::app);
    
    // open for input and output
    fstream inOut(&quot;copyOut&quot;, fstream::in | fstream::out);
    
    int val1 = 512, val2 = 1024;
    ostringstream format_message;
    // ok: converts values to a string representation
    format_message &lt;&lt; &quot;val1: &quot; &lt;&lt; val1 &lt;&lt; &quot;\n&quot;
                   &lt;&lt; &quot;val2: &quot; &lt;&lt; val2 &lt;&lt; &quot;\n&quot;;
    // str member obtains the string associated with a stringstream
    istringstream input_istring(format_message.str());
    string dump; // place to dump the labels from the formatted message
    // extracts the stored ascii values, converting back to arithmetic types
    input_istring &gt;&gt; dump &gt;&gt; val1 &gt;&gt; dump &gt;&gt; val2;
    cout &lt;&lt; val1 &lt;&lt; &quot; &quot; &lt;&lt; val2 &lt;&lt; endl;  // prints 512 1024


    string line, word;      // will hold a line and word from input, respectively
    while (getline(cin, line))   {            // read a line from the input into line
       // do per-line processing
       istringstream stream(line);            // bind to stream to the line we read
       while (stream &gt;&gt; word){          // read a word from line
           // do per-word processing
           cout &lt;&lt; word &lt;&lt; endl;
       }
    }

    return 0;
}</pre><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/161982#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 12 Feb 2008 22:00:22 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/161982</link>
        <guid>http://stonelet.javaeye.com/blog/161982</guid>
      </item>
      <item>
        <title>函数function</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/161981" style="color:red;">http://stonelet.javaeye.com/blog/161981</a>&nbsp;
          发表时间: 2008年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>函数由函数名以及一组操作数类型唯一地表示。函数的操作数，也即形参，在一对圆括号中声明，形参与形参之间以逗号分隔。函数执行的运算在一个称为函数体的块语句中定义。每一个函数都有一个相关联的返回类型。</p><p>C++ 语言使用调用操作符（即一对圆括号）实现函数的调用。</p><p>函数体是一个作用域</p><p>类似于局部变量，函数的形参为函数提供了已命名的局部存储空间。它们之间的差别在于形参是在函数的形参表中定义的，并由调用函数时传递函数的实参初始化。</p><p>实参则是一个表达式。它可以是变量或字面值常量，甚至是包含一个或几个操作符的表达式。</p><p>实参个数必须与函数的形参个数完全相同，实参必须具有与形参类型相同、或者能隐式转换为形参类型的数据类型。</p><p>函数的返回类型可以是内置类型（如 int 或者 double）、类类型或复合类型（如 int&amp; 或 string*），还可以是 void 类型，表示该函数不返回任何值。</p><p>函数不能返回另一个函数或者内置数组类型，但可以返回指向函数的指针，或指向数组元素的指针的指针</p><p>函数必须指定返回类型，在定义或声明函数时，没有显式指定返回类型是不合法的</p><p>函数形参表可以为空，但不能省略。没有任何形参的函数可以用空形参表或含有单个关键字 void 的形参表来表示。</p><p>如果两个参数具有相同的类型，则其类型必须重复声明</p><p>参数表中不能出现同名的参数</p><p>参数传递<br />形参的初始化与变量的初始化一样：如果形参具有非引用类型，则复制实参的值，如果形参为引用类型，则它只是实参的别名。</p><p>普通的非引用类型的参数通过复制对应的实参实现初始化，不会修改实参的值。</p><p>非引用形参表示对应实参的局部副本。对这类形参的修改仅仅改变了局部副本的值。一旦函数执行结束，这些局部变量的值也就没有了。</p><p>如果函数将新指针赋给形参，主调函数使用的实参指针的值没有改变。<br />如果函数形参是非 const 类型的指针，则函数可通过指针实现赋值，修改指针所指向对象的值</p><p>如果保护指针指向的值，则形参需定义为指向 const 对象的指针</p><p>在调用函数时，如果该函数使用非引用的形参(const、非const)，则既可给该函数传递 const 实参也可传递非 const 的实参，因为是以副本的形式传递.</p><p>具有 const 形参或非 const 形参的函数并无区别<br />形参与 const 形参的等价性仅适用于非引用形参。<br />有 const 引用形参的函数与有非 const 引用形参的函数是不同的</p><p>不适宜复制实参的情况包括：<br />1. 当需要在函数中修改实参的值时<br />2. 当需要以大型对象作为实参传递时<br />3. 当没有办法实现对象的复制时</p><p>每次调用函数，引用形参被创建并与相应实参关联。</p><p>如果使用引用形参的唯一目的是避免复制实参，则应将形参定义为 const 引用。</p><p>如果函数具有普通的非 const 引用形参，则显然不能通过 const 对象进行调用。比较容易忽略的是，调用这样的函数时，传递一个右值或具有需要转换的类型的对象同样是不允许的。</p><p>非 const 引用形参只能与完全同类型的非 const 对象关联。</p><p>应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用 const 对象初始化，也不能用字面值或产生右值的表达式实参初始化。</p><p>传递指向指针的引用</p><p>vector 和其他容器类型的形参</p><p>通常，函数不应该有 vector 或其他标准库容器类型的形参。调用含有普通的非引用 vector 形参的函数将会复制 vector 的每一个元素。</p><p>事实上，C++ 程序员倾向于通过传递指向容器中需要处理的元素的迭代器来传递容器</p><p>数组形参<br />处理数组的函数通常通过操纵指向数组指向数组中的元素的指针来处理数组。</p><p>形参的长度会引起误解</p><p>编译器忽略为任何数组形参指定的长度。当编译器检查数组形参关联的实参时，它只会检查实参是不是指针、指针的类型和数组元素的类型时是否匹配，而不会检查数组的长度。</p><p>不需要修改数组形参的元素时，函数应该将形参定义为指向 const 对象的指针</p><p>通过引用传递数组</p><p>如果形参是数组的引用，编译器不会将数组实参转化为指针，而是传递数组的引用本身。在这种情况下，数组大小成为形参和实参类型的一部分。编译器检查数组的实参的大小与形参的大小是否匹配</p><p>f(int &amp;arr[10])&nbsp;&nbsp;&nbsp;&nbsp; // error: arr is an array of references<br />f(int (&amp;arr)[10])&nbsp;&nbsp; // ok: arr is a reference to an array of 10 ints</p><p>传递给函数的数组的处理</p><p>任何处理数组的程序都要确保程序停留在数组的边界内。</p><p>三种常见的编程技巧确保函数的操作不超出数组实参的边界。<br />1. 在数组本身放置一个标记来检测数组的结束，如C 风格字符串采用NULL作为结束标记<br />2. 传递指向数组第一个和最后一个元素的下一个位置的指针<br />3. 将第二个形参定义为表示数组的大小</p><p>main: 处理命令行选项<br />int main(int argc, char *argv[]) { ... }<br />int main(int argc, char **argv) { ... }</p><p>将实参传递给主函数 main 时，argv 中的第一个字符串（如果有的话）通常是程序的名字。</p><p>含有可变形参的函数<br />void foo(parm_list, ...);<br />void foo(...);</p><p>当需要传递给省略符形参时，大多数类类型对象都不能正确地复制，省略符暂停了类型检查机制</p><p>不带返回值的 return 语句只能用于返回类型为 void 的函数</p><p>隐式的 return 发生在函数的最后一个语句完成时</p><p>一般情况下，返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束，这种 return 的用法类似于循环结构中的 break 语句的作用</p><p>任何返回类型不是 void 的函数必须返回一个值，而且这个返回值的类型必须和函数的返回类型相同，或者能隐式转化为函数的返回类型。</p><p>尽管 C++ 不能确保结果的正确性，但能保证函数每一次 return 都返回适当类型的结果。</p><p>在含有 return 语句的循环后没有提供 return 语句是很危险的，因为大部分的编译器不能检测出这个漏洞，运行时会出现什么问题是不确定的。</p><p>主函数 main 的返回值，返回 0 表示程序运行成功，其他大部分返回值则表示失败，非 0 返回值的意义因机器不同而不同</p><p>cstdlib 头文件定义了两个预处理变量EXIT_FAILURE和EXIT_SUCCESS，分别用于表示程序运行成功和失败</p><p>如果返回类型不是引用，在调用函数的地方会将函数返回值复制给临时对象。当函数返回非引用类型时，其返回值既可以是局部对象，也可以是求解表达式的结果。</p><p>当函数返回引用类型时，没有复制返回值。相反，返回的是对象本身。</p><p>理解返回引用至关重要的是：千万不能返回局部变量的引用。</p><p>返回引用的函数返回一个左值。因此，这样的函数可用于任何要求使用左值的地方</p><p>千万不要返回指向局部对象的指针</p><p>直接或间接调用自己的函数称为递归函数。</p><p>递归函数必须定义一个终止条件；否则，函数就会&ldquo;永远&rdquo;递归下去</p><p>主函数 main 不能调用自身。</p><p>函数声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型，但是不必对形参命名。这三个元素被称为函数原型，函数原型描述了函数的接口。</p><p>函数声明中的形参名会被忽略，如果在声明中给出了形参的名字，它应该用作辅助文档</p><p>函数应当在头文件中声明，并在源文件中定义。</p><p>义函数的源文件应包含声明该函数的头文件。</p><p>可为一个或多个形参定义默认值。但是，如果有一个形参具有默认实参，那么，它后面所有的形参都必须有默认实参。</p><p>调用包含默认实参的函数时，可以为该形参提供实参，也可以不提供。如果提供了实参，则它将覆盖默认的实参值；否则，函数将使用默认实参值。</p><p>函数调用的实参按位置解析，默认实参只能用来替换函数调用缺少的尾部实参。</p><p>默认情况下，局部变量的生命期局限于所在函数的每次执行期间。只有当定义它的函数被调用时才存在的对象称为自动对象。自动对象在每次调用函数时创建和撤销。</p><p>局部变量所对应的自动对象在函数控制经过变量定义语句时创建。</p><p>形参也是自动对象。形参所占用的存储空间在调用函数时创建，而在函数结束时撤销。</p><p>static 局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化。这种对象一旦被创建，在程序结束前都不会撤销。</p><p>将函数指定为 inline 函数，（通常）就是将它在程序中每个调用点上&ldquo;内联地&rdquo;展开。</p><p>inline 说明对于编译器来说只是一个建议，编译器可以选择忽略这个。</p><p>一般来说，内联机制适用于优化小的、只有几行的而且经常被调用的函数。</p><p>大多数的编译器都不支持递归函数的内联</p><p>内联函数应该在头文件中定义，这一点不同于其他函数。</p><p>内联函数应该在头文件中定义，这一点不同于其他函数。</p><p>在头文件中加入或修改 inline 函数时，使用了该头文件的所有源文件都必须重新编译。</p><p>出现在相同作用域中的两个函数，如果具有相同的名字而形参表不同，则称为重载函数。</p><p>任何程序都仅有一个 main 函数的实例。main 函数不能重载。</p><p>函数重载和重复声明的区别<br />如果两个函数声明的返回类型和形参表完全匹配，则将第二个函数声明视为第一个的重复声明。如果两个函数的形参表完全相同，但返回类型不同，则第二个声明是错误的。</p><p>函数不能仅仅基于不同的返回类型而实现重载。</p><p>&nbsp;</p><p><br />设计带有默认实参的函数，其中部分工作就是排列形参，使最少使用默认实参的形参排在最前，最可能使用默认实参的形参排在最后。</p><p>如果默认实参是一个表达式，而且默认值用作实参，则在调用函数时求解该表达式。</p><p>既可以在函数声明也可以在函数定义中指定默认实参。但是，在一个文件中，只能为一个形参指定默认实参一次。</p><p>通常，应在函数声明中指定默认实参，并将该声明放在合适的头文件中。</p><p>如果在函数定义的形参表中提供默认实参，那么只有在包含该函数定义的源文件中调用该函数时，默认实参才是有效的。</p><p>一般来说，局部地声明函数是一种不明智的选择。函数的声明应放在头文件中。</p><p>在 C++ 中，名字查找发生在类型检查之前。</p><p>函数匹配与实参转换</p><p>重载确定的三个步骤<br />1. 候选函数<br />与被调函数同名的函数，并且在调用点上，它的声明可见。<br />2. 选择可行函数 <br />可行函数必须满足两个条件：第一，函数的形参个数与该调用的实参个数相同；第二，每一个实参的类型必须与对应形参的类型匹配，或者可被隐式转换为对应的形参类型。如果函数具有默认实参，则调用该函数时，所用的实参可能比实际需要的少。默认实参也是实参，在函数匹配过程中，它的处理方式与其他实参一样<br />。<br />3. 寻找最佳匹配（如果有的话）<br />为了确定最佳匹配，编译器将实参类型到相应形参类型转换划分等级。转换等级以降序排列如下：<br />(1) 精确匹配 实参与形参类型相同<br />(2) 通过类型提升实现的匹配&nbsp; <br />(3) 通过标准转换实现的匹配<br />(4) 通过类类型转换实现的匹配</p><p>必须注意的一个重点是较小的整型提升为 int 型</p><p>通过类型提升实现的转换优于其他标准转换</p><p>整数对象即使具有与枚举元素相同的值也不能用于调用期望获得枚举类型实参的函数</p><p>在使用有枚举类型形参的重载函数时，请记住：由于不同枚举类型的枚举常量值不相同，在函数重载确定过程中，不同的枚举类型会具有完全不同的行为。其枚举成员决定了它们提升的类型，而所提升的类型依赖于机器。</p><p>仅当形参是引用或指针时，形参是否为 const 才有影响。</p><p>注意不能基于指针本身是否为 const 来实现函数的重载</p><p>指向函数的指针</p><p>函数指针是指指向函数而非指向对象的指针</p><p>函数类型由其返回类型以及形参表确定，而与函数名无关</p><p>函数指针类型相当地冗长。使用 typedef 为指针类型定义同义词，可将函数指针的使用大大简化</p><p>在引用函数名但又没有调用该函数时，函数名将被自动解释为指向函数的指针</p><p>可使用函数名对函数指针做初始化或赋值</p><p>直接引用函数名等效于在函数名上应用取地址操作符</p><p>函数指针只能通过同类型的函数或函数指针或 0 值常量表达式进行初始化或赋值。</p><p>将函数指针初始化为 0，表示该指针不指向任何函数。</p><p>指向不同函数类型的指针之间不存在转换</p><p>指向函数的指针可用于调用它所指向的函数</p><p>如果指向函数的指针没有初始化，或者具有 0 值，则该指针不能在函数调用中使用。只有当指针已经初始化，或被赋值为指向某个函数，方能安全地用来调用函数。</p><p>函数的形参可以是指向函数的指针</p><p>返回指向函数的指针</p><p>允许将形参定义为函数类型，但函数的返回类型则必须是指向函数的指针，而不能是函数。</p><p>C++ 语言允许使用函数指针指向重载的函数</p><p>指针的类型必须与重载函数的一个版本精确匹配</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;cstdlib&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
using namespace std;

// error: missing return type
// ISO C++ forbids declaration of `test' with no type 
// test(double v1, double v2){ }

     
void process() { /* ... */ }      // implicit void parameter list
//void process(void){ /* ... */ }  equivalent declaration

//int manip(int v1, v2) { /* ... */ }   error
int manip(int v1, int v2) { /* ... */ }  // ok

void fcn(const int i) { /* fcn can read but not write to i */ }
//void fcn(int i) { /* ... */ }     error: redefines fcn(int)

int incr(int &amp;val)
{
 return ++val;
}

// swap values of two pointers to int
void ptrswap(int *&amp;v1, int *&amp;v2)
{
 int *tmp = v2;
 v2 = v1;
 v1 = tmp;
}

// pass iterators to the first and one past the last element to print
void print(vector&lt;int&gt;::const_iterator beg,vector&lt;int&gt;::const_iterator end)
{
     while (beg != end) 
     {
         cout &lt;&lt; *beg++;
         if (beg != end) 
         {
            cout &lt;&lt; &quot; &quot;; // no space after last element
         }
     }
     cout &lt;&lt; endl;
}

// three equivalent definitions of printValues
void printValues(int*) { /* ... */ }
//void printValues(int[]) { /* ... */ }  
//void printValues(int[10]) { /* ... */ }

// ok: parameter is a reference to an array; size of array is fixed
void printValuesByRef(int (&amp;arr)[10]) 
{ 
     for(size_t index=0; index!=10; ++index)
     {
          cout &lt;&lt; arr[index] &lt;&lt; &quot; &quot;;
     }
     cout &lt;&lt; endl;
}

size_t count_calls()
{
  static size_t ctr = 0; // value will persist across calls
  return ++ctr;
}

// inline version: find longer of two strings
inline const string &amp; shorterString(const string &amp;s1, const string &amp;s2)
{
     return s1.size() &lt; s2.size() ? s1 : s2;
}


void f();
void f(int);
void f(int, int);
void f(double, double = 3.14);


enum Tokens{INLINE = 128, VIRTUAL = 129};
void ff(Tokens);
void ff(int);

void fff(int *){}
//void fff(int *const){}  error redeclaration

// pf points to function returning bool that takes two const string references
bool (*pf)(const string &amp;, const string &amp;);

// declares a function named pf that returns a bool*
bool *pd(const string &amp;, const string &amp;);

// compares lengths of two strings
bool lengthCompare(const string &amp;, const string &amp;);

typedef bool (*cmpFcn)(const string &amp;, const string &amp;);

// compares lengths of two strings
bool lengthCompare(const string &amp;, const string &amp;);

/* useBigger function's third parameter is a pointer to function
* that function returns a bool and takes two const string references
* two ways to specify that parameter:
*/
// third parameter is a function type and is automatically treated as a pointer to function
void useBigger(const string &amp;, const string &amp;,
            bool(const string &amp;, const string &amp;));
// equivalent declaration: explicitly define the parameter as a pointer to function
void useBigger(const string &amp;, const string &amp;,
            bool (*)(const string &amp;, const string &amp;));

// ff is a function taking an int and returning a function pointer
// the function pointed to returns an int and takes an int* and an int
int (*fs(int))(int*, int);

// PF is a pointer to a function returning an int, taking an int* and an int
typedef int (*PF)(int*, int);
PF fd(int);  // ff returns a pointer to function

void ff(vector&lt;double&gt;);
void ff(unsigned int);

int main()
{
    short v1 = 0;
    const int v2 = 42;
    int v3 = 33;
    //v3 = incr(v1);    //error: v1 is not an int
    //v3 = incr(v2);        error: v2 is const
    //v3 = incr(0);         error: literals are not lvalues
    //v3 = incr(v1 + v2);   error: addition doesn't yield an lvalue
    int v4 = incr(v3);   // ok: v3 is a non const object type int

    int i = 10;
    int j = 20;
    int *pi = &amp;i;  // pi points to i
    int *pj = &amp;j; // pj points to j
    cout &lt;&lt; &quot;Before ptrswap():\t*pi: &quot;
      &lt;&lt; *pi &lt;&lt; &quot;\t*pj: &quot; &lt;&lt; *pj &lt;&lt; endl;
    ptrswap(pi, pj); // now pi points to j; pj points to i
    cout &lt;&lt; &quot;After ptrswap():\t*pi: &quot;
      &lt;&lt; *pi &lt;&lt; &quot;\t*pj: &quot; &lt;&lt; *pj &lt;&lt; endl;

    int ia[10] = {0,1,2,3,4,5,6,7,8,9};
    printValuesByRef(ia);
    
    for (size_t i = 0; i != 10; ++i)
    {
     cout &lt;&lt; count_calls() &lt;&lt; endl;
    }
    
    f(5.6);   //calls void f(double, double)

    
    Tokens curTok = INLINE;
    ff(128);    // exactly matches ff(int)
    ff(INLINE); // exactly matches ff(Tokens)
    ff(curTok); // exactly matches ff(Tokens)
    
    cmpFcn pf1 = 0;             // ok: unbound pointer to function
    cmpFcn pf2 = lengthCompare; // ok: pointer type matches function's type
    cmpFcn pf3 = &amp;lengthCompare;  // the same sa above
    pf1 = lengthCompare;        // ok: pointer type matches function's type
    pf2 = pf1;                  // ok: pointer types match
    
    cmpFcn pf = lengthCompare;
    lengthCompare(&quot;hi&quot;, &quot;bye&quot;); // direct call
    pf(&quot;hi&quot;, &quot;bye&quot;);            // equivalent call: pf1 implicitly dereferenced
    (*pf)(&quot;hi&quot;, &quot;bye&quot;);         // equivalent call: pf1 explicitly dereferenced

    // which function does pf1 refer to?
    void (*pf4)(unsigned int) = &amp;ff; // ff(unsigned)
        
    if(true)
    {
        return EXIT_SUCCESS;
    }  
}


void f()
{
 cout &lt;&lt; &quot;f() ...&quot; &lt;&lt; endl;     
}
void f(int)
{
 cout &lt;&lt; &quot;f(int) ...&quot; &lt;&lt; endl;     
}
void f(int, int)
{
 cout &lt;&lt; &quot;f(int ,int) ...&quot; &lt;&lt; endl;     
}
void f(double, double)
{
 cout &lt;&lt; &quot;f(double, double = 3.14) ...&quot; &lt;&lt; endl;     
}

void ff(Tokens)
{
 cout &lt;&lt; &quot;ff(Tokens) ...&quot; &lt;&lt; endl;     
}
void ff(int)
{
 cout &lt;&lt; &quot;ff(int) ...&quot; &lt;&lt; endl;     
}
bool lengthCompare(const string &amp;s1, const string &amp;s2)
{
     return  s1 &lt; s2 ;
}
void ff(vector&lt;double&gt;)
{
  cout &lt;&lt; &quot;ff(vector&lt;double&gt;) ...&quot; &lt;&lt; endl;      
}
void ff(unsigned int)
{
  cout &lt;&lt; &quot;ff(unsigned int) ...&quot; &lt;&lt; endl;      
}</pre><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/161981#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 12 Feb 2008 21:57:17 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/161981</link>
        <guid>http://stonelet.javaeye.com/blog/161981</guid>
      </item>
      <item>
        <title>语句(...)</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/161980" style="color:red;">http://stonelet.javaeye.com/blog/161980</a>&nbsp;
          发表时间: 2008年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>简单语句</p><p>C++ 中，大多数语句以分号结束。</p><p>程序语句最简单的形式是空语句(只有一个单独的分号)</p><p>如果在程序的某个地方，语法上需要一个语句，但逻辑上并不需要，此时应该使用空语句。<br />使用空语句时应该加上注释，以便任何读这段代码的人都知道该语句是有意省略的。</p><p>无关的空语句并非总是无害的。</p><p>在 C++ 中，对象或类的定义或声明也是语句。</p><p>复合语句，通常被称为块，是用一对花括号括起来的语句序列（也可能是空的）。块标识了一个作用域，在块中引入的名字只能在该块内部或嵌套在块中的子块里访问。通常，一个名字只从其定义处到该块的结尾这段范围内可见。</p><p>与其他大多数语句不同，块并不是以分号结束的。</p><p>语句作用域</p><p>有些语句允许在它们的控制结构中定义变量，如for,while</p><p>在条件表达式中定义的变量必须初始化，该条件检验的就是初始化对象的值。</p><p>在语句的控制结构中定义的变量，仅在定义它们的块语句结束前有效。这种变量的作用域限制在语句体内。</p><p>如果程序需要访问某个控制结构中的变量，那么这个变量必须在控制语句外部定义。</p><p>if 语句根据特定表达式是否为真来有条件地执行另一个语句<br />最简单的if语句语法为：<br />if (condition)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement<br />其中的 condition 部分必须用圆括号括起来。它可以是一个表达式，或者一个初始化声明。<br />通常，statement 部分可以是复合语句，即用花括号括起来的块语句。</p><p>if else 语句的语法形式为：<br />&nbsp;&nbsp;&nbsp;&nbsp; if (condition)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement1<br />&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement2<br />statement2 既可以是任意语句，也可以是用花括号起来的块语句</p><p>悬垂 else<br />C++ 中悬垂 else 问题带来的二义性，通过将 else 匹配给最后出现的尚未匹配的 if 子句来解决</p><p>建议总是在 if 后面使用花括号。这样做可以避免日后修改代码时产生混乱和错误。至少，无论 if（或者 while）后面是简单语句，例如赋值和输出语句，还是其他任意语句，使用花括号都是一个比较好的做法。</p><p>关键字 case 和它所关联的值称为 case 标号。每个 case 标号的值都必须是一个常量表达式</p><p>小心case穿透！</p><p>存在一个普遍的误解：以为程序只会执行匹配的 case 标号相关联的语句。实际上，程序从该点开始执行，并跨越 case 边界继续执行其他语句，直到 switch 结束或遇到 break 语句为止。</p><p>对于 switch 结构，漏写 break 语句是常见的程序错误。</p><p>尽管没有严格要求在 switch 结构的最后一个标号之后指定 break 语句，但是，为了安全起见，最好在每个标号后面提供一个 break 语句，即使是最后一个标号也一样。如果以后在 switch 结构的末尾又需要添加一个新的 case 标号，则不用再在前面加 break 语句了。</p><p>慎用 break 语句，它并不总是恰当的</p><p>default 标号提供了相当于 else 子句的功能</p><p>哪怕没有语句要在 default 标号下执行，定义 default 标号仍然是有用的。</p><p>一个标号不能独立存在，它必须位于语句之前。如果 switch 结构以 default 标号结束，而且 default 分支不需要完成任何任务，那么该标号后面必须有一个空语句。</p><p>case 标号必须是整型常量表达式</p><p>如果两个 case 标号具有相同的值，同样也会导致编译时的错误。</p><p>对于 switch 结构，只能在它的最后一个 case 标号或 default 标号后面定义变量，制定这个规则是为避免出现代码跳过变量的定义和初始化的情况。</p><p>如果需要为某个特殊的 case 定义变量，则可以引入块语句，在该块语句中定义变量，从而保证这个变量在使用前被定义和初始化。</p><p>当条件为真时，while 语句反复执行目标语句<br />while (condition)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement</p><p>循环条件 condition 可以是一个表达式，或者是提供初始化的变量定义。</p><p>在循环条件中定义的变量在每次循环里都要经历创建和撤销的过程。</p><p>for 语句的语法形式是：<br />for (initializer; condition; expression)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement<br />initializer必须是声明语句、表达式语句或空语句。</p><p>for 语句头中，可以省略 init-statement、condition 或者 expression（表达式）中的任何一个（或全部）</p><p>可以在 for 语句的 init-statement 中定义多个对象；但是不管怎么样，该处只能出现一个语句，因此所有的对象必须具有相同的一般类型</p><p>do<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; statement<br />&nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;&nbsp; (condition);<br />与 while 语句不同。do-while 语句总是以分号结束。</p><p>它保证循环体至少执行一次</p><p>break 语句用于结束最近的 while、do while、for 或 switch 语句，并将程序的执行权传递给紧接在被终止语句之后的语句。</p><p>break 只能出现在循环或 switch 结构中，或者出现在嵌套于循环或 switch 结构中的语句里。</p><p>break 出现在循环外或者 switch 外将会导致编译时错误。</p><p>continue 语句导致最近的循环语句的当次迭代提前结束。对于 while 和 do while 语句，继续求解循环条件。而对于 for 循环，程序流程接着求解 for 语句头中的 expression 表达式。</p><p>continue 语句只能出现在 for、while 或者 do while 循环中，包括嵌套在这些循环内部的块语句中。</p><p>goto 语句提供了函数内部的无条件跳转，实现从 goto 语句跳转到同一函数内某个带标号的语句。<br />(从上世纪 60 年代后期开始，不主张使用 goto 语句 -_- !!!)</p><p>用法：<br />goto label;</p><p>goto 语句不能跨越变量的定义语句向前跳转</p><p>如果确实需要在 goto 和其跳转对应的标号之间定义变量，则定义必须放在一个块语句中</p><p>try 块和异常处理<br />C++ 的异常处理中包括:<br />1. throw 表达式，错误检测部分使用这种表达式来说明遇到了不可处理的错误。<br />2. try 块，错误处理部分使用它来处理异常。try 语句块以 try 关键字开始，并以一个或多个 catch 子句结束<br />3. 由标准库定义的一组异常类，用来在 throw 和相应的 catch 之间传递有关的错误信息。</p><p>如果不存在处理该异常的 catch 子句，程序的运行就要跳转到名为 terminate 的标准库函数，该函数在 exception 头文件中定义。这个标准库函数的行为依赖于系统，通常情况下，它的执行将导致程序非正常退出。</p><p>标准异常<br />1. exception 头文件定义了最常见的异常类，它的类名是 exception。这个类只通知异常的产生，但不会提供更多的信息。<br />2. stdexcept 头文件定义了几种常见的异常类<br />3. new 头文件定义了 bad_alloc 异常类型，提供因无法分配内在而由 new抛出的异常<br />4. type_info 头文件定义了 bad_cast 异常类型</p><p><br />使用预处理器进行调试<br />可使用 NDEBUG 预处理变量实现有条件的调试代码</p><p>如果 NDEBUG 未定义，那么程序就会将信息写到 cerr 中。如果 NDEBUG 已经定义了，那么程序执行时将会跳过 #ifndef 和 #endif 之间的代码。</p><p>默认情况下，NDEBUG 未定义，这也就意味着必须执行 #ifndef 和 #endif 之间的代码。在开发程序的过程中，只要保持 NDEBUG 未定义就会执行其中的调试语句。开发完成后，要将程序交付给客户时，可通过定义 NDEBUG 预处理变量，（有效地）删除这些调试语句。大多数的编译器都提供定义 NDEBUG 命令行选项。<br />例:#g++ -DNDEBUG -c main.cc</p><p>预处理器还定义了其余四种在调试时非常有用的常量(以下为两个连续的下划线)：<br />__FILE__ 文件名<br />__LINE__ 当前行号<br />__TIME__ 文件被编译的时间<br />__DATE__ 文件被编译的日期</p><p>另一个常见的调试技术是使用 NDEBUG 预处理变量以及 assert 预处理宏。assert 宏是在 cassert 头文件中定义的，所有使用 assert 的文件都必须包含这个头文件。<br />assert(expr)</p><p>只要 NDEBUG 未定义，assert 宏就求解条件表达式 expr，如果结果为 false，assert 输出信息并且终止程序的执行。如果该表达式有一个非零（例如，true）值，则 assert 不做任何操作。</p><p>与异常不同（异常用于处理程序执行时预期要发生的错误），程序员使用 assert 来测试&ldquo;不可能发生&rdquo;的条件</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;cassert&gt;
using namespace std;

int main()
{
    ;  // null statement
    
    cout &lt;&lt; &quot;type exit to end the loop &gt;&quot; &lt;&lt; endl;
    string s;
    // read until we hit end-of-file or find an input equal to sought
    while (cin &gt;&gt; s &amp;&amp; s != &quot;exit&quot;)
     ; // null statement
         
    cout &lt;&lt; &quot;type exit to end the loop &gt;&quot; &lt;&lt; endl;
    while (cin &gt;&gt; s &amp;&amp; s != &quot;exit&quot;)
     { } // empty block
   
    cout &lt;&lt; &quot;input any char you want , press CTRL+Z to end &gt;&quot; &lt;&lt; endl;
    char ch;
    // initialize counters for each vowel
    int aCnt = 0, eCnt = 0, iCnt = 0,
     oCnt = 0, uCnt = 0;
    while (cin &gt;&gt; ch) {
     // if ch is a vowel, increment the appropriate counter
     switch (ch) {
         case 'a':
             // int first = 1; error ! crosses initialization of `int first' 
             ++aCnt;
             {
               int first = 1;   // ok  in the block
             }
             break;
         case 'e':
             ++eCnt;
             break;
         case 'i':
             ++iCnt;
             break;
         case 'o':
             ++oCnt;
             break;
         case 'u':
             int last = 1;   //ok  the last case label
             ++uCnt;
             break;
     }
    }
    // print results
    cout &lt;&lt; &quot;Number of vowel a: \t&quot; &lt;&lt; aCnt &lt;&lt; '\n'
         &lt;&lt; &quot;Number of vowel e: \t&quot; &lt;&lt; eCnt &lt;&lt; '\n'
         &lt;&lt; &quot;Number of vowel i: \t&quot; &lt;&lt; iCnt &lt;&lt; '\n'
         &lt;&lt; &quot;Number of vowel o: \t&quot; &lt;&lt; oCnt &lt;&lt; '\n'
         &lt;&lt; &quot;Number of vowel u: \t&quot; &lt;&lt; uCnt &lt;&lt; endl;
         
    int ival;
    const double dval = 3.14;
    
    switch(ival) {
       case 1 :
            ;
            break;
      /* case 1 :    error: duplicate case value 
       *  ;
       *  break;
       * case 3.14:  error:case label does not reduce to an integer constant 
       *  ;
       *  break;
       * case dval:  error:`dval' cannot appear in a constant-expression 
       *  ;
       *  break;
       */
       default :
            break; 
    }

   // for(int i,int j; ;)  //error expected `,' or `;' before &quot;int&quot; 
       ;   
    for(int i,j,k; ;)  //ok  i j k has the same type  
       break;   
    
    goto label1;
    cout &lt;&lt; &quot;I will not be printed...&quot; &lt;&lt; endl;
    
    label1: cout &lt;&lt; &quot;here is label1 &quot; &lt;&lt; endl;


    #ifndef NDEBUG
    cerr &lt;&lt; &quot;ending main...&quot; &lt;&lt; endl;
    #endif
    //assert(1&gt;2);
    
    cerr &lt;&lt; &quot;An Error Info like this: &quot; &lt;&lt; __FILE__
         &lt;&lt; &quot; : line &quot; &lt;&lt; __LINE__ &lt;&lt; endl
         &lt;&lt; &quot;       Compiled on &quot; &lt;&lt; __DATE__
         &lt;&lt; &quot; at &quot; &lt;&lt; __TIME__ &lt;&lt; endl;

    return 0;   
}</pre><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/161980#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 12 Feb 2008 21:55:14 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/161980</link>
        <guid>http://stonelet.javaeye.com/blog/161980</guid>
      </item>
      <item>
        <title>表达式</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/161979" style="color:red;">http://stonelet.javaeye.com/blog/161979</a>&nbsp;
          发表时间: 2008年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>表达式由一个或多个操作数通过操作符组合而成。</p><p>最简单的表达式仅包含一个字面值常量或变量。</p><p>每个表达式都会产生一个结果。</p><p>当一个对象用在需要使用其值的地方，则计算该对象的值。</p><p>除了特殊用法外，表达式的结果是右值，可以读取该结果值，但是不允许对它进行赋值。</p><p>操作符的含义&mdash;&mdash;该操作符执行什么操作<br />操作结果的类型&mdash;&mdash;取决于操作数的类型</p><p><br />回绕<br />1. 无符号整数变量的值超过它能保存的最大值后，会发生回绕，回到 0 重新开始；<br />2. 有符号变量回绕是从正的极端回绕到负的极端；</p><p>对两个整数做除法，结果仍为整数，如果它的商包含小数部分，则小数部分会被截除</p><p>操作符 % 称为&ldquo;求余（remainder）&rdquo;或&ldquo;求模（modulus）&rdquo;操作符<br />该操作符的操作数只能为整型，包括 bool、char、short 、int 和 long 类型，以及对应的 unsigned 类型</p><p>当只有一个操作数为负数时，求模操作结果值的符号可依据分子（被除数）或分母（除数）的符号而定。如果求模的结果随分子的符号，则除出来的值向零一侧取整；如果求模与分母的符号匹配，则除出来的值向负无穷一侧取整。</p><p>关系操作符和逻辑操作符</p><p>expr1 &amp;&amp; expr2 // logical AND<br />expr1 || expr2 // logical OR<br />仅当由 expr1 不能确定表达式的值时，才会求解 expr2。</p><p>对于逻辑与操作符，一个很有价值的用法是：如果某边界条件使 expr2 的计算变得危险，则应在该条件出现之前，先让 expr1 的计算结果为 false。</p><p>不应该串接使用关系操作符</p><p>对于位操作符，由于系统不能确保如何处理其操作数的符号位，所以强烈建议使用unsigned整型操作数。</p><p>左移操作符（&lt;&lt;）在右边插入 0 以补充空位。对于右移操作符（&gt;&gt;），如果其操作数是无符号数，则从左边开始插入 0；如果操作数是有符号数，则插入符号位的副本或者 0 值，如何选择需依据具体的实现而定。移位操作的右操作数不可以是负数，而且必须是严格小于左操作数位数的值。</p><p>一般而言，标准库提供的 bitset 操作更直接、更容易阅读和书写、正确使用的可能性更高。而且，bitset 对象的大小不受 unsigned 数的位数限制。通常来说，bitset 优于整型数据的低级直接位操作。</p><p>IO 操作符为左结合</p><p>赋值操作符</p><p>赋值操作符的左操作数必须是非 const 的左值</p><p>数组名是不可修改的左值：因此数组不可用作赋值操作的目标。</p><p>赋值表达式的值是其左操作数的值，其结果的类型为左操作数的类型。</p><p>赋值操作具有右结合特性</p><p>赋值操作具有低优先级</p><p>谨防混淆相等操作符和赋值操作符</p><p>复合赋值操作符<br />+=&nbsp;&nbsp; -=&nbsp;&nbsp; *=&nbsp;&nbsp; /=&nbsp;&nbsp; %=&nbsp;&nbsp; // arithmetic operators<br />&lt;&lt;= &gt;&gt;=&nbsp;&nbsp; &amp;=&nbsp;&nbsp; ^=&nbsp;&nbsp; |=&nbsp;&nbsp; // bitwise operators</p><p>左操作数只计算了一次；而使用相似的长表达式时，该操作数则计算了两次，第一次作为右操作数，而第二次则用做左操作数</p><p>自增和自减操作符</p><p>因为前置操作返回加1后的值，所以返回对象本身，这是左值。而后置操作返回的则是右值。</p><p>建议：只有在必要时才使用后置操作符(为前置操作需要做的工作更少，只需加 1 后返回加 1 后的结果即可。而后置操作符则必须先保存操作数原来的值，以便返回未加 1 之前的值作为操作的结果)</p><p>后置操作符返回未加1的值</p><p>在单个表达式中组合使用解引用和自增操作</p><p>箭头操作符</p><p>C++ 为在点操作符后使用的解引用操作定义了一个同义词：箭头操作符（-&gt;）。假设有一个指向类类型对象的指针（或迭代器），下面的表达式相互等价：<br />(*p).foo; // dereference p to get an object and fetch its member named foo<br />p-&gt;foo;&nbsp;&nbsp; // equivalent way to fetch the foo from the object to which p points</p><p>条件操作符是 C++ 中唯一的三元操作符<br />cond ? expr1 : expr2;</p><p>避免条件操作符的深度嵌套</p><p>sizeof 操作符的作用是返回一个对象或类型名的长度，返回值的类型为 size_t，长度的单位是byte(字节)</p><p>将 sizeof 用于 expr 时，并没有计算表达式 expr 的值。特别是在 sizeof *p 中，指针 p 可以持有一个无效地址，因为不需要对 p 做解引用操作。</p><p>使用 sizeof 的结果部分地依赖所涉及的类型：<br />1. 对 char 类型或值为 char 类型的表达式做 sizeof 操作保证得 1<br />2. 对引用类型做 sizeof 操作将返回存放此引用类型对象所需的内在空间大小<br />3. 对指针做 sizeof 操作将返回存放指针所需的内在大小；注意，如果要获取该指针所指向对象的大小，则必&nbsp;须对指针进行引用<br />4. 对数组做 sizeof 操作等效于将对其元素类型做 sizeof 操作的结果乘上数组元素的个数</p><p>sizeof 返回整个数组在内存中的存储长度，所以用 sizeof 数组的结果除以 sizeof 其元素类型的结果，即可求出数组元素的个数</p><p>逗号表达式是一组由逗号分隔的表达式，这些表达式从左向右计算。逗号表达式的结果是其最右边表达式的值。</p><p>含有两个或更多操作符的表达式称为复合表达式，注意优先级和结合性！</p><p>圆括号凌驾于优先级之上</p><p>求值顺序</p><p>C++中，规定了操作数计算顺序的操作符还有条件（?:）和逗号操作符。除此之外，其他操作符并未指定其操作数的求值顺序。</p><p>建议：复合表达式的处理 <br />1. 如果有怀疑，则在表达式上按程序逻辑要求使用圆括号强制操作数的组合。<br />2. 如果要修改操作数的值，则不要在同一个语句的其他地方使用该操作数。如果必须使用改变的值，则把该表达式分割成两个独立语句：在一个语句中改变该操作数的值，再在下一个语句使用它。<br />3.一个表达式里，不要在两个或更多的子表达式中对同一对象做自增或自减操作。<br />第二个规则有一个重要的例外：如果一个子表达式修改操作数的值，然后将该子表达式的结果用于另一个子表达式，这样则是安全的，例如，*++iter 。</p><p>new 和 delete 表达式动态创建和释放单个对象</p><p>C++ 使用直接初始化（direct-initialization）语法规则初始化动态创建的对象。如果提供了初值，new 表达式分配到所需要的内存后，用给定的初值初始化该内存空间。</p><p>如果不提供显式初始化，对于类类型的对象，用该类的默认构造函数初始化；而内置类型的对象则无初始。</p><p>可对动态创建的对象做值初始化（value-initialize）<br />值初始化的 () 语法必须置于类型名后面，而不是变量后。</p><p>内容为空的圆括号表示虽然要做初始化，但实际上并未提供特定的初值。对于提供了默认构造函数的类类型（例如 string），没有必要对其对象进行值初始化：无论程序是明确地不初始化还是要求进行值初始化，都会自动调用其默认构造函数初始化该对象。而对于内置类型或没有定义默认构造函数的类型，采用不同初始化方式则有显著的差别.</p><p>如果指针指向不是用 new 分配的内存地址，则在该指针上使用 delete 是不合法的。</p><p>C++ 保证：删除 0 值的指针是安全的。</p><p>删除指针后，该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存，但该对象已经不再存在了。悬垂指针往往导致程序错误，而且很难检测出来。</p><p>建议：一旦删除了指针所指向的对象，立即将指针置为 0，这样就非常清楚地表明指针不再指向任何对象。</p><p>C++ 允许动态创建 const 对象<br />动态创建的 const 对象必须在创建时初始化，并且一经初始化，其值就不能再修改<br />由于 new 返回的地址上存放的是 const 对象，因此该地址只能赋给指向 const 的指针。</p><p>对于类类型的 const 动态对象，如果该类提供了默认的构造函数，则此对象可隐式初始化<br />内置类型对象或未提供默认构造函数的类类型对象必须显式初始化。</p><p>即使 delete 表达式的操作数是指向 int 型 const 对象的指针，该语句同样有效地回收 pci 所指向的内容。</p><p>下面三种常见的程序错误都与动态内存分配相关：<br />1. 删除（ delete ）指向动态分配内存的指针失败，因而无法将该块内存返还给自由存储区。删除动态分配内存失败称为&ldquo;内存泄漏（memory leak）&rdquo;。内存泄漏很难发现，一般需等应用程序运行了一段时间后，耗尽了所有内存空间时，内存泄漏才会显露出来。<br />2. 读写已删除的对象。如果删除指针所指向的对象之后，将指针置为 0 值，则比较容易检测出这类错误。<br />3. 对同一个内存空间使用两次 delete 表达式。当两个指针指向同一个动态创建的对象，删除时就会发生错误。如果在其中一个指针上做 delete 运算，将该对象的内存空间返还给自由存储区，然后接着 delete 第二个指针，此时则自由存储区可能会被破坏。</p><p>类型转换<br />如果两个类型之间可以相互转换，则称这两个类型相关。</p><p>C++ 定义了算术类型之间的内置转换以尽可能防止精度损失</p><p>如果赋值操作的左右操作数类型不相同，则右操作数会被转换为左边的类型。</p><p>在下列情况下，将发生隐式类型转换:<br />1. 在混合类型的表达式中，其操作数被转换为相同的类型<br />2. 用作条件的表达式被转换为 bool 类型<br />3. 用一表达式初始化某个变量，或将一表达式赋值给某个变量，则该表达式被转换为该变量的类型<br />4. 另外，在函数调用中也可能发生隐式类型转换</p><p>算术转换:最简单的转换为整型提升，对于所有比 int 小的整型，包括 char、signed char、unsigned char、short 和 unsigned short，如果该类型的所有可能的值都能包容在 int 内，它们就会被提升为 int 型，否则，它们将被提升为 unsigned int。如果将 bool 值提升为 int ，则 false 转换为 0，而 true 则转换为 1。</p><p>若表达式中使用了无符号（ unsigned ）数值，所定义的转换规则需保护操作数的精度。unsigned 操作数的转换依赖于机器中整型的相对大小.</p><p>包含 short 和 int 类型的表达式， short 类型的值转换为 int 。如果 int 型足够表示所有 unsigned short 型的值，则将 unsigned short 转换为 int，否则，将两个操作数均转换为 unsigned int 。</p><p>long 和 unsigned int 的转换也是一样的。只要机器上的 long 型足够表示 unsigned int 型的所有值，就将 unsigned int 转换为 long 型，否则，将两个操作数均转换为 unsigned long 。</p><p>在 32 位的机器上，long 和 int 型通常用一个字长表示，因此当表达式包含 unsigned int 和 long 两种类型，其操作数都应转换为 unsigned long 型。</p><p>对于包含 signed 和 unsigned int 型的表达式，其中的 signed 型数值会被转换为 unsigned 型。</p><p>其他隐式转换</p><p>在使用数组时，大多数情况下数组都会自动转换为指向第一个元素的指针</p><p>不将数组转换为指针的例外情况有：数组用作取地址（&amp;）操作符的操作数或 sizeof 操作符的操作数时，或用数组对数组的引用进行初始化时，不会将数组转换为指针。</p><p>指向任意数据类型的指针都可转换为 void* 类型</p><p>整型数值常量 0 可转换为任意指针类型</p><p>算术值和指针值都可以转换为 bool 类型</p><p>C++ 自动将枚举类型的对象或枚举成员（ enumerator ）转换为整型，其转换结果可用于任何要求使用整数值的地方</p><p>将 enum 对象或枚举成员提升为什么类型由机器定义，并且依赖于枚举成员的最大值。无论其最大值是什么， enum 对象或枚举成员至少提升为 int 型。</p><p>当使用非 const 对象初始化 const 对象的引用时，系统将非 const 对象转换为 const 对象。此外，还可以将非 const 对象的地址（或非 const 指针）转换为指向相关 const 类型的指针</p><p>显式转换也称为强制类型转换(cast)<br />虽然有时候确实需要强制类型转换，但是它们本质上是非常危险的。</p><p>因为要覆盖通常的标准转换，所以需显式使用强制类型转换<br />显式使用强制类型转换的另一个原因是：可能存在多种转换时，需要选择一种特定的类型转换。</p><p>命名的强制类型转换<br />cast-name&lt;type&gt;(expression);<br />其中 cast-name 为 static_cast、dynamic_cast、const_cast 和reinterpret_cast 之一，type 为转换的目标类型，而 expression 则是被强制转换的值。</p><p>dynamic_cast 支持运行时识别指针或引用所指向的对象</p><p>const_cast 将转换掉表达式的 const 性质。只有使用 const_cast 才能将 const 性质转换掉。除了添加或删除 const 特性，用 const_cast 符来执行其他任何类型转换，都会引起编译错误。</p><p>编译器隐式执行的任何类型转换都可以由 static_cast 显式完成.当需要将一个较大的算术类型赋值给较小的类型时，使用强制转换非常有用。当我们显式地提供强制类型转换时，警告信息就会被关闭.</p><p>reinterpret_cast 通常为操作数的位模式提供较低层次的重新解释，本质上依赖于机器。</p><p>建议：避免使用强制类型转换</p><p>旧式强制类型转换用圆括号将类型括起来实现</p><pre name="code" class="cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int main()
{
    // max value if shorts are 8 bits
    short short_value = 32767;
    short sv = 1;
    // this calculation overflows
    short_value += sv;
    cout &lt;&lt; &quot;short_value: &quot; &lt;&lt; short_value &lt;&lt; endl; 

    unsigned short ushort_value = 65535;
    ushort_value += sv;
    cout &lt;&lt; &quot;ushort_value: &quot; &lt;&lt; ushort_value &lt;&lt; endl;
    
    int ival1 = 21/6;  //  integral result obtained by truncating the remainder
    int ival2 = 21/7;  //  no remainder, result is an integral value
    cout &lt;&lt; &quot;21/6 = &quot; &lt;&lt; ival1 &lt;&lt; endl;
    cout &lt;&lt; &quot;21/7 = &quot; &lt;&lt; ival2 &lt;&lt; endl;
    
    int iv = 42;
    double dv = 3.14;
    cout &lt;&lt; iv % 12 &lt;&lt; endl;   //  ok: returns 6
    // iv % dv;  error: floating point operand
    
    cout &lt;&lt; &quot;21 % 6 = &quot; &lt;&lt; 21%6 &lt;&lt; endl;   //  ok: result is 3
    cout &lt;&lt; &quot;21 % 7 = &quot; &lt;&lt; 21%7 &lt;&lt; endl;   //  ok: result is 0
    cout &lt;&lt; &quot;-21 % -8 = &quot; &lt;&lt; -21%-8 &lt;&lt; endl;; //  ok: result is -5
    cout &lt;&lt; &quot;21 % -5 = &quot; &lt;&lt; 21%-5 &lt;&lt; endl;  //  machine-dependent: result is 1 or -4
    cout &lt;&lt; &quot;21 / 6 = &quot; &lt;&lt; 21/6 &lt;&lt; endl;   //  ok: result is 3
    cout &lt;&lt; &quot;21 / 7 = &quot; &lt;&lt; 21/7 &lt;&lt; endl;   //  ok: result is 3
    cout &lt;&lt; &quot;-21 / -8 = &quot; &lt;&lt; -21/-8 &lt;&lt; endl; //  ok: result is 2
    cout &lt;&lt; &quot;21 / -5 = &quot; &lt;&lt; 21 / -5 &lt;&lt; endl;  //  machine-dependent: result -4 or -5

    unsigned char bits = 0227;   // 100 100 111
    bits = ~bits;                // 001 011 000 (104 h)
    cout &lt;&lt; bits &lt;&lt; endl;        //will print h
    
    bits = 9;                    //   00001001
    cout &lt;&lt; (bits &lt;&lt; 1) &lt;&lt; endl; // left shift  00010010  will print 18
    cout &lt;&lt; (int)bits &lt;&lt; endl;    //bits isn't changed 
    cout &lt;&lt; (bits &lt;&lt; 2) &lt;&lt; endl; // left shift  00100100  will print 36
    cout &lt;&lt; (int)bits &lt;&lt; endl;    //bits isn't changed 
    cout &lt;&lt; (bits &gt;&gt; 3) &lt;&lt; endl; // right shift   00000001 will print 1
    cout &lt;&lt; (int)bits &lt;&lt; endl;    //bits isn't changed 
    
    unsigned char b1 = 0145;         // 01100101
    unsigned char b2 = 0257;         // 10101111
    unsigned char result1 = b1 &amp; b2; // 00100101   
    unsigned char result2 = b1 | b2; // 11101111   
    unsigned char result3 = b1 ^ b2; // 11001010   
    cout &lt;&lt; (int)result1 &lt;&lt; endl;    
    cout &lt;&lt; (int)result2 &lt;&lt; endl;
    cout &lt;&lt; (int)result3 &lt;&lt; endl;
    
    vector&lt;int&gt; ivec(10,8);
    vector&lt;int&gt;::iterator iter = ivec.begin();
    while (iter != ivec.end())
    {
     cout &lt;&lt; *iter++ &lt;&lt; endl; // iterator postfix increment
    }
    
    cout &lt;&lt; ( 1 &lt; 2 ? &quot;1&lt;2&quot; : &quot;1 &gt; 2&quot;) &lt;&lt; endl;
    
    int ia[] = {1,2,3,4,5,6};
    // sizeof(ia)/sizeof(*ia) returns the number of elements in ia
    cout &lt;&lt; sizeof(ia) &lt;&lt; endl;
    cout &lt;&lt; sizeof(*ia) &lt;&lt; endl;
    int sz = sizeof(ia)/sizeof(*ia);  //the length of array ia
    cout &lt;&lt; sz &lt;&lt; endl;
    
    int *pi = new int(1024);  // object to which pi points is 1024
    string *ps = new string(10, '9');    // *ps is &quot;9999999999&quot;
    cout &lt;&lt; *pi &lt;&lt; endl;
    cout &lt;&lt; *ps &lt;&lt; endl;
    delete pi;
    pi = 0;
    delete ps;
    ps = 0;
    
    int i;
    int *pi2 = &amp;i;
    string str = &quot;dwarves&quot;;
    double *pd = new double(33);
    //delete str;  error: str is not a dynamic object
    /**
     * delete pi2;   error: pi refers to a local  
     * Most compilers will accept this code, even though it is in error.
     */
    delete pd;  // ok
    pd = 0;

    int *ip = 0;
    delete ip; // ok: always ok to delete a pointer that is equal to 0
    
    // allocate and initialize a const object
    const int *pci = new const int(1024);
    delete pci ;
    pci = 0;
    
    bool           flag;         
    char           cval;
    short          sval;        
    unsigned short usval;
    int            ival;         
    unsigned int   uival;
    long           lval;         
    unsigned long  ulval;
    float          fval;         
    double         dval;
    3.14159L + 'a'; // promote 'a' to int, then convert to long double
    dval + ival;    // ival converted to double
    dval + fval;    // fval converted to double
    ival = dval;    // dval converted (by truncation) to int
    flag = dval;    // if dval is 0, then flag is false, otherwise true
    cval + fval;    // cval promoted to int, that int converted to float
    sval + cval;    // sval and cval promoted to int
    cval + lval;    // cval converted to long
    ival + ulval;   // ival converted to unsigned long
    usval + ival;   // promotion depends on size of unsigned short and int
    uival + lval;   // conversion depends on size of unsigned int and long
                 
    int ci;
    const int &amp;j = ci;   // ok: convert non-const to reference to const int
    const int *p = &amp;ci; // ok: convert address of non-const to address of a const
    
    ival *= static_cast&lt;int&gt;(dval);  //cast
    
    return 0;   
}</pre><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://stonelet.javaeye.com/blog/161979#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 12 Feb 2008 21:52:03 +0800</pubDate>
        <link>http://stonelet.javaeye.com/blog/161979</link>
        <guid>http://stonelet.javaeye.com/blog/161979</guid>
      </item>
      <item>
        <title>数组和指针</title>
        <author>clskkk2222</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://stonelet.javaeye.com">clskkk2222</a>&nbsp;
          链接：<a href="http://stonelet.javaeye.com/blog/161977" style="color:red;">http://stonelet.javaeye.com/blog/161977</a>&nbsp;
          发表时间: 2008年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>数组是由类型名、标识符和维数组成的复合数据类型</p><p>数组也是一种存储单一数据类型对象的容器，其中每个对象都没有单独的名字，而是通过它在数组中的位</p><p>置对它进行访问。<br />数组的长度是固定的，数组一经创建，就不允许添加新的元素。</p><p>Note:<br />数组的维数必须用值大于等于1的常量表达式定义<br />常量表达式只能包含整型字面值常量、枚举常量或者用常量表达式初始化的整型 const 对象。<br />非 const 变量以及要到运行阶段才知道其值的 const 变量都不能用于定义数组的维数。<br />(可能会引起不可预料的错误)</p><p>数组定义中的类型名可以是内置数据类型或类类型;<br />除引用之外，数组元素的类型还可以是任意的复合类型;<br />没有所有元素都是引用的数组.</p><p>如果没有显式提供元素初值，则数组元素会像普通变量一样初始化:<br />在函数体外定义的内置数组，其元素均初始化为 0。<br />在函数体内定义的内置数组，其元素无初始化。<br />不管数组在哪里定义，如果其元素为类类型，则自动调用该类的默认构造函数进行初始化；如果该类没有</p><p>默认构造函数，则必须为该数组的元素提供显式初始化。<br />显式初始化的数组不需要指定数组的维数值，编译器会根据列出的元素个数来确定数组的长度：</p><p>如果指定了数组维数，那么初始化列表提供的元素个数不能超过维数值。如果维数大于列出的元素初值个</p><p>数，则只初始化前面的数组元素；剩下的其他元素，若是内置类型则初始化为0，若是类类型则调用该类</p><p>的默认构造函数进行初始化</p><p>数组操作</p><p>数组下标从零开始,下标是数组元素到开始的偏移量.<br />数组中元素在内存中连续存放，低编号在低地址，高编号在高地址；</p><p>在用下标访问元素时，vector 使用 vector::size_type 作为下标的类型，而数组下标的正确类型则是 </p><p>size_t</p><p>在使用数组时，也必须保证其下标值在正确范围之内，即数组在该下标位置应对应一个元素。</p><p>不允许数组直接复制和赋值<br />个数组不能用另外一个数组初始化，也不能将一个数组赋值给另一个数组</p><p>特殊的字符数组</p><p>字符数组既可以用一组由花括号括起来、逗号隔开的字符字面值进行初始化，也可以用一个字符串字面值</p><p>进行初始化。<br />字符串字面值包含一个额外的空字符（null）用于结束字符串。当使用字符串字面值来初始化创建的新数</p><p>组时，将在新数组中加入空字符</p><p>1.输入输出时字符数组名都处理成字符串，对字符变量的地址在输入输出时都会处理成字符串，只有字符</p><p>变量地址这样规定；<br />2.'\0'是字符串唯一合法的结束标志；<br />3.c++中字符串相比：<br />&nbsp;(1)加头文件&lt;string&gt;,格式：变量名＝＝&ldquo;字符串&rdquo;；相等为1，不等为0；＝＝只比地址不比内</p><p>容；<br />&nbsp;(2)加头文件&lt;cstring&gt;,&nbsp;格式：strcmp（字串1，字串2）；结果相等为0，不等结果随意；<br />4.以下操作需加 头文件&lt;cstring&gt;<br />&nbsp;(1)strcpy（变量名，&ldquo;字符串&rdquo;）：意为把字符串放入字符数组中，是覆盖<br />&nbsp;(2)strcat（变量名，&ldquo;字符串&rdquo;）：意为追加在字符数组后面，追加时需保证字符数组足够大</p><p>，以免越界；<br />&nbsp;(3)strlen（数组名）：计算字符串(不包含'\0')有多长;<br />5.char型数组输出：cout&lt;&