C++語言筆記C11庫

1.C++函數庫python

Algorithms ios

<algorithm> c++

C Library Wrappers 算法

<cassert> , <cctype><cerrno><cfenv><cfloat><cinttypes><ciso646><climits><clocale><cmath><csetjmp><csignal><cstdarg><cstdbool><cstddef><cstdint><cstdio><cstdlib><cstring><ctgmath><ctime><cwchar><cwctype> 編程

Containers windows

Sequence 設計模式

<array> 、<deque><forward_list><list><vector> 數組

Ordered Associative promise

<map> , <set> 緩存

Unordered Associative 

<unordered_map> , <unordered_set> 

Adaptor 

<queue> , <stack> 

Error Handling 

<exception><stdexcept><system_error> 

Input/Output 

<filesystem> , <fstream><iomanip><ios><iosfwd><iostream><istream><ostream><sstream><streambuf><strstream> 

Iterators Library 

<iterator> 單獨的迭代器一般用於泛型算法中,而不會單用

Localization 

<codecvt> , <cvt/wbuffer><cvt/wstring><locale> 

Math and Numerics 

<complex> , <limits><numeric><random><ratio><valarray> 

Memory Management 

<allocators> , <memory><new><scoped_allocator> 

Multithreading 

<atomic> , <condition_variable><future><mutex><thread> 

Other Utilities 

<bitset> , <chrono><functional><initializer_list><tuple><type_traits><typeinfo><typeindex><utility> 

Strings and Character Data 

<regex> , <string> 

 

1.C++命名空間
C++只有一個命名空間std,包含了全部的C++庫。關於命名空間,同一個命名空間的名字能夠在不一樣的文件或同一文件中屢次出現,也能夠出如今頭文件裏面,表示同一命名空間的不一樣部分代碼。

2.C++引用與指針
C++的引用通常用於函數入參和函數返回值。引用和指針其實存在本質的區別,引用就是一個對象的別名,沒有分配存儲對象指針的存儲空間;而指針須要分配一個存儲對象指針的存儲空間。因此指針和引用通常狀況下是沒有辦法相互轉換的。C++中,若是對象沒有名字(如用new分配的對象)那麼它也不可能有別名,而其餘語言(如C#)能夠只有別名。

3.C++的模板:針對類型的一種統一操做
C++的模板分爲函數模板和類模板,二者聲明的形式都同樣。
template <class T1,...,class Tn> 函數或類聲明
{
函數體或類體
}
函數模板在調用時自動實例化,而類模板必須先實例化(指明類型),才能調用。

4.關於throw的棧展開
throw進行棧展開時會調用不少析構函數或進行不少函數的局部變量回收,直到遇到了catch爲止。

5.操做符重載
操做符的本質是一個函數,其重載有兩種狀況:第一種操做符在類中做爲成員函數重載;第二種操做符做爲全局函數重載。兩種重載方式有一點區別:1)在類中做爲成員函數重載,將默認第一個函數入參(雙目時爲左操做數)爲this指向的本類對象,因此在類中重載操做符時,雙參在形式上變成了單參。2)基於第一條規則,有些左操做數特殊的操做符(如<<>>等要求左操做數必須爲流對象)不能在類中重載。在衆多的運算符中,大多對左右操做數是敏感的,不容許左右操做數交換。第一個入參是左操做數,第二個入參是右操做數。

6.友元
在類中定義一個友元函數(或重載的操做符)或友元對象,那麼在該友元中能夠以‘.’訪問該類對象的私有成員,這是在其餘非友元做用域裏作不到的。
Class A{private :int a ;friend void myfunc(void);}
void myfunc(void)
{
A  myA;
cout<<myA.a;//在非友元做用域中不能這樣訪問。
}

7.容器
全部容器提供的內置方法絕大部分是與迭代器有關的,大部分泛型算法也與迭代器有關。
迭代器和指針是徹底同樣的,可是迭代器更安全。
容器在構造時可使用其餘不一樣類型的容器的迭代器或指針來構造,可是須要保證兩種容器或數組的數據類型是一致的。

8.

頭文件

容器

說明

<iostream>

istream

Wistream,wostream,wiostream

從流中讀寫數據

ostream

iostream

<fsteram>

ifstream

Wifstream,Wofstream,wiofstream

從文件中讀寫數據

ofsteram

fsteram

<sstream>

istringstream

Wistringstream,Wostringstream,wstringstream

字符串流中讀取數據(底層沒有字符串)

ostringstream

stringstream

在流對象使用中,若是產生任何失敗,則其對象爲0,不然爲非0,相應的錯誤狀態被記錄在該對象中。

例:Int a;Cin>>a;若是輸入了一個字符,那麼將產生錯誤。則cin==0If (cin) 將爲false。其它流都有這種用法。

流的做用:->本質上是字符處理。將字符從不一樣的底層以多種格式(或方式)輸入到內存中來,或者相反。如下是例舉的幾種流的做用:

(1)以單個字符、字符串、行任意形式存取字符

(2)以×××、浮點型等形式存取字符

wKioL1XpElngE4CEAAClZx9iNEQ694.jpg 

 

9.lambda 函數

返回值類型[可以使用的全局變量列表](形參列表){函數體}

若是「可以使用的全局變量列表」爲「=」,則能使用全部的全局變量

10.順序容器

