程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 《C++標准程序庫》學習筆記(一)C++相關特性,群眾路線學習筆記

《C++標准程序庫》學習筆記(一)C++相關特性,群眾路線學習筆記

編輯:C++入門知識

《C++標准程序庫》學習筆記(一)C++相關特性,群眾路線學習筆記


  抱著本厚厚的《C++標准庫》讀了幾天,想想也該寫點關於用法的總結,一來怕今後容易忘記,二來將書上的事例重新敲一遍,鞏固對程序庫相關知識的了解。今天開第一篇,以後不固定更新。當然,筆者所讀為該書為基於C++98的第一版,已有一定的年代感,不過雖然C++11的推出已有一定的時日,但是在普及上還需要一定的時間,因而,這本中文譯本還是有一定的可讀性的。這本書更新的版本為英文第二版,很遺憾還未出現其中文譯本。

  由於是開篇,本文所講都很基礎,但這些基礎內容對後面的學習是非常重要的。

1  C++標准

  C++的標准化是一個漫長的過程。為何要標准化?作為一個語言,如果不設立相應的標准規格那是萬萬不能的。世界上有很多C++程序員,他們都有各自的編程風格,如果不做統一的話,那麼這門語言將會演變出相當多個版本,這也會讓C++進入分崩離析的狀態,其中的一個嚴重後果就是兼容性問題,因而,標准化是必不可少的,標准規格的建立,是C++的一個重要裡程碑。

  標准程序庫是C++標准規格的一部分,提供一系列核心組件,用以支持I/O、字符串(string)、容器(數據結構)、算法(排序、搜索、合並等等)、數值計算、國別(例如不同字符集,character set)等主題。

2  C++語言特性

  C++語言核心和C++程序庫是同時被標准化的。C++每5年會有一個新版本。如果你沒有緊跟其發展,可能會對某些新的語言特性大感驚訝。接下來的幾個小節,將描述與C++標准程序庫有關的幾個最重要的語言特性。

2.1   template(模板)

  程序庫中幾乎所有東西都被設計為template形式。不支持template,就不能使用標准程序庫。

  所謂template,是針對“一個或多個尚未明確的類型”所撰寫的函數或類別。使用templete時,可以顯式地或隱式地將類型當做參數來傳遞。下面的一個典型例子,傳回兩數中的較大數:

template <class T>
const T& max( const T& a, const T& b)
{
    return a < b ? b : a;
}

  在這裡,第一行將T定義為任意數據類型,在函數被調用時由調用者指定。任何合法標示符都可以拿來做參數名,但通常以T表示,這差不多成了一個“准”標准。這個類別用關鍵字class引導,但類型本身不一定得是class-------任何數據類型只要提供template定義式內所用到的操作,都可適用於此template。

  遵循同樣原則,你可以將class參數化,並以任意類型作為實際參數。這一點對容器類別非常有用。你可以實現出“有能力操控任意類型元素”的容器。C++標准程序庫提供了許多模板容器類。

  欲實現C++標准程序庫的完整功能,編譯器不僅要提供一般的template支持,還需要很多新的template標准特性,以下分別探討。

2.1.1  Nontype Templates參數(非類型模板參數)

  類型(type)可以作為模板(template)參數,非類型(nontype)也可以作為模板(template)參數。因而後者可被看為前者的一部分。例如,可以把標准類型bitset<>的bits數量以template參數指定。以下定義了兩個由bits構成的容器,分別為32個bits空間和50個bits空間:

    bitset<32> flags32; //32個bits空間的bitset
    bitset<50> flags50; //50個bits空間的bitset

  這些bitsets由於使用不同的template參數,所以有不同的類型,不能互相復制(assign)或比較(除非提供了相應的類型轉換機制)。

2.1.2  Default Template Parameters(缺省模板參數)

  template classes可以缺省參數。例如一下聲明,允許你使用一個或兩個template參數來聲明MyClass對象:

template <class T, class container = vector<T> >    //注意兩個>之間必須有一個空格,如果沒寫空格“>>”會被解讀為移位運算子,導致語法錯誤。
class MyClass;

  如果只傳給它一個參數,那麼缺省參數可作為第二個參數使用:

    //以下兩條語句等價
    MyClass<int> x1;
    MyClass<int, vector<int> > x1;

  注意:template缺省參數可根據前一個(或前一些)參數定義。

2.1.3  關鍵字typename

