1. 程式人生 > >用C++寫MTP程式遇到的知識

用C++寫MTP程式遇到的知識

1、C++ string類的方法

string 函式列表
函式名   描述

begin     得到指向字串開頭的Iterator

end 得到指向字串結尾的Iterator
rbegin 
得到指向反向字串開頭的Iterator
rend 
得到指向反向字串結尾的Iterator
size 
得到字串的大小
length 
和size函式功能相同
max_size 
字串可能的最大大小
capacity 
在不重新分配記憶體的情況下,字串可能的大小
empty 
判斷是否為空
operator[] 
取第幾個元素,相當於陣列
c_str 
取得C風格的const char* 字串
data 
取得字串內容地址
operator= 
賦值操作符
reserve 
預留空間
swap 
交換函式
insert 
插入字元
append 
追加字元
push_back 
追加字元
operator+= 
+= 操作符
erase 
刪除字串
clear 
清空字元容器中所有內容
resize 
重新分配空間
assign 
和賦值操作符一樣
replace 
替代
copy 
字串到空間
find 
查詢
rfind 
反向查詢
find_first_of 
查詢包含子串中的任何字元,返回第一個位置
find_first_not_of 
查詢不包含子串中的任何字元,返回第一個位置
find_last_of 
查詢包含子串中的任何字元,返回最後一個位置
find_last_not_of 
查詢不包含子串中的任何字元,返回最後一個位置
substr 
得到字串
compare 
比較字串
operator+ 
字串連結
operator== 
判斷是否相等
operator!= 
判斷是否不等於
operator< 
判斷是否小於
operator>> 
從輸入流中讀入字串
operator<< 
字串寫入

1)string substr(offset, length)

需要新增標頭檔案<string>,標頭檔案區分大小寫

獲取從offset位置開始,length長度的字串

如果 length 為 0 或負數,將返回一個空字串,如果沒有指定該引數,則子字串將延續到字串的結尾。

2)int find_last_of(char c)

查詢字串中最後一個出現的c。有匹配,則返回匹配位置;否則返回-1.


2、char和string相互轉化

string可以看成是以字元為元素的一種容器,標準的string類提供了STL容器介面,具有一些成員函式如begin()、end()

與char*不同的是,string不一定以NULL('\0')結束。

1)string轉換成char*

不能將string直接賦值給char*

string.c_str()    返回有”\0“的字串陣列

string.data()    返回沒有”\0“的字串陣列

2)char轉換成string

可以直接賦值

string s;
char *p = "adghrtyh";
s = p;

也可以用char *來初始化string

string s(char *)

3、int stat(const char *file_name, struct stat *buf);

可以用來判斷路徑對應是檔案還是目錄,需要新增標頭檔案 #include <sys/stat.h>、#include <unistd.h>
函式說明:    通過檔名filename獲取檔案資訊,並儲存在buf所指的結構體stat中
返回值:     執行成功則返回0,失敗返回-1,錯誤程式碼存於errno
錯誤程式碼:
    ENOENT         引數file_name指定的檔案不存在
    ENOTDIR        路徑中的目錄存在但卻非真正的目錄
    ELOOP          欲開啟的檔案有過多符號連線問題,上限為16符號連線
    EFAULT         引數buf為無效指標,指向無法存在的記憶體空間
    EACCESS        存取檔案時被拒絕
    ENOMEM         核心記憶體不足
    ENAMETOOLONG   引數file_name的路徑名稱太長