特色:根據位置存儲和訪問容器,容器元素的排列次序與元素值無關,而由元素添加到容器裏的次序決定。標準庫只定義了三種順序容器:vector,list,deque.

和三種順序容器適配器(適配器是以基本的容器類型爲底層,經過定義新的操做接口,實現不一樣的數據操做):stack,queue,priority_queue

容器和容器適配器都只提供了少許操做,大部分操做由泛型算法庫提供。

容器的長度都是動態可增加縮短的。

容器元素的類型約束有兩點:元素類型必須支持賦值運算和元素類型對象必須可複製。(即不能爲引用和流)

迭代器的範圍爲[iter.begin,iter.end),其編程意義爲:當iter.begin== iter.end迭代器範圍爲空。

 

容器內置的操做主要有:

任意位置任意方式增減元素個數的操做,迭代操做,交換元素操做。

11.關聯容器
基本的有4種:map,set,multimap,multiset

map中有個很差的地方:

Map<string,int>word_count;

Int occurs=word_count[foobar];

這樣的查找若是foobar鍵不存在於word_count容器中,那麼將會增長一個foobar鍵,並返回值爲0。這是編譯語言與解釋語言的區別。.find方法提供了判斷該鍵是否存在的方法。

.count方法提供了判斷mapmultimap中鍵個數的方法。

python中能實現的操做,在C++中基本上都有這些功能。沒有相應的語法支持,可是能夠提供對象內置方法來解決(反射除外)

Set容器提供了insert,count,find,erase等操做。可使用其餘容器的迭代器來構造set容器,只保留不重複的元素集合。

除了容器內置的迭代器之外,C++還提供了3類獨立迭代器:

1)插入迭代器
back_inserter,front_inserter,inserter.使用容器構造

2)流迭代器
istream_iterator,ostream_iterator

3)反向迭代器
reverse_iterator

12.指向類成員的指針

Class screen{;}

定義指向數據成員的指針:Int screen::*ptr;定義了一個指向screenint類型成員的指針。

定義指向函數成員的指針:void (screen::*ptr)(void);定義了一個指向類函數的指針

指向類成員的指針能夠定義在類中也能夠定義在類外。

使用定義在類中的指向類成員的指針:兩個新的解引用操做符->*.*,他們可以將成員指針綁定到實際對象。

 

成員函數指針列表 :typedef void (*action)(void);

screen::action screen::menu[]={&screen::home,...}

13.auto自動數據類型

Auto int,double不一樣,他能推斷數據的類型,從而將其轉化爲推斷出的類型。

14.序列for循環

全部能使用iterator的序列都能使用序列for循環

Map<string,int>m{{a,1},{b,2},{c,3}};

For(auto p:m){cout <<p.first<<:<<p.second<<endl;}

15.c98和c11的智能指針-爲分配的對象提供異常安全

(1)auto_ptr 可以代理new,能賦值(與unique_ptr的區別),但同一時刻只能有一個auto_ptr管理指針,不帶有引用計數(c98),能管理任意非數組指針對象

(2)unique_ptr 可以代理new[]和new,不能轉讓,不容許拷貝構造和拷貝賦值(不能放在容器中,但unique_ptr能保存容器指針),不帶有引用計數,能夠徹底代替scoped_ptr和scoped_array(c11)

(3)shared_ptr可以代理new的指針,實現了引用計數(應該是new int了一個內存用來保存引用計數,當發生shared_ptr賦值時將引用計數加1並將引用計數指針賦給另一個shared_ptr),支持拷貝和賦值,能夠放入容器中且能夠保存容器指針(make_share模板函數)(最有價值的指針)(c11)

(4)shared_array代理new[]的指針,shared_array能力有限。通常使用shared_ptr<vector>或vector<shared_ptr>來替代(c11)

(5)weak_ptr 該指針是輔助shared_ptr的,shared_ptr給weak_ptr賦值不會增長引用計數(c11)

(6)boost/smart_ptr.hpp

scoped_ptr 接受new的指針,不能轉讓(不支持拷貝和複製(私有拷貝和複製函數實現),不放入做容器),且只能在本做用域內使用,與auto_ptr基本相同

scoped_array接受new[]的指針,不能轉讓(不支持拷貝和複製(私有拷貝和複製函數實現),不能用做容器),且只能在本做用域內使用,能夠用vector替代

16.一些須要注意的C++編程細節

(1)環形緩存:
#define SIZE 100
int Buffer[SIZE];
Buffer[i%SIZE]='X'

(2)類的複製控制:
定義private的複製構造函數(如IO類),能夠保證該對象只有一個副本。經過設計模式還可使得該類只被構造一次。
classfa a;
classfa b=a;//實際上是調用了classfa的默認複製構造函數 classfa(const classfa &)
string c="ancd" //調用了字符構造函數和複製構造函數。

(3)順序容器:string,vector,list均可以使用.erase()和insert()在任意位置刪除和插入指定迭代器的節點。

(4)友元friend 是不支持繼承的。即類A是BASE的友元,但它不會是該BASE 的CHILD的友元。

(5)一個C標準函數:將從流中讀出的數據,從新壓入流的緩存中,能夠經過此函數實現peek()函數
    ungetc( char ch, FILE* f);   //向IO緩存中壓入數據
   一次只能壓入一個字符