關鍵字typename被用來作為類型之前的標識符號。考慮下面例子:

template <class T>
class MyClass {
    typename T::SubType* ptr;
    //To do something...
};

  這裡,typename指出SubType是class T中定義的一個類型,因此ptr是一個指向T::SubType類型的指針。如果沒有關鍵字typename,Subtype會被當成一個static成員,於是:

         T::Subtype * ptr 會被解釋為類型T內的數值Subtype與ptr的乘積。

         Subtype成為一個類型的條件是,任何一個用來取代T的類型,其內都必須提供一個內部類型(inner type)Subtype的定義。例如,將類型Q當做template參數:

    MyClass<Q> x;

         必要條件是類型Q有如下的內部類型定義:

class Q {
    typedef int SubType;
    //To do something...
};

  注意,如果要把一個template中的某個標識符號指定為一種型別,就算意圖顯而易見,關鍵字typename也不可或缺,因此C++的一般規則是,除了以typename修飾之外,template內的任何標識符號都被視為一個值(value)而非一個類型。

  typename還可以在template聲明中用來替換關鍵字class:

template <typename T> class MyClass;

2.1.4  Member Template(成員模板)

  類成員函數可以是個template(模板),但是這樣的成員模板既不能是virtual,也不能缺省參數。例如:

class MyClass {
    //To do something...
    template<class T> void f(T);
    //To do something...
};

  在這裡,MyClass::f() 聲明了一個成員函數集,適用任何類型參數。只要某個類型提供有f()用到的所有操作,它就可以被當做參數傳遞進去。這個特性通常用來為模板類中的成員提供自動類型轉換。例如以下定義式中,assign()的參數x,其類型必須和調用端所提供的對象的類型完全吻合。

template <class T>
class MyClass {
private:
    T value;
public:
    void assign(const MyClass<T>& x) {  //x必須和*this有同樣的T
        value = x.value;
    }
    //To do something...
};

  即使兩個類型之間可以自動轉換,如果我們對assign()使用不同的template類型,也會出錯:

void f()
{
    MyClass<double> d;
    MyClass<int> i;

    d.assign(d);    //OK
    a.assign(i);    //錯誤,i是MyClass<int>,但這裡需要MyClass<double>
}

  如果C++允許我們為成員函數提供不同的template類型,就可以放寬“必須精確吻合”這條規則:只要類型可被賦值,就可以被當做上述成員模板函數的參數。

template <class T>
class MyClass {
private:
    T value;
public:
    template <class X>  //成員模板
    void assign(const MyClass<X>& x) {   //允許不同的模板類型
        value.x.getValue();
    }
    T getValue() const {
        return value;
    }
    //To do something...
};
void f()
{
    MyClass<double> d;
    MyClass<int> i;

    d.assign(d);    //OK
    a.assign(i);    //OK,int對於double是可賦值的。
}

  請注意,現在,assign()的參數x和*this的類型並不相同,所以你不能夠直接存取MyClass<>的private成員和protected成員,取而代之的是,此例中你必須使用類似getValue()之類的東西。

  模板構造函數(templeta constructor)是成員模板的一種特殊形式,它通常用於“在復制對象時實現隱式類型轉換”。注意,模板構造函數並不遮蔽隱式的復制構造函數。如果類型完全吻合,隱式復制構造函數就會被產生出來並被調用。舉個例子:

template <class T>
class MyClass {
public:
    //帶隱式類型轉換的復制構造函數並不遮蔽隱式的復制構造函數
    template <class U> MyClass(const &MyClass<U>& x);
};
void f()
{
    MyClass<double> xd;

    MyClass<double> xd2(xd);    //調用內建復制構造函數(隱式)
    MyClass<int> xi(xd);        //調用模板構造函數
    //To do something...
}

  在這裡,xd2和xd的類型完全一致,所以它被內建的復制構造函數初始化,xi和xd的類型不同,所以它使用模板構造韓式進行初始化。因此,撰寫模板復制構造函數時,如果default輔助構造函數不符合你的要求,別忘了自己提供一個復制構造函數。

2.1.5  嵌套模板類

  嵌套類本身也可以是個template:

template <class T>
class MyClass {
    //To do something...

    template <class T2> //嵌套類
    class NestedClass;

    //...
};