-----------------------------------------------------
struct stat {
    dev_t         st_dev;       //檔案的裝置編號
    ino_t         st_ino;       //節點
    mode_t        st_mode;      //檔案的型別和存取的許可權
    nlink_t       st_nlink;     //連到該檔案的硬連線數目,剛建立的檔案值為1
    uid_t         st_uid;       //使用者ID
    gid_t         st_gid;       //組ID
    dev_t         st_rdev;      //(裝置型別)若此檔案為裝置檔案,則為其裝置編號
    off_t         st_size;      //檔案位元組數(檔案大小)
    unsigned long st_blksize;   //塊大小(檔案系統的I/O 緩衝區大小)
    unsigned long st_blocks;    //塊數
    time_t        st_atime;     //最後一次訪問時間
    time_t        st_mtime;     //最後一次修改時間
    time_t        st_ctime;     //最後一次改變時間(指屬性)
};
先前所描述的st_mode 則定義了下列數種情況:
    S_IFMT   0170000    檔案型別的位遮罩
    S_IFSOCK 0140000    scoket
    S_IFLNK 0120000     符號連線
    S_IFREG 0100000     一般檔案
    S_IFBLK 0060000     區塊裝置
    S_IFDIR 0040000     目錄
    S_IFCHR 0020000     字元裝置
    S_IFIFO 0010000     先進先出
    S_ISUID 04000     檔案的(set user-id on execution)位
    S_ISGID 02000     檔案的(set group-id on execution)位
    S_ISVTX 01000     檔案的sticky位
    S_IRUSR(S_IREAD) 00400     檔案所有者具可讀取許可權
    S_IWUSR(S_IWRITE)00200     檔案所有者具可寫入許可權
    S_IXUSR(S_IEXEC) 00100     檔案所有者具可執行許可權
    S_IRGRP 00040             使用者組具可讀取許可權
    S_IWGRP 00020             使用者組具可寫入許可權
    S_IXGRP 00010             使用者組具可執行許可權
    S_IROTH 00004             其他使用者具可讀取許可權
    S_IWOTH 00002             其他使用者具可寫入許可權
    S_IXOTH 00001             其他使用者具可執行許可權 

上述的檔案型別在POSIX中定義了檢查這些型別的巨集定義:
    S_ISLNK (st_mode)    判斷是否為符號連線
    S_ISREG (st_mode)    是否為一般檔案
    S_ISDIR (st_mode)    是否為目錄
    S_ISCHR (st_mode)    是否為字元裝置檔案
    S_ISBLK (s3e)        是否為先進先出
    S_ISSOCK (st_mode)   是否為socket
    若一目錄具有sticky位(S_ISVTX),則表示在此目錄下的檔案只能被該檔案所有者、此目錄所有者或root來刪除或改名。

此處摘自:http://www.360doc.com/content/11/0110/16/4559801_85509847.shtml#

4、statvfs獲取檔案系統統計資訊

摘自:http://yiibai.com/unix_system_calls/fstatvfs.html

struct statvfs {
    unsigned long  f_bsize;    /* file system block size */
    unsigned long  f_frsize;   /* fragment size */
    fsblkcnt_t     f_blocks;   /* size of fs in f_frsize units */
    fsblkcnt_t     f_bfree;    /* # free blocks */
    fsblkcnt_t     f_bavail;   /* # free blocks for non-root */
    fsfilcnt_t     f_files;    /* # inodes */
    fsfilcnt_t     f_ffree;    /* # free inodes */
    fsfilcnt_t     f_favail;   /* # free inodes for non-root */
    unsigned long  f_fsid;     /* file system ID */
    unsigned long  f_flag;     /* mount flags */
    unsigned long  f_namemax;  /* maximum filename length */
  };

#include <sys/statvfs.h>

5、map

標準std中只有map,是使用平衡二叉樹實現的,查詢和新增的複雜度都為O(log(n)),
沒有提供hash map,gnu c++提供了hash_map,是一個hash map的實現,查詢和新增複雜度均為O(1)。

map的功能自動建立”key - value“的對應,Key和Value可以是任意你需要的型別。每個key在map中只能出現一次。

包含標頭檔案#include <map>

std:map<int, string> ,這樣就定義了一個用int為索引,並擁有相關聯的指向string的指標

C++Maps是一種關聯式容器,包含“關鍵字/值”對
 
begin() 返回指向map頭部的迭代器
 
clear() 刪除所有元素
 
count() 返回指定元素出現的次數
 
empty() 如果map為空則返回true
 
end() 返回指向map末尾的迭代器
 
equal_range() 返回特殊條目的迭代器對
 
erase() 刪除一個元素
 
find() 查詢一個元素
 
get_allocator()  返回map的配置器
 
insert() 插入元素
 
key_comp() 返回比較元素key的函式
 
lower_bound() 返回鍵值>=給定元素的第一個位置
 
max_size() 返回可以容納的最大元素個數
 
rbegin() 返回一個指向map尾部的逆向迭代器
 
rend() 返回一個指向map頭部的逆向迭代器
 
size() 返回map中元素的個數
 
swap() 交換兩個map
 
upper_bound() 返回鍵值>給定元素的第一個位置
 
value_comp() 返回比較元素value的函式