(6)遞歸:(鏈接2個鏈表)
已知兩個鏈表head1 和 head2 各自有序,請把它們合併成一個鏈表依然有序,此次要求用遞歸方法進行。 
Node * MergeRecursive(Node *head1 , Node *head2) 

if ( head1 == NULL ) 
return head2 
if ( head2 == NULL) 
return head1 
Node *head = NULL   
if ( head1->data < head2->data ) 

head = head1   
head->next = MergeRecursive(head1->next,head2); 

else 

head = head2   
head->next = MergeRecursive(head1,head2->next); 

return head 
}

(7)引用:
常引用:不能經過引用改變變量的值
int b=1;
const int & a=b;
a=2;//錯
b=2;//對

string foo( ); 
void bar(string & s); 
那麼下面的表達式將是非法的:
bar(foo( )); 
bar("hello world"); 
    foo()的返回值必然是const string型的,「hello world」也被編譯器構造爲const string,而把他們用於非const函數入參,編譯器將會報錯。函數的引用入參應儘可能指明爲const,這樣const和非const的實參都不會報錯。
    引用做爲函數返回值類型的好處:在內存中不產生被返回值的副本;(注意:正是由於這點緣由,因此返回一個局部變量的引用是不可取的。由於隨着該局部變量生存期的結束,相應的引用也會失效,產生runtime error!

(8)求一個數中包含的二進制1的個數:
def func(x):
cont=0;
while(x):
  x=x&(x-1)
  cont=cont+1
return cont
求任意數據由二進制表示時包含的1的個數

(9)虛析構函數做用:
將析構函數定義爲virtual 這樣在用delete刪除基類指針時能夠運行子類的析構函數。

(10)mutable關鍵字:在const變量中被改變
c++ 中 mutable關鍵字申明的變量能夠在const申明的函數中改變。
class M
{mutable int a;
void set() const
{a=10;}
}

(11)靜態成員:
class M
{
static M a; //correct
M *b;   //correct
M c;     //error
}
靜態成員是類的一部分,而不是對象的一部分。

(12)const 用法:
const對象不能調用非const的成員函數和變量;爲了使const對象和非const對象均可以調用一個同名函數,能夠重載該非const和const成員函數。

(13)c數組:
在c/C++中數組是一種完整的數據類型,同結構體,對象同樣:
main( ){
  using namespace std;
  int num[5]={1,2,3,4,5};
  cout <<*((int *)(&num+1)-1) <
  }
  在C語言中,一維數組名錶示數組的首地址,並且是一個指針.如上例num,
  對&num,表示指針的指針.意味着這裏強制轉換爲二維數組指針.
  這樣 &num+1 等同於 num[5][1],爲代碼空間. (&num+1)-1表示 num[4][0].即num[4].

char (*a)[3][4] 定義一個數組指針

(14)switch的入參:
switch的參數不能是不能自動轉化爲整型的參數。
由於switch後面只能帶自動轉換爲×××(包括×××)的類型,好比字符型char,unsigned int等,實數型不能自動轉換爲×××.能夠手動強轉實數型(int)double,可是會致使精度的丟失.若是後面要對實數型作選擇的話,能夠乘以10的倍數,而後進行選擇,這樣不會丟失精度.可是這樣的話就要靠你去手動的控制乘以多少了

(15)sizeof

使用sizeof對變量求所佔內存長度時,是對該變量直接求內存長度,須要搞清楚,sizeof的對象是誰。

char *p=「dk4m7」;//sizeof(p),p內存爲4字節

char p[]="dk4m7";//sizof(p),p內存爲6字節,(p符號至關於一個引用,自己不佔內存)

(16)多維數組

char a[2][3][4]={{{1,2,3,4},{5,6,7,8},{9,10,11,12}},{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};

a符號表示一個引用,不佔內存。其指向的地址爲a[0],a[0][0],&a[0][0][0]

a表示一個3維數組,a+1表示最高維加1,至關於a[1]的首地址

*a表示一個2維數組,*a+2,表示2維數組加1,至關於a[0][2]的首地址

*(a+1)+2至關於a[1][2]的首地址

*(*(a+1)+2)+3至關於a[1][2][3]的首地址





c11新特徵

1.列表初始化

任何對象或對象數組均可以使用列表初始化,列表賦值不支持任何形式的類型轉化,包括浮點到×××的轉化。

例子:如下4種等價

int s=0;

int s={0};

int s{0};

int s(0);

vector<int> vi{1,2,3,4,5};

vector<int> *vi=new vector<int>{1,2,3,4,5};

vi={5,6};//任何形式的列表賦值

map<string,string> authors={{"joyce","james"},{"austen","jane"}};//關聯容器的列表初始化

pair<string,string>authors{"james","joyce"};//列表初始化pair


2.constexpr 編譯時驗證,賦值表達式是否爲常量賦值

constexpr int mf=20;

constexpr int linit=mf+1; //正確,編譯時計算

constexpr 也可用於函數,要求函數只用一個return語句,而且形參類型必須是字面值類型

3.類型別名申明

typedef double  base,*p;   /舊方法。  base是double的別名;p是double*的別名

using double=base

using double*=p             //新標準使用USING申明變量別名

4.auto 自動推斷賦值表達式的左值類型

auto b=20;

auto &a=b;

5.decltype  做用同auto,只能推斷表達式的類型。可是能夠靈活的指定表達式

const int ci=0,&cj=ci,p=&ci;

decltype(ci) x=0;

decltype(cj) y=x;   //指定推斷cj表達式的類型,推斷的類型爲引用

decltype(*p) z=10;  //對×解引用推斷的結果是引用,必須初始化

decltype((ci)) m=ci; //強制推斷爲引用  (3種引用推斷方式)

6.類實例化時,使用()和不使用()的區別

classA a;//不會進行處理爲顯式初始化的成員

classA a();//會將a的未初始化的成員默認初始化爲0 


7.範圍for

string str("some string");

for(auto c:str)


8.initializer_list 

a.initializer_list函數形參列表

void error_msg(ErrCode e,initializer_list<string> il){

 for( const auto &elem:il){

    cout<<elem<<"  "<<endl;

  }

}


調用:

 error_msg(ErrCode(42),{"func1","ok","sd"});

 

 b.initializer_list函數返回值

 vector<string> process(){

  if(expected.empty()){

     return {};

   }else if(expected==actual){

     return {"fun","ok"};

    else{

     return {"fun","ok","lk"};

    }

   }

 }

 

 9.函數的尾置返回類型

 對於函數的返回值類型較長的函數可使用尾置返回類型

 auto func(int i)  -> int(*)[10]   //返回一個指向10個元素的數組指針

10. =default 申明默認構造函數

有時在含有有參構造函數時,也須要默認構造函數,這是就須要使用=default申明

Sales_data()=default;

11.c11支持的因此順序容器

vector,deque,list,forward_list(單向鏈表),array,string

均可以使用列表初始化

12.lambda函數

形式:

[捕獲參數列表](形參列表){...;[return]}   //無需指定返回值類型

捕獲參數列表中有2個特殊的符號

&:表示全部外部變量的捕獲使用引用

=:表示全部外部變量的捕獲使用值

13.bind函數

用於綁定函數名及其參數,能夠交換參數位置。bind的返回值通常直接賦給auto


14.智能指針

shared-ptr<?>  實現引用計數,能夠轉移或賦值

unique-ptr<?>  不能轉移或賦值,取代auto-ptr<?>   但容許swap(由於該函數實現的是轉移)

scoped-ptr<?>  能夠轉移或賦值,可是同時只可能有一個對象持有指針

weaked-ptr<?>  解決share-ptr<?>的循環鏈中發生內存泄露的問題。


15.移動構造函數,右值引用,標準庫中的move函數,移動賦值

  Quote& operator=(Quote&&){...} //移動賦值

16.final 經過定義類的final來阻止繼承

class NoDerived final{...}


17.模板的類型別名

template<typename T> using twin=pair<T,T>;

twin<string>authors{"jim","ali"};//是一個pair



c11補充


1.異常

(1)throw能夠拋出異常對象和異常對象的指針,當拋出指針是必須保證catch處該指針變量還有效(通常必須在同一函數中處理)。

range_error r("error");

throw r; 拋出異常對象

exception *p=&r;

throw *p;拋出異常對象的指針

(2)棧展開

棧展開:拋出異常時將暫停當前函數的執行,開始查找匹配的catch子句。首先檢查throw自己是否在try塊內部,若是是,檢查與該try相關的catch子句,看是否其中之一能與被拋出的對象匹配,若是找到匹配的catch,就處理異常;找不到,就退出當前函數,而且繼續在調用函數中查找。

(3)棧展開要執行的操做

a.爲局部對象調用析構函數:當一個包含throw的函數因爲異常提前退出時,該函數的棧上的局部存儲會被釋放,將釋放在throw調用前建立的全部棧對象。若是局部對象是類類型的,就直接調用對象的析構函數。new分配的對象不能釋放。

b.析構函數應該從不拋出異常:棧展開期間會常常執行析構函數,若是析構函數再次拋出本身沒有處理的異常,將致使系統調用terminate函數,使整個程序崩潰。因此編程者不要在析構函數中拋出異常,而且必須處理析構函數中的默認異常。

c.構造函數經常拋出異常:若是在構造對象的時候發生異常,則對象可能部分被構建而一些成員能夠沒有被初始化。須要保證適當的撤銷以構造的元素。

d.異常被拋出到線程入口時尚未被捕獲,則程序將調用terminate終止程序。

(4)catch

a.catch中可使用throw;不加對象或rethrow從新拋出異常。被拋出的異常是原來的異常對象而不是形參。若是想改變拋出的異常對象的成員,形參可使用&引用

(5)容器與數組

void fun(){

 vector<string> v;

 while(cin>>s){

  v.push_back(s);

 }

 string *p=new string[20];

 //do something

 delete[] p;

}


若是在do something發生了任何異常,那麼Vector的內存可以正常釋放,但數組p的內存沒法釋放。由於無論什麼時候發生異常析構函數老是會被調用的。

(6)用類來分配資源

將分配資源的額動做必定要寫在類中,而不是單個的函數中,這樣在析構函數中寫釋放資源的操做,保證資源總能被釋放。

這種經過定義類來封裝資源的分配和釋放的技術稱爲RAII(資源分配即初始化)。原理就在於不管是否發生異常,類的析構函數老是會被調用的。

(7)auto_ptr類(保證異常安全-new出的對象自動釋放)

auto_ptr類是RAII技術的一個例子。auto_ptr類能夠接收任意一個非數組類型或模版的形參,爲動態分配的對象(new)提供異常安全。auto_ptr能夠保存任何類型指針。auto_ptr類的對象惟一持有指向的對象(不提供引用計數),這也決定了它不能放在容器中。

auto_ptr對象的複製和賦值是破壞性操做,將改變右操做數。

auto_ptr<string>ap1(new string("stg"));

auto_ptr<string>ap2(ap1);//ap1將解除綁定

ap1=ap2; //將對ap1之前指向的對象執行析構(雖然ap1沒有指向對象)

此時將解除右操做數對指針指向的對象的綁定,同時對左操做數原來指向的對象執行析構。(左右操做數都將有影響)

auto_ptr賦值時不能直接將一個地址賦給auto_ptr對象

p_auto=new int(1024);//error

而必須調用auto_ptr的reset函數來從新賦值

if(p_auto.get())//判斷該指針是否爲0

   *p_auto=1024;

else

  p_auto.reset(new int(1024)); //能夠用0來重置p_auto.reset(0)。調用reset方法可使多個auto_ptr指針指向同一對象。

自動指針賦值(ap1=ap2;)的過程爲:對左邊執行析構;左邊獲得右邊的值;對右邊執行解除綁定

auto_ptr指針釋放對象時使用的是delete而不是deleted[],因此他不能管理數組。

因爲auto_ptr不知足複製和複製操做,因此他不能放置在容器中。auto_ptr做爲形參時,必須使用引用。

(至關於auto_ptr在內存中是靜止的,不能移動)

(8)函數的異常說明

void fun(void) throw (runtime_error,logic_error)

{;}//代表該函數只可能拋出一個runtime_error的類及其子類

void fun(void) throw ()

{;}//代表該函數不會拋出異常

若是函數拋出了其餘沒有說明的異常,那麼系統將會調用unexpected函數,將會異常終止程序。

類繼承時,在虛函數中,子類拋出的異常說明必須比基類可能拋出的異常少,且必須是基類異常的一個子集。

(9)函數指針的異常說明

void (*pf)(int) throw(runtime_error);

代表pf只能指向一個只可能拋出runtime_errorauto_ptr類的函數。



2.運行時類型識別(RTTI)

c++經過一下2個操做符提供RTTI功能

(1)typeid操做符,返回指針或或引用所指向對象的實際類型。(基類和子類是相等的)

(2)dynamic_cast操做符,將基類類型的指針或引用安全的轉換爲派生類型的指針或引用。


3.c++11的移動語義與右值引用

C++ 11中引入的一個很是重要的概念就是右值引用。理解右值引用是學習「移動語義」(move semantics)的基礎。而要理解右值引用,就必須先區分左值與右值。

對左值和右值的一個最多見的誤解是:等號左邊的就是左值,等號右邊的就是右值。【左值和右值都是針對表達式而言的】,左值是指表達式結束後依然存在的持久對象,右值是指表達式結束時就再也不存在的臨時對象。【一個區分左值與右值的便捷方法是:看能不能對錶達式取地址,若是能,則爲左值,不然爲右值。】下面給出一些例子來進行說明。

移動語義體如今2點:

1. g++ -std=c++11 main.cpp   c++11標準使用了賦值的移動語義 object a=foo();即將a與foo()返回的零時對象內存指針交換,而後釋放零時變量指向的內存。

2. 定義的&&對零時變量進行引用(右值引用),常做爲函數入參。用於獲取臨時變量中能夠複用的內存。

例如:

 container& operator = (container&& other)   //移動賦值

    {

        delete value; //假如value是一個使用new分配的內存指針,此時就能夠複用這個內存。

        value = other.value;

        other.value = NULL;

        return *this;


    }

 轉移語義的定義

右值引用是用來支持轉移語義的。轉移語義能夠將資源 ( 堆,系統對象等 ) 從一個對象轉移到另外一個對象,這樣可以減小沒必要要的臨時對象的建立、拷貝以及銷燬,可以大幅度提升 C++ 應用程序的性能。臨時對象的維護 ( 建立和銷燬 ) 對性能有嚴重影響。


轉移語義是和拷貝語義相對的,能夠類比文件的剪切與拷貝,當咱們將文件從一個目錄拷貝到另外一個目錄時,速度比剪切慢不少。


經過轉移語義,臨時對象中的資源可以轉移其它的對象裏。


在現有的 C++ 機制中,咱們能夠定義拷貝構造函數和賦值函數。要實現轉移語義,【須要定義轉移構造函數,還能夠定義轉移賦值操做符】。對於右值的拷貝和賦值會調用轉移構造函數和轉移賦值操做符。若是轉移構造函數和轉移拷貝操做符沒有定義,那麼就遵循現有的機制,拷貝構造函數和賦值操做符會被調用。


普通的函數和操做符也能夠利用右值引用操做符實現轉移語義。  

  char* _data; 

  size_t   _len; 

    void _init_data(const char *s) { 

    _data = new char[_len+1]; 

    memcpy(_data, s, _len); 

    _data[_len] = '\0'; 

  } 

    MyString(const MyString& str) { 

    _len = str._len; 

    _init_data(str._data); 

    std::cout << "Copy Constructor is called! source: " << str._data << std::endl; 

  } 

   

  MyString(MyString&& str) { 

    std::cout << "Move Constructor is called! source: " << str._data << std::endl; 

    _len = str._len; 

    _data = str._data; 

    str._len = 0; 

    str._data = NULL; 

 }


和拷貝構造函數相似,有幾點須要注意:

a. 參數(右值)的符號必須是右值引用符號,即「&&」。

b. 參數(右值)不能夠是常量,由於咱們須要修改右值。

c. 參數(右值)的資源連接和標記必須修改。不然,右值的析構函數就會釋放資源。轉移到新對象的資源也就無效了。   

    

轉移語義的目的:實現零時對象中申請的資源的複用。    

    

    

4.c++的成員指針調用成員函數(非virtual函數,實現基類調用子類的函數)


方法1:非靜態map存儲子類的成員函數,並遍歷。原理相似於虛函數(可見:只要基類有子類成員函數的指針就能夠調用到子類的成員函數)

#include "stdafx.h"

#include <iostream>

#include <string>

#include <map>

using namespace std;


class Inr{

public:

virtual void display(void){;}

};


class Myview :public Inr{

public:

typedef void (Myview::*memfun)(void);

 map<string,memfun> sav;  //若是爲靜態將出現沒法鏈接的編譯錯誤;若是這裏map定義爲靜態的,則必須在類外申明一下,以表示初始化,方式爲map<string,memfun>Myview::sav;便可(複雜類必須在類外申明初始化,分配內存)


public:

 void Myview::display(void){

map<string,memfun>::iterator itr=sav.begin();   

for(;itr!=sav.end();itr++){

(this->*(itr->second))();

}

}

};


class Childview :public Myview{

public:

Childview(){

sav["disview"]=(memfun)disview;

}

public:

void disview(void){

cout<<"this is child view"<<endl;

}

};


int main(int argc, char* argv[])

{

Childview Am;

Am.display();

return 0;

}


方法2:靜態數組存儲子類的成員函數,並遍歷。

 #include <OBJBASE.H>

using namespace std;


interface Base1{

     public:

     virtual void ExeFunc(void)=0;

     };


typedef void (Base1::*Pfunc)(int);

typedef struct typefunclink{

     Pfunc * PFmen;

  typefunclink *MemLink;

}TypeFuncLink;


class Base2: public Base1{

public:

     Base2(){;};

     ~Base2(){;};

     static Pfunc Memfunc[];

     static TypeFuncLink LkupList;

     void B2Printf(int);

      virtual void ExeFunc(void);

     };


     Pfunc Base2::Memfunc[2]={(Pfunc)(&B2Printf),0};


     TypeFuncLink Base2::LkupList={Base2::Memfunc,0};

     

     void Base2::B2Printf(int){

           cout<<"This Base2 Class Print"<<endl;

     }

     void Base2::ExeFunc(void)

     {      

                 TypeFuncLink LkLink=Base2::LkupList;

                 while(LkLink.PFmen||LkLink.MemLink)

                 {int i=0;

                       while(LkLink.PFmen[i])

                       {

                             Pfunc am=LkLink.PFmen[i];

                             (this->*am)(0);

                             i++;

                       }

                       if(LkLink.MemLink)

                             LkLink=*(LkLink.MemLink);

                       else

                             break;

                 }

     }

class Base3: public Base2{


public:

     Base3(){;};

     ~Base3(){;};

     static Pfunc Memfunc[];

     static TypeFuncLink LkupList;

     void B3Printf(int);

      virtual void ExeFunc(void);

     };


     Pfunc Base3::Memfunc[2]={(Pfunc)(&B3Printf),0};


     TypeFuncLink Base3::LkupList={Base3::Memfunc,&Base2::LkupList};

     

     void Base3::B3Printf(int){

           cout<<"This Base3 Class Print"<<endl;

     }

     void Base3::ExeFunc(void)

     {

                 

                 TypeFuncLink LkLink=Base3::LkupList;

                 while(LkLink.PFmen||LkLink.MemLink)

                 {int i=0;

                       while(LkLink.PFmen[i])

                       {

                             Pfunc am=LkLink.PFmen[i];

                             (this->*am)(0);

                             i++;

                       }

                       if(LkLink.MemLink)

                             LkLink=*(LkLink.MemLink);

                       else

                             break;

                 }

                 

                 

     }            

     void main(void)

     {

           Base3 cl;

           cl.ExeFunc();

           system("pause");

     }   

    

   

5.線程中join和detach的做用

不管在windows中,仍是Posix中,主線程和子線程的默認關係是:不管子線程執行完畢與否,一旦主線程執行完畢退出,全部子線程執行都會終止。這時整個進程結束或僵死(部分線程保持一種

終止執行但還未銷燬的狀態,而進程必須在其全部線程銷燬後銷燬,這時進程處於僵死狀態),須要強調的是,線程函數執行完畢退出,或以其餘很是方式終止,線程進入終止態,但千萬要記住的是,進入終止態後,爲線程分配的系統資源並不必定已經釋放,並且可能在系統重啓以前,一直都不能釋放。終止態的線程,仍舊做爲一個線程實體存在於操做系統中。(這點在win和unix中是一致的。)而何時銷燬線程,取決於線程屬性。一般,這種終止方式並不是咱們所指望的結果,並且一個潛在的問題是未執行完就終止的子線程,除了做爲線程實體佔用系統資源以外,其線程函數所擁有的資源(申請的動態內存,打開的文件,打開的網絡端口等)也不必定能釋放。因此,針對這個問題,主線程和子線程之間一般定義兩種關係:

      (1)可會合(joinable)。這種關係下,主線程須要明確執行join等待操做。在子線程結束後,主線程的等待操做執行完畢,子線程和主線程會合。這時主線程繼續執行等待操做以後的下一步操做。主線程必須會合可會合的子線程,Thread類中,這個操做經過在主線程的線程函數內部調用子線程對象的wait()函數實現。這也就是上面加上三個wait()調用後顯示正確的緣由。必須強調的是,即便子線程可以在主線程以前執行完畢,進入終止態,也必需顯示執行會合操做,不然,系統永遠不會主動銷燬線程,分配給該線程的系統資源(線程id或句柄,線程管理相關的系統資源)也永遠不會釋放。

      (2)相分離(detached)。顧名思義,這表示子線程無需和主線程會合,也就是相分離的。這種狀況下,子線程一旦進入終止態,系統當即銷燬線程,回收資源。無需在主線程內調用wait()實現會合。Thread類中,調用detach()使線程進入detached狀態。這種方式經常使用在線程數較多的狀況,有時讓主線程逐個等待子線程結束,或者讓主線程安排每一個子線程結束的等待順序,是很困難或者不可能的。因此在併發子線程較多的狀況下,這種方式也會常用。缺省狀況下,建立的線程都是可會合的。可會合的線程能夠經過調用detach()方法變成相分離的線程。但反向則不行。  

6.運算符重載

運算符重載的實質是函數重載,它提供了C++的可擴展性,也是C++最吸引人的特性之一。運算符重載是經過建立運算符函數實現的,運算符函數定義了重載的運算符將要進行的操做。運算符函數的定義與其餘函數的定義相似,唯一的區別是運算符函數的函數名是由關鍵字operator和其後要重載的運算符符號構成的。運算符函數定義的通常格式以下:

<返回類型說明符> operator <運算符符號>(<參數表>)

{

  <函數體>

規則:

(1)不可重載的5個運算符:類屬關係運算符"."、成員指針運算符".*"、做用域運算符"::"、三目運算符"?:"  和sizeof運算符

(2)重載運算符限制在C++語言中已有的運算符範圍內的容許重載的運算符之中,不能建立新的運算符

(3)重載以後的運算符不能改變運算符的優先級和結合性,也不能改變運算符操做數的個數及語法結構。

(4)...

在進行對象之間的運算時,程序會調用與運算符相對應的函數進行處理,因此運算符重載有兩種方式:成員函數重載方式和友元函數重載方式。成員函數的形式比較簡單,就是在類裏面定義了一個與操做符相關的函數。友元函數由於沒有this指針,因此形參會多一個。

class A

{

public:

 A(int d):data(d){}

 A operator+(A&);//成員函數

 A operator-(A&);

 A operator*(A&);

 A operator/(A&);

 A operator%(A&);

 friend A operator+(A&,A&);//友元函數

 friend A operator-(A&,A&);

 friend A operator*(A&,A&);

 friend A operator/(A&,A&);

 friend A operator%(A&,A&);

 

bool operator == (const A& ); 

bool operator != (const A& );

bool operator <  (const A& );

bool operator <= (const A& );

bool operator >  (const A& );

bool operator >= (const A& );


bool operator || (const A& );

bool operator && (const A& );

bool operator !  ();


A& operator + ();  //這裏的+、-是正負的意思,放在對象前面。 

A& operator - ();

A* operator & ();

A& operator * ();  //取對象運算


A& operator ++ ();//前置++

A operator ++ (int);//後置++    ,這個int是沒有意義的,僅僅與前置作區分

A& operator --();//前置--

A operator -- (int);//後置-- ,返回不能是「引用」,由於返回的是一個右值對象




A& operator ++ ();//前置++  //++和–根據位置的不一樣有四種狀況,均可以重載。

A operator ++ (int);//後置++

A& operator --();//前置--

A operator -- (int);//後置--


A operator | (const A& );

A operator & (const A& );

A operator ^ (const A& );

A operator << (int i);

A operator >> (int i);

A operator ~ ();


A& operator += (const A& );

A& operator -= (const A& ); 

A& operator *= (const A& );

A& operator /= (const A& );

A& operator %= (const A& );

A& operator &= (const A& );

A& operator |= (const A& );

A& operator ^= (const A& );

A& operator <<= (int i);

A& operator >>= (int i);


void *operator new(size_t size);

void *operator new(size_t size, int i);

void *operator new[](size_t size);

void operator delete(void*p);

void operator delete(void*p, int i, int j);

void operator delete [](void* p);


A& operator = (const A& );

char operator [] (int i);//返回值不能做爲左值

const char* operator () ();

T operator -> ();

//類型轉換符

operator char* () const;

operator int ();

operator const char () const;

operator short int () const;

operator long long () const;


friend inline ostream &operator << (ostream&, A&);//輸出流   這些只能以友元函數的形式重載 

friend inline istream &operator >> (istream&, A&);//輸入流

private:

 int data;

};



A& A::operator++( ) //前置

{

data++;

return *this;

}

A A::operator++( int ) //後置

{  A temp(*this);

data++;

return temp;

}


//成員函數的形式

A A::operator+(A &a)

{

 return A(data+a.data);

}

A A::operator-(A &a)

{

 return A(data-a.data);

}

A A::operator*(A &a)

{

 return A(data*a.data);

}

A A::operator/(A &a)

{

 return A(data/a.data);

}

A A::operator%(A &a)

{

 return A(data%a.data);

}

//友元函數的形式

A operator+(A &a1,A &a2)

{

 return A(a1.data+a2.data);

}

A operator-(A &a1,A &a2)

{

 return A(a1.data-a2.data);

}

A operator*(A &a1,A &a2)

{

 return A(a1.data*a2.data);

}

A operator/(A &a1,A &a2)

{

 return A(a1.data/a2.data);

}

A operator%(A &a1,A &a2)

{

 return A(a1.data%a2.data);

}


運算符函數的特色:

(1)通常都會有返回值

(2)入參通常爲儘可能使用「引用」

(3)儘可能返回*this對象,而不要構造一個對象返回

(4)返回值是「引用」的,通常都返回*this對象

(5)第一個操做數是stream的,只能以友元的形式重載


6.c11多線程與異步任務

(1)線程建立

c11的線程建立比任何一種語言都要簡單.

void foo(int x,int y)

  {

    // x = 4, y = 5.

  }

void main()

  {

    std::thread t(foo,4,5);  //foo能夠是匿名函數 

    t.join(); //t.detach(); 

  }

(2)異常簡單的mutex和recursive_mutex

std::mutex m;  //std::recursive_mutex

int j = 0;

void foo()

  {

  m.lock();        // 進入臨界區域

  j++;

  m.unlock();      // 離開

  }

void main()

  {

  std::thread t1(foo);

  std::thread t2(foo);

  t1.join();

  t2.join();

 // j = 2;

}

(3)異常簡單的線程本地存儲(線程退出時,不改變該變量的值)

thread_local int j = 0;

void foo()

  {

  m.lock();

  j++; // j is now 1, no matter the thread. j is local to this thread.

  m.unlock();

  }

void main()

  {

  j = 0;

  std::thread t1(foo);

  std::thread t2(foo);

  t1.join();

  t2.join();

 // j still 0. The other "j"s were local to the threads

}

(4)異常簡單的條件變量

std::condition_variable c;

// 咱們使用mutex而不是recursive_mutex是由於該鎖須要一次性獲取和釋放

std::mutex mu; 

void foo5()

{

   std::unique_lock lock(mu); 

   c.notify_one(); 

}

void main()

{

   std::unique_lock lock(mu); 

   std::thread t1(foo5);

   

   c.wait(lock); 

   t1.join();

}

5.異常簡單的future實現異步任務

int GetMyAnswer()

   {

    return 10;

   }

int main()

  {

  std::future<int> GetAnAnswer = std::async(GetMyAnswer);  // GetMyAnswer starts background execution

  int answer = GetAnAnswer.get(); // answer = 10;

  // If GetMyAnswer has finished, this call returns immediately.

  // If not, it waits for the thread to finish.

  }


你也可使用 std::promise。該對象能夠提供一些 std::future之後需求的特性。若是在任何東西放入承諾(promise)以前你調用 std::future::get(),將會致使等待,直到承諾值(promised value)出現。若是 std::promise::set_exception()被調用, std::future::get()則會拋出異常。若是 std::promise銷燬了,而你調用 std::future::get(),你將會產生 broken_promise 異常。 

例子:

std::promise<int> sex;

void foo()

  {

  // 在下面的調用以後,future::get()將會返回該值

  sex.set_value(1); // After this call, future::get() will return this value.

     

  // 調用以後,future::get()將會拋出這個異常

  sex.set_exception(std::make_exception_ptr(std::runtime_error("TEST")));

  }

int main()

  {

  future<int> makesex = sex.get_future();

  std::thread t(foo);

   

  // do stuff

  try

    {

    makesex.get();

    hurray();

    }

  catch(...)

    {

    // She dumped us :(

    }

  }

6.bind綁定成員函數和非成員函數

#include < functional> 


(1)綁定非成員函數

int Func(int x, int y);  

auto bf1 = std::bind(Func, 10, std::placeholders::_1);  

bf1(20); ///< same as Func(10, 20)  


(2)綁定成員函數(bind成員函數的第一個入參必須是對象自己)

class A  

{  

public:  

    int Func(int x, int y);  

};  

   

A a;  

auto bf2 = std::bind(&A::Func, a, std::placeholders::_1, std::placeholders::_2);  

bf2(10, 20); ///< same as a.Func(10, 20)  

   

std::function< int(int)> bf3 = std::bind(&A::Func, a, std::placeholders::_1, 100);  

bf3(10); ///< same as a.Func(10, 100)  





C程序存儲分紅4段:

a.代碼段:存儲每個函數包括main函數的二進制代碼

b.數據段:分3種

b.a只讀數據段:存儲只讀全局變量和只讀局部變量,以及全部常量

b.b.已初始化讀寫數據段:存儲已經初始化的全局變量和局部靜態變量

b.c.未初始化讀寫數據段:運行時纔會產生該段,存儲未通過初始化的數據,該段不是目標文件的一部分

c.棧:存儲程序運行時的函數內部使用的變量、函數的參數以及返回值

d.堆:存儲程序運行時malloc分配的空間

相關文章
相關標籤/搜索