2.2   基本類型的顯式初始化

  如果采用不含參數的、明確的構造函數調用語法,基本類型會被初始化為零:

    int i1;         //未定義的值
    int i2 = int(); //初始化為0

  這個特性可以確保我們在撰寫template程序代碼時,任何型別都有一個確切的初值。例如下面的這個函數中,x保證被初始化為零。

template <class T> void f()
{
    T x = T();
    //...
}

2.3   異常處理

  通過異常處理,C++標准程序庫可以在不污染函數接口(亦即參數和返回值)的情況下處理異常。要注意的是這種概念叫做“異常處理”,而不是“錯誤處理”,兩者未必相同。舉個例子,許多時候用戶的無效輸入並非一種異常;這時候最好是在區域范圍內采用一般的錯誤處理技術來處理。

  C++標准庫提供了一些通用的異常處理特性,例如標准異常類別(standard exception classes)和auto_ptr類別。

2.4   Namespaces(命名空間)

  越來越多的軟件由程序庫、模塊和組件拼湊而成。各種不同事物的結合,可能導致一場名稱大沖突。Namespaces正是用來解決此問題的。

  namespaces將不同的標識符號集合在一個具名作用域(names scope)內。如果你在namespace之內定義所有標識符號,則namespace本身就成了唯一可能與其他全局符號沖突的標識符號。你必須在標識符號前加上namespace名字,才能援引namespace內的符號,這和class處理方式雷同。namespace的名字和標識符號之間以::分割開來(這個符號及其意義和class與其成員之間的聯系有點類似)。

//在命名空間josuttis中定義標識符號
namespace josuttis {
    class File;
    void myGlobalFunc();

    //...
}
//使用命名空間中的標識符
josuttis::File obj;
josuttis::myGlobalFunc();

  不同於class的是,namespace是開放的,你可以在不同模塊中定義和擴展namespace。因此你可以使用namespace來定義模塊、程序庫或組件,甚至在多個文件之間完成。namespace定義的是邏輯模塊,而非實質模塊。請注意,在UML及其他建模表示法中,模塊又被成為package。

  如果某個函數的一個或多個參數類型,定義於函數所處的namespace中,那麼你可以不必為該函數指定namespace。這個規則成為Koenig lookup。例如:

//在命名空間josuttis中定義標識符號
namespace josuttis {
    class File;
    void myGlobalFunc(File& );

    //...
}
//使用命名空間中的標識符
josuttis::File obj;
myGlobalFunc(obj);

           通過using聲明(using declaration),我們可以避免一再寫出冗長的namespace名稱,例如一下聲明:

using josuttis::File;       //using declaration

           會使File成為當前作用域內代表josuttis::File的一個同義字。

           Using directive會使namespace內的所有名字曝光,它等於將這些名字聲明於namespace之外。但這麼一來,名稱沖突問題可能死灰復燃。例如:

using namespace josuttis;   //using directive

           會使File和myGlobalFunc()在當前作用域內完全曝光。如果全局范圍內已存在同名的File和myGlobalFunc(),而且使用者不加任何資格飾詞地使用這兩個名字,編譯器將東西難辨。

           注意,如果場合(contex)不甚清楚(例如不清楚是在頭文件、模塊裡還是在程序庫裡),你不應該使用using directive。這個指令可能會改變namespace的作用域,從而使程序代碼被包含或使用於另一模塊中,導致意外的行為發生。事實上在頭文件中使用using directive相當不明智。

           C++標准程序庫在namespace std中定義了它的所有標識符號。

2.5   Bool類型

  為了支持布爾值(真假),C++增加了bool類型。Bool可增加程序可讀性,並允許你對bool值實現重載動作。兩個常數true和false同時亦被引入C++。此外C++還提供bool值與整數值之間的自動轉換。0值相當於false,非0值相當於true。

2.6   關鍵字explicit

  通過關鍵字explicit的作用,我們可以禁止“單參數構造函數”被用於自動類型轉換。典型的例子便是群集類型。你可以將初始長度作為參數傳給構造函數,例如你可以聲明一個構造函數,以stack的初始大小為參數:

