標簽(空格分隔): 學習筆記
(本學習筆記整理于2016年暑假期間,在編寫了超過3000行的代碼之后重學c++ PRimer,獲得的一些新鮮想法和技巧)
1)./指出該文件在當前目錄中; 2)為了處理輸入,使用cin; (cin>>
11)頭文件中不應包含using聲明; 12)cin逐個輸入,getline讀取一整行輸入,直到遇到換行符;
string和vector是兩種最重要的標準庫類型。string對象是一個可變長的字符序列,vector對象時一組同類型對象的容器。迭代器允許對容器中的對象進行間接訪問,對于string對象和vector對象來說,可以通過迭代器訪問元素或者元素間的移動。數組和指向數組元素的指針在一個較低層次上實現了與標準庫類型string和vector類似的功能。一般來說,應該優先選用標準可提供的實現類型,之后在考慮c++語言內置的底層的替代品數組或指針。 1)string類中,初始化下標為string::size_type,以確保下標的范圍不會出現負數。 2)
vector<int>v2;以上語句用來聲明一個空的vector
for(int i =0;i<100;i++)v2.push_back(i);以上語句用來向空的vector后方循環插入新值,這也是vector最快的賦值方式。 vector可以用下標引用來直接索引對應位置的值,v2[n]; 3)迭代器的初始化: vector::iterator it; string::iterator it2; 4)訪問迭代器所指向的元素需要解引用(*it).empty()或者it->empty. 5)但凡使用了迭代器的循環體,都不要向迭代器所屬的容器添加元素。 6)強制類型轉換:
double slope = static_cast<double>(j)/i;函數是命了名的計算單元,它對程序個結構化至關重要。每個函數都包含了返回類型、名字、形參列表以及函數實體。函數體是一個塊,當函數被調用時候才執行塊內的內容。此時,傳遞給函數的實參類型必須與對應的形參類型相容。在c++語言中,函數可以被重載:同一個名字可用于定義多個函數,只要這些函數的形參數量或者形參類型不同就行。從一組重載函數匯總選取最佳函數的過程稱為函數匹配。 1)如果函數無須改變引用形參的值,最好將其聲明為常量引用。
bool isShorter(const string &s1, const string &s2);2)內聯函數機制用于優化規模較小,流程直接,頻繁調用的函數。 3)任何對類成員的直接訪問都被看作this的隱式調用。
類是c++語言中最基本的特性。類允許我們為自己的應用定義新類型,從而使得程序更加簡潔且易于修改。類有兩項基本能力:一是數據抽象,二是封裝。類可以定義一種特殊的成員函數:構造函數,其作用是控制初始化對象的方式。構造函數可以重載,構造函數應該使用構造函數初始值列表來初始化所有的數據成員。 1)構造函數,類內初始值:
Sales_data(const std:: string &s): bookNo(s),Uints_sold(0),reveneu(0){};2)應該令構造函數初始值的順序與成員聲明的順序保持一致; 3)靜態成員不屬于類的某個對象,但是我們仍然可以使用類的對象、引用或者指針來訪問靜態成員。 4)類似于全局變量,static數據成員一旦被定義,講講也一直存在于程序的整個生命周期中。
c++使用標準庫類來處理面向流的輸入和輸出:iosream處理控制臺IO,fstream用來控制命名文件的IO,stringstream完成內存string的IO。每個IO對象都維護一組條件狀態,用戶來指出此對象上是否可以進行IO操作。 標準庫容器室模板類型,用來保存給定類型的對象。所有容器都提供高效的動態內存管理。我們可以向容器中添加元素而不必擔心元素存儲在哪里。容器負責管理自身的存儲。每個容器都定義了構造函數、添加和刪除操作,確定容器大小的操作以及返回指向特定元素的迭代器的操作。當我們使用添加或者刪除元素的而操作時,必須注意到這些操作可能使指向容器中元素的迭代器、指針或引用失效。 標準庫定義了大約100個類型無關的對序列進行操作的算法。算法通過在迭代器上進行操作來實現類型無關。算法從不直接改變他們所操作的序列的大小。他們會將元素一個位置拷貝到另一個位置,但不會直接添加或刪除元素 關聯容器支持通過關鍵字高效查找和提取元素。對關鍵字的使用將關聯容器和順序容器區分開來,順序容器中是通過位置訪問元素的。標準庫定義了8跟關聯容器(有序無序,關鍵字是否重復)。有序容器使用比較函數來比較關鍵字,從而將元素按順序存儲。默認情況下,比較操作時采用關鍵字類型的<運算符。而無序容器使用關鍵字類型的==運算符和一個hash<key__type>類型的對象來組織元素。無論在有序容器還是在無序容器中,具有相同關鍵字的元素都是相鄰存儲的。 在c++中,內存是通過new表達式來進行分配,通過delete表達式進行釋放。新的標準庫定義了智能指針類型——shared_ptr和weak_ptr,可令動態內存管理更為安全。對于一塊內存,當沒有任何用戶使用的時候,會自動釋放?,F代c++程序應該盡可能的使用智能指針。 1)代碼通常應該在使用一個流之前檢查它是否處于良好狀態。
if(out); //out是一個關聯命名文件的文件指針while(cin>>Word){}2)當一個fstream對象被銷毀是,close會自動被調用。即使這樣,在讀寫文件操作時,我們也應該顯式的調用out.close(); 3)c++程序應該使用標準可容器,而不是更原始的數據結構,通常vector是最好的選擇,下面列出了容器的選擇方案: a:如果程序要求隨機訪問元素,應該使用vector或者deque; b:如果程序要求在容器的中間插入或刪除元素,應使用list或forward_list; c:在頭尾位置插入或者刪除元素,但不會在中間位置進行插入或者刪除從操作,則應該使用deque; d:如果程序在讀取輸入時需要在容器中間插入元素,隨后需要隨機訪問容器中的元素。則可以再輸入階段使用list,在操作階段將list拷貝到vector中。 4)insert函數將元素插入到迭代器所指定的位置之前。 5)
7)注意Set和multiset的區別,set是具有唯一關鍵字的容器,multiset允許重復關鍵字。 8)lower_bound和upper_bound不適用于無序容器,下標和at操作只適用于非const的map和unordered_map。 9)如果一個multimap或者multiset中有多個元素具有給定關鍵字,則這些元素在容器中會相鄰存儲。 10)如果author中有search_item,則返回的lower_bound和upper_bound會不等。
for(auto beg = authors.lower_bound(search_item),end = authors.upper_bound(search_item);beg!=end;++beg;){cout<<beg->second<<endl;}11)智能指針的陷阱: a:不適用相同的內置指針初始化多個智能指針; b:不delete get()返回的指針; c:不使用get()初始化或reset另一個智能指針; d:如果使用了get()返回的指針,記住當最后一個對應的智能指針銷毀后,你使用的指針會變為無效。 e:如果智能指針管理的資源不是new分配的內存,需要傳遞一個刪除器;
shared_ptr<connection>p(&c,end_connection);void end_connection(connection *p){disconnect(*p)};12)new申請的內存進行初始化零值可以使用以下操作
int *array = new int [length]();每個類都會控制該類型的對象拷貝、移動、賦值以及銷毀。特殊的成員函數——拷貝構造函數、移動構造函數、拷貝賦值運算符、移動賦值運算符和析構函數定義了這些操作。如果一些類沒有定義這些操作,編譯器會自動為其進行合成。如果這些操作位定義成刪除的,他們會逐成員初始化,移動、賦值或銷毀對象;合成的操作依次處理每個非static數據成員,根據成員類型確定如何移動、拷貝、賦值或銷毀他。 1)如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則此構造函數式拷貝構造函數。(拷貝構造函數的第一個參數必須是自身類類型的引用類型)
{sales_Data * p = new sales_Data; //p為動態申請的類指針auto p2 = make_shared<sales_Data>(); //p2為智能類指針sales_Data item(*p); //拷貝構造函數將*p拷貝到item中vector<sales_Data>vec; //局部對象vec.push_back(*p2); //拷貝P2指向的對象;delete p;}//退出局部作用域,對item,p2,vec調用析構函數;2)當我們決定一個類是否需要定義它自己的版本的控制成員時,一個基本原則是首先確定這個類是否需要一個析構函數。(需要拷貝操作的類也需要賦值操作) 3)將拷貝控制成員定義為=default來顯式地要求編譯器生成合成的版本;
class sales_Data{public: sales_Data() = default; sales_Data(const sales_Data&) = default; //賦值 sales_Data & Operator = (const sales_Data&)//拷貝 ~sales_Data() = default;};sales_Data & sales_Data::operator = (const sales_Data &) = default;4)拷貝時交換指針優于真實值拷貝
class hasptr{friend void swap(hasPtr&, hasPtr&);};void swap(hasPtr& lhs, hasPtr& rhs){using std::swap;swap(lhs.ps,rhs.ps);swap(lhs.i,rhs.i);}5)message類實例:
class Message{friend class Folder;public: explicit Message (const std:: string &str = ""): contents (str){}; //拷貝控制成員,用來管理指向本message的指針; Message(const Message&); //拷貝構造函數 Message& operator = (const Message&); //拷貝賦值運算符 ~Message(); void save(Folder &); void remove(Folder &);private: std::string contents; std::set<Folder *>folders; void add_to_Folders(const Message&); void remove_from_Folders();};一個重載的運算符必須是某個類的成員或者至少擁有一個類類型的運算對象。重載運算符的運算對象數量、結合律、優先級與對應的用于內置類型的運算符完全一致。當運算符被定義為類的成員時,類對象的隱式this指針綁定到第一個運算對象。賦值、下標、函數調用和箭頭運算符必須作為類的成員使用。 6)重載運算符:重載的運算符是具有特殊名字的函數:他們的名字由關鍵字operatpr和其后要定義的運算符號共同組成。 data1 + data2; 等價于 operator+(data1,data2); data1 += data2; 等價于 data1.operator +=(data2); 7)某些運算符不應該被重載:邏輯與,邏輯或,逗號運算符(&&,||,“,”,&); 8)賦值(=),下標([]),調用(())和成員訪問箭頭(->)運算符必須是成員函數;遞增,遞減和解引用運算符通常應該是成員函數;算術(+,-,*,/,%)相等性、關系和位運算符,通常應該是非成員函數。 9)重載輸出<<的例子:
ostream &operator<<(ostream &os, const sales_Data &item){os<< item.isbn()<<""<<item.uints_sold<<"" <<item.revenue<<""<<item.arg_price();return os;}//盡量減少格式化操作,盡量減少換行符、制表符重載輸入>>運算符
istream &operator>>(istream &is, sales_Data & item){ double price; is>>item.bookNo>>item.uint_sold>>price; if(is) item.revenue = item.uint_sold * price; else item = sale_Data(); return is;}輸入輸出運算符必須是非成員函數,一般被聲明為友元函數; 10)重載下標運算符
class strVec{public: std::string & operator[](std::size_t n) {return elements[n];} const std::string & operator[](std::size_t n)const {return elements[n];}private: std::string * elements;};//下標運算符必須是成員函數11)從一個類轉換到另一個類
operator type()const;oop面向對象的編程的核心思想是:數據抽象,繼承和動態綁定。繼承使得我們可以編寫一些新的淚,這些新類既能共享其基類的行為,又能根據需要覆蓋或添加行為。動態綁定使得我們可以忽略類型之間的差異。在c++語言中,動態綁定值作用于懸殊,并且需要通過指針或引用調用。在派生類對象中包含有與它的每個基類對應的子對象,因為所有派生類對象都含有基類部分,所以我們能將派生類的引用或指針轉換為一個可訪問的基類引用或指針。當執行派生類的構造、拷貝、移動和賦值操作的時候,首先構造、拷貝、移動和賦值其中的基類部分,然后才輪到派生類部分。而析構函數的執行順序則正好相反?;愅ǔ6紤摱x一個虛析構函數。 12)面向對象的編程的核心思想:數據抽象、繼承、動態綁定。數據抽象使得接口與實現的分離;繼承可以定義相似類及其建模;動態綁定在一定程度上忽略了相似類型的區別。 13)層次關系的根部有一個基類,其他類則直接或間接地從基類繼承而來,這些繼承得到的類稱為派生類?;愗撠煻x在層次關系中所有類共同擁有的成員,而每個派生類定義各自特有的成員。 14)protected成員:供派生類訪問,禁止其他用戶訪問。 15)派生類也必須使用基類的構造函數來初始化它的基類部分
Bulk_Quote(const satd::string& book, double p,std::size_t qty, double disc): Quote(book,p),min_qty(qty),discount(disc){};16)派生類的聲明:與其他類相同,不包含公共派生列表。如果想將某類用作基類,則該類必須已經經過聲明和定義。 17)基類應該將其接口成員聲明為公有的;同時將屬于其實現的部分分成兩組。一組可供派生類訪問,另一組只能由基類及基類的友元函數訪問。 18)除了覆蓋繼承而來的虛函數之外,派生類最好不要重用其他定義在基類中的名字。 19)派生類繼承基類的構造函數:提供一條注明了基類名的using聲明語句。 20)函數模板
template<typename T>int compare(const T& v1, const T& v2){if(v1 < v2) return -1;if(v2 < v1) return 1;return 0;}21)函數模板的inline聲明:
template <typename T> inline T min(const T &, const T &);二、類的設計者編程實例 基類以及派生類的聲明
//dma.h --類的繼承關系以及動態內存在基類和派生類中的使用#ifndef DMA_H_#define DMA_H_#include <iostream>//Bass class using dmaclass baseDMA{private: char* label; int rating;public: baseDMA(const char* l = "null", int r = 0); //構造函數 baseDMA(const baseDMA &rs); //賦值函數 virtual ~baseDMA(); //基類的虛析構函數 baseDMA &operator = (const baseDMA & rs); //移動拷貝函數 friend std::ostream &operator <<(std::ostream & os, const baseDMA &rs); //重載的輸出操作符};//derived class without DMA//no destructor needed//uses implicit copy constructor//uses implicit saaignment operatorclass lacksDMA : public baseDMA{private: enum {COL_LEN =40}; char color[COL_LEN];public: lacksDMA(const char* c = "blank", const char * l = "null", int r = 0);//派生類的構造函數,想給派生類的變量進行初始化,在對基類的變量進行初始化,順序保持一致 lacksDMA(const char* c, const baseDMA& rs);//派生類的賦值函數 friend std::ostream &operator<<(std::ostream &os,const lacksDMA &rs);};//derived class with DMAclass hasDMA :public baseDMA{private: char* style;public: hasDMA(const char* s = "none", const char* l = "null", int r = 0); hasDMA(const char * s, const baseDMA& rs); hasDMA(const hasDMA &hs); ~hasDMA(); hasDMA &operator = (const hasDMA & rs); //拷貝賦值函數 friend std::ostream & operator<<(std::ostream &os, const hasDMA& rs);};#endif基類以及派生類的方法定義:
//dma.cpp --dma class methods#include "dma.h"#include <string>using namespace std;//baseDMA methodsbaseDMA::baseDMA(const char * l, int r){ label = new char[strlen(l) + 1]; strcpy(label, l); rating = r;}baseDMA::baseDMA(const baseDMA &rs){ label = new char[strlen(rs.label) + 1]; strcpy(label, rs.label); rating = rs.rating;}baseDMA::~baseDMA(){ delete[] label;}baseDMA & baseDMA::operator=(const baseDMA & rs){ if (this == &rs) { return *this; } delete[] label; label = new char[strlen(rs.label) + 1]; strcpy(label, rs.label); rating = rs.rating; return *this;}ostream &operator <<(ostream &os, const baseDMA & rs){ os << "Label:" << rs.label << endl; os << "Rating:" << rs.rating << endl; return os;}//lacksDMA methodslacksDMA::lacksDMA(const char * c, const char* l, int r) :baseDMA(l, r){ strncpy(color, c, 39); color[39] = '/0';}lacksDMA::lacksDMA(const char* c, const baseDMA &rs) :baseDMA(rs){ strncpy(color, c, COL_LEN - 1); color[COL_LEN - 1] = '/0';}ostream &operator <<(ostream & os, const lacksDMA& ls){ os << ls; os << "Color:" << ls.color << endl; return os;}//hasDMA methodshasDMA::hasDMA(const char* s, const char* l, int r) :baseDMA(l,r){ style = new char[strlen(s) + 1]; strcpy(style, s);}hasDMA::hasDMA(const char* s, const baseDMA & rs) :baseDMA(rs){ style = new char[ strlen(s) + 1]; strcpy(style, s);}hasDMA::hasDMA(const hasDMA & hs) :baseDMA(hs){ style = new char[strlen(hs.style) + 1]; strcpy(style, hs.style);}hasDMA::~hasDMA(){ delete[] style;}//移動拷貝構造函數//即相當于重載=運算符來完成拷貝構造函數的功能,等價于hasDMA(const hasDMA & hs)hasDMA &hasDMA:: operator=(const hasDMA &hs) { if (this == &hs) { return *this; } baseDMA::operator = (hs); //首先調用基類的拷貝函數 style = new char[strlen(hs.style) + 1]; strcpy(style, hs.style); return *this;}ostream & operator<<(ostream & os, const hasDMA &hs){ os << hs; os << "Style:" << hs.style << endl; return os;}新聞熱點
疑難解答
圖片精選