1)map中3種插入資料的方法

第一種,用insert函式插入pair資料

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
       Map<int, string> mapStudent;
       mapStudent.insert(pair<int, string>(1, “student_one”));
       mapStudent.insert(pair<int, string>(2, “student_two”));
       mapStudent.insert(pair<int, string>(3, “student_three”));
       map<int, string>::iterator  iter;
       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
       {
           Cout<<iter->first<<”   ”<<iter->second<<end;
       }
}

第二種:用insert函式插入value_type資料

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
       Map<int, string> mapStudent;
       mapStudent.insert(map<int, string>::value_type (1, “student_one”));
       mapStudent.insert(map<int, string>::value_type (2, “student_two”));
       mapStudent.insert(map<int, string>::value_type (3, “student_three”));
       map<int, string>::iterator  iter;
       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
       {
           Cout<<iter->first<<”   ”<<iter->second<<end;
       }
}

第三種:用陣列方式插入資料,map陣列可以用來賦值和讀取,而vector只能讀取

#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
       map<int, string> mapStudent;
       mapStudent[1] =  "student_one";
       mapStudent[2] =  "student_two";
       mapStudent[3] =  "student_three";
       map<int, string>::iterator  iter;
       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
       {
           cout<<iter->first<<"  "<<iter->second<<end;
       }
}

第一種和第二種在效果上是完成一樣的,用insert函式插入資料,

在資料的插入上涉及到集合的唯一性這個概念,即當map中有這個關鍵字時,insert操作是插入資料不了的,但是用陣列方式就不同了,它可以覆蓋以前該關鍵字對應的值,用程式說明

mapStudent.insert(map<int, string>::value_type (1, “student_one”));
mapStudent.insert(map<int, string>::value_type (1, “student_two”));
//上面這兩條語句執行後,map中1這個關鍵字對應的值是“student_one”,第二條語句並沒有生效,而下面這個卻生效。
Map<int, string> mapStudent;
mapStudent[1] =  “student_one”;
mapStudent[1] =  “student_two”;

2)map的大小

在往map裡面插入了資料,我們怎麼知道當前已經插入了多少資料呢,可以用size函式,用法如下:

Int nSize = mapStudent.size();

3)資料的查詢

第一種:用count函式來判定關鍵字是否出現,其缺點是無法定位資料出現位置,由於map的特性,一對一的對映關係,就決定了count函式的返回值只有兩個,要麼是0,要麼是1,出現的情況,當然是返回1

第二種:用find函式來定位資料出現位置,傳入的引數是key,它返回的一個迭代器,當資料出現時,它返回資料所在位置的迭代器,如果map中沒有要查詢的資料,它返回的迭代器等於end函式返回的迭代器,程式說明

#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
       Map<int, string> mapStudent;
       mapStudent.insert(pair<int, string>(1, “student_one”));
       mapStudent.insert(pair<int, string>(2, “student_two”));
       mapStudent.insert(pair<int, string>(3, “student_three”));
       map<int, string>::iterator iter;
       iter = mapStudent.find(1);
       if(iter != mapStudent.end())
       {
           Cout<<”Find, the value is ”<<iter->second<<endl;
       }
       Else
       {
           Cout<<”Do not Find”<<endl;
       }
}
4)資料的清空與判空

清空map中的資料可以用clear()函式,判定map中是否有資料可以用empty()函式,它返回true則說明是空map

5)資料的刪除

這裡要用到erase函式,它有三個過載了的函式,下面在例子中詳細說明它們的用法

#include<map>
#include<string>
#include<iostream>
Usingnamespace std;
Int main()
{

      Map<int,string> mapStudent;
      mapStudent.insert(pair<int,string>(1, “student_one”));
      mapStudent.insert(pair<int,string>(2, “student_two”));
      mapStudent.insert(pair<int,string>(3, “student_three”));

//如果你要演示輸出效果,請選擇以下的一種,你看到的效果會比較好
      //如果要刪除1,用迭代器刪除
      map<int,string>::iterator iter;
      iter= mapStudent.find(1);
      mapStudent.erase(iter);

      //如果要刪除1,用關鍵字刪除
      Int n = mapStudent.erase(1);//如果刪除了會返回1,否則返回0

      //用迭代器,成片的刪除
      //一下程式碼把整個map清空
      mapStudent.earse(mapStudent.begin(),mapStudent.end());
      //成片刪除要注意的是,也是STL的特性,刪除區間是一個前閉後開的集合

       //自個加上遍歷程式碼,列印輸出吧
}

在map insert value的時候出現問題如下:

一大串錯誤說實話很不想看,但我猜了一下估計是key值的問題,於是谷歌一下,發現確實

原來map中的key預設是以less<>升序對元素排序(排序準則也可以修改),也就是說key必須具備operator<對元素排序,而平常我們的用的基本上都是基本型別元素作為key,所以就不存在這個問題了,更詳細的解釋請看C++標準程式庫一書,第六章,set容器章節。

因此,原則上只要支援操作符 < 都可以作為key型別。

在我的map的定義中map<MtpInt128, ObjHandle>裡面的key MtpInt128是個類,裡面沒有實現operator < 這個成員函式

具體的實現如下,格式必須滿足要求,常函式,引數型別是常數:

//map<MtpInt128, ObjHandle> need operator <
bool operator<(const MtpInt128 &rhs) const
{
        for(int32_t i = 15; i >= 0; i--)
        {
		if (this->val[i] < rhs.val[i])
			return true;
		else if (this->val[i] > rhs.val[i])
			return false;
        }
	//if all value of char[16] is the same,return false
        return false;
}

6、vector

容器中所有物件必須是同種型別,我們可以定義儲存string物件的 vector,或儲存int值的vector,又或是儲存自定義的類型別物件。

基本操作

(1)標頭檔案#include<vector>.

(2)建立vector物件,vector<int> vec;

(3)尾部插入數字:vec.push_back(a);在末尾增加一個值為a的元素

(4)使用下標訪問元素,cout<<vec[0]<<endl;記住下標是從0開始的。記住下標只能用於訪問,不能用於賦值。

(5)使用迭代器訪問元素.

vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
    cout<<*it<<endl;

(6)插入元素:   vec.insert(vec.begin()+i,a);在第i+1個元素前面插入a;

(7)刪除元素:   vec.erase(vec.begin()+2);刪除第3個元素

vec.erase(vec.begin()+i,vec.end()+j);刪除區間[i,j-1];區間從0開始

(8)向量大小:vec.size();

(9)清空:vec.clear();

7、list

#include <list>

C++ STL提供了一些容器,list、vector、map、set

STL iterator是容器中指向物件的指標,STL使用iterator在容器上進行操作。

1)push_back和push_front,push_back將物件放到一個list的後面,而push_front把物件放到前面。

list<string> a;

a.push_back("chocolate");

a.push_back("strawberry");

2)empty()

8、vector與list的區別

vector和陣列類似,它擁有一段連續的記憶體空間,並且起始地址不變,因此他能很好的支援隨機存取(即使用[]操作符訪問元素),但由於它的記憶體空間是連續的,所以在中間進行插入和刪除會造成記憶體塊的拷貝(複雜度是O(n)),另外,當該陣列後的記憶體空間不夠時,需要重新申請一塊足夠大的記憶體並進行記憶體的拷貝。這些都大大影響了vector的效率。

list是由資料結構中的雙向連結串列實現的,因此它的記憶體空間可以是不連續的。因此只能通過指標來進行資料的訪問,這個特點使得它的隨機存取變的非常沒有效率,需要遍歷中間的元素,搜尋複雜度O(n),因此它沒有提供[]操作符的過載。但由於連結串列的特點,它可以以很好的效率支援任意地方的刪除和插入

另外list::iterator與vector::iterator也有一些不同

由於vector擁有一段連續的記憶體空間,能非常好的支援隨機存取,因此vector<int>::iterator支援“+”、“+=”、“<”等操作符。

而list的記憶體空間可以是不連續,它不支援隨機訪問,因此list<int>::iterator則不支援“+”、“+=”、“<”等操作符運算。只能使用“++”進行迭代

總之,如果需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector;如果需要大量的插入和刪除,而不關心隨即存取,則應使用list。

摘自:http://genwoxuevc.blog.51cto.com/1852984/503337

9、C/C++中的日期和時間tmie_t,struct tm

1)struct tm

在標準C/C++中,我們可通過tm結構來獲得日期和時間,tm結構在time.h中的定義如下:

#ifndef _TM_DEFINED
struct tm {
        int tm_sec;     /* 秒 – 取值區間為[0,59] */
        int tm_min;     /* 分 - 取值區間為[0,59] */
        int tm_hour;    /* 時 - 取值區間為[0,23] */
        int tm_mday;    /* 一個月中的日期 - 取值區間為[1,31] */
        int tm_mon;     /* 月份(從一月開始,0代表一月) - 取值區間為[0,11] */
        int tm_year;    /* 年份,其值等於實際年份減去1900 */
        int tm_wday;    /* 星期 – 取值區間為[0,6],其中0代表星期天,1代表星期一,以此類推 */
        int tm_yday;    /* 從每年的1月1日開始的天數 – 取值區間為[0,365],其中0代表1月1日,1代表1月2日,以此類推 */
        int tm_isdst;   /* 夏令時識別符號,實行夏令時的時候,tm_isdst為正。不實行夏令時的進候,tm_isdst為0;不瞭解情況時,tm_isdst()為負。*/
        };
#define _TM_DEFINED
#endif

ANSI C標準稱使用tm結構的這種時間表示為分解時間(broken-down time)。

2)time_t
而日曆時間(Calendar Time)是通過time_t資料型別來表示的,用time_t表示的時間(日曆時間)是從一個時間點(例如:1970年1月1日0時0分0秒)到此時的秒數。在time.h中,我們也可以看到time_t是一個長整型數:

#ifndef _TIME_T_DEFINED
typedef long time_t;         /* 時間值 */
#define _TIME_T_DEFINED     /* 避免重複定義 time_t */
#endif

3)time_t轉成struct tm

struct tm * gmtime(const time_t *timer);                                          
struct tm * localtime(const time_t * timer);

4)將時間結構體轉為字串

我們可以通過asctime()函式和ctime()函式將時間以固定的格式顯示出來,兩者的返回值都是char*型的字串。返回的時間格式為:

星期幾 月份 日期 時:分:秒 年\n\0
例如:Wed Jan 02 02:03:55 1980\n\0

其中\n是一個換行符,\0是一個空字元,表示字串結束。下面是兩個函式的原型:

char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);

10、linux c++輸出debug資訊

三個重要的巨集

 __LINE__ 當前的行號

 __FILE__ 當前的檔名(xxx.cpp)

 __PRETTY_FUNCTION__/__FUNCTION__ 帶簽名和不帶簽名的函式名

11、C++類建構函式初始化

class CExample {
public:
        int a;
        float b;
        CExample() : a(0), b(8, 8)
        {}
        CExample()
        {
            a = 0;
            b = 8.0;
        }
};
上面的例子中兩個建構函式的結果是一樣的。上面的建構函式(使用初始化列表的建構函式)顯式的初始化類的成員;而沒使用初始化列表的建構函式是對類的成員賦值,並沒有進行顯式的初始化。

有的時候必須用帶有初始化列表的建構函式:
1.成員型別是沒有預設建構函式的類。若沒有提供顯示初始化式,則編譯器隱式使用成員型別的預設建構函式,若類沒有預設建構函式,則編譯器嘗試使用預設建構函式將會失敗。
2.
const成員引用型別的成員。因為const物件或引用型別只能初始化,不能對他們賦值。

初始化資料成員與對資料成員賦值的含義是什麼?有什麼區別?
首先把資料成員按型別分類並分情況說明:
1.內建資料型別,複合型別(指標,引用)
    在成員初始化列表和建構函式體內進行,在效能和結果上都是一樣的
2.使用者定義型別(類型別)
    
結果上相同,但是效能上存在很大的差別。

    因為類型別的資料成員物件在進入函式體前已經構造完成,也就是說在成員初始化列表處進行構造物件的工作;

    呼叫建構函式,在進入函式體之後,進行的是對已經構造好的類物件的賦值,又呼叫個拷貝賦值操作符才能完成(如果並未提供,則使用編譯器提供的預設按成員賦值行為)

Note:
初始化列表的成員初始化順序:
    C++初始化類成員時,是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。

    Example:

class CMyClass {
       CMyClass(int x, int y);
       int m_x;
       int m_y;
};
CMyClass::CMyclass(int x, int y) ; m_y(y), m_x(m_y)
你可能以為上面的程式碼將會首先做m_y=y,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順序宣告的。結果是m_x將有一個不可預測的值。有兩種方法避免它,一個是總是按照你希望它們被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。