class Stack {
    explicit Stack(int size);   //用初始大小size構造Stack
    //...
};

  在這裡,explicit的應用非常重要。如果沒有explicit,這個構造函數有能力將一個Int自動轉型為Stack。一旦發生這種情況,你甚至可以給Stack指派一個整數值而不會引起任何問題:

    Stack s;
    s = 40; //糟糕,創建了一個有40個元素的Stack,並把它賦值給了s

  自動類型轉換動作會把40轉換為有40個元素的Stack,並指派給s,這幾乎肯定不是我們所要的結果。如果我們將構造函數聲明為explicit,上述賦值操作就會導致編譯錯誤(那很好)。

  注意,explicit同樣也能阻絕“以賦值語法進行帶有轉型操作的初始化”:

    Stack s1(40);    //OK
    Stack s2 = 40;   //Error

  這是因為一下兩組操作:

    X x;
    Y y(x);     //顯式轉換

  和

    X x;
    Y y = x;     //隱式轉換

  存在一個小差異。前者透過顯式轉換,根據類型X產生了一個類型Y的新對象;後者通過隱式轉換,產生了一類型Y的新對象。

2.7   新的類型轉換操作符

  為了讓你對“參數的顯式類型轉換”了解更加透徹,C++引入一下4個新的操作符:

2.7.1  static_cast

  將一個值以符合邏輯的形式轉換。這可看做是“利用原值重建一個新對象,並在設立初值時使用類型轉換”。唯有當上述的類型轉換有所定義,真個轉換才會成功。所謂“有所定義”,可以是語言內建規則,也可以是程序員自定的轉換動作。例如:

    float x;
    cout << static_cast<int>(x);        //打印int型的x
    f(static_cast<string>("hello"));    //調用f(),參數為string類型,而不是char*

2.7.2  dynamic_cast

  將多態類型向下轉型為其實際靜態類型。這是唯一在執行期進行檢驗的轉型動作。你可以用它來檢驗多個多態對象的類型,例如:

class Car;       //抽象的基類,至少用於一個虛函數
class Cabriolet : public Car {
    //...
};
class Limousine : public Car {
    //...
};
void f(Car* cp)
{
    Cabriolet* p = dynamic_cast<Cabriolet*>(cp);
    if(p == NULL) {
        //...
    }
}

  在這個例子中,面對實際靜態類型為Cabriolet的對象,f()有特殊應對行為。當參數是個reference,而且類型轉換失敗時,dynamic_cast會丟出一個bad_cast異常。注意,從設計者角度而言,你應該在運用多態技術的程序中,避免這種“程序行為取決於具體類型”的寫法。

2.7.3  const_cast

  設定或去除類型的常數性,亦可去除volatile飾詞。除此之外不允許任何轉換。

2.7.4  reinterpret_cast

  此操作符的行為由實際編譯器定義。可能重新解釋bits意義,但也不一定如此,使用此一轉型動作通常帶來不可移植性。

 

  這些操作符取代了以往小圓括號所代表的舊式轉型,能夠清楚闡明轉型的目的。小圓括號轉型可替換dynamic_cast之外的其他三種轉型,也因此當你運用它時,你無法明確顯示使用它的確切理由。這些新式轉型操作給了編譯器更多信息,讓編譯器清楚知道轉型的理由,並在轉型失敗時釋出一份錯誤報告。

  注意,這些操作符只接受一個參數。試看下面例子:

    static_cast<Fraction>(15, 100) //糟糕,創建了Fraction(100)

  在這個例子中你得不到你想要的結果。它只用一個數值100,將Fraction暫時對象設定初值,而非設定分子15、分母100。逗號在這裡並不起分隔作用,而是形成一個comma操作符,將兩個表達式組合為一個表達式,並傳回第二個表達式作為最終結果。將15和100“轉換”為分數的正確做法是:

    Fraction(15, 100);

2.8   常數靜態成員的初始化

  如今,我們終於能夠在class聲明中對“整數型常數靜態成員”直接賦予初值。初始化後,這個常數便可用於class之中,例如:

class MyClass {
    static const int num = 100;
    int elems[num];
};

  注意,你還必須為class之中聲明的常數靜態成員,定義一個空間:

const int MyClass::num; //不用初始化

2.9   main()的定義式

  在C++中有一個重要而又常被誤解的問題,那就是正確而可移植的main()的唯一寫法。根據C++標准,只有兩種main()是可移植的:

int main()
{
    //...
}

  和

int main(int argc, char* argv[])
{
    //...
}

  這裡argv(命令行參數數組)也可以定義為char**。請注意,由於不允許“不言而喻”的返回類型int,所以返回類型必須明白寫為int。你可以使用return語句來結束main(),但不必一定如此。這一點和C不同,換句話說,C++在main()的末尾定義了一個隱式地:

    return 0;

  這意味著如果你不采用return語句離開main(),實際上就表示成功退出(傳回任何一個非0值都代表某種失敗)。處於這個原因,本范例在main()尾端都沒有return語句。有些編譯器可能會對此發出警告,那正是標准制定前的黑暗日子。

3 總結

  第一篇到此結束,關於復雜度的問題我在此省略,相信讀者能理解。


高一必修一數學學習筆記, 與 總結

第一章 集合與函數概念
一、集合有關概念
1、集合的含義:某些指定的對象集在一起就成為一個集合,其中每一個對象叫元素。
2、集合的中元素的三個特性:
1.元素的確定性; 2.元素的互異性; 3.元素的無序性
說明:(1)對於一個給定的集合,集合中的元素是確定的,任何一個對象或者是或者不是這個給定的集合的元素。
(2)任何一個給定的集合中,任何兩個元素都是不同的對象,相同的對象歸入一個集合時,僅算一個元素。
(3)集合中的元素是平等的,沒有先後順序,因此判定兩個集合是否一樣,僅需比較它們的元素是否一樣,不需考查排列順序是否一樣。
(4)集合元素的三個特性使集合本身具有了確定性和整體性。
3、集合的表示:{ … } 如{我校的籃球隊員},{太平洋,大西洋,印度洋,北冰洋}
1. 用拉丁字母表示集合:A={我校的籃球隊員},B={1,2,3,4,5}
2.集合的表示方法:列舉法與描述法。
注意啊:常用數集及其記法:
非負整數集(即自然數集) 記作:N
正整數集 N*或 N+ 整數集Z 有理數集Q 實數集R
關於“屬於”的概念
集合的元素通常用小寫的拉丁字母表示,如:a是集合A的元素,就說a屬於集合A 記作 a∈A ,相反,a不屬於集合A 記作 aA
列舉法:把集合中的元素一一列舉出來,然後用一個大括號括上。
描述法:將集合中的元素的公共屬性描述出來,寫在大括號內表示集合的方法。用確定的條件表示某些對象是否屬於這個集合的方法。
①語言描述法:例:{不是直角三角形的三角形}
②數學式子描述法:例:不等式x-3>2的解集是{xR| x-3>2}或{x| x-3>2}
4、集合的分類:
1.有限集 含有有限個元素的集合
2.無限集 含有無限個元素的集合
3.空集 不含任何元素的集合 例:{x|x2=-5}
二、集合間的基本關系
1.“包含”關系—子集
注意: 有兩種可能(1)A是B的一部分,;(2)A與B是同一集合。
反之: 集合A不包含於集合B,或集合B不包含集合A,記作A B或B A
2.“相等”關系(5≥5,且5≤5,則5=5)
實例:設 A={x|x2-1=0} B={-1,1} “元素相同”
結論:對於兩個集合A與B,如果集合A的任何一個元素都是集合B的元素,同時,集合B的任何一個元素都是集合A的元素,我們就說集合A等於集合B,即:A=B
① 任何一個集合是它本身的子集。AA
②真子集:如果AB,且A B那就說集合A是集合B的真子集,記作A B(或B A)
③如果 AB, BC ,那麼 AC
④ 如果AB 同時 BA 那麼A=B
3. 不含任何元素的集合叫做空集,記為Φ
規定: 空集是任何集合的子集, 空集是任何非空集合的真子集。
三、集合的運算
1.交集的定義:一般地,由所有屬於A且屬於B的元素所組成的集合,叫做A,B的交集.
記作A∩B(讀作”A交B”),即A∩B={x|x∈A,且x∈B}.
2、並集的定義:一般地,由所有屬於集合A或屬於集合B的元素所組成的集合,叫做A,B的並集。記作:A∪B(讀作”A並B”),即A∪B={x|x∈A,或x∈B}.
3、交集與並集的性質:A∩A = A, A∩φ= φ, A∩B......余下全文>>
 

某同學在復習元素化合物的性質時,做了如下學習筆記,你一起完成一,氯氣的顏色為何?由氯的原子結

氯氣為黃綠色,具有很強的氧化性;方程式:Fe+Cl2===FeCl3
與氫氣反應有蒼白色火焰
正確。氯氣可以與變價金屬反應使之生成最高價化合物,也可以與非金屬化合。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved