用C++寫MTP程式遇到的知識
1、C++ string類的方法
string 函式列表
函式名 描述
begin 得到指向字串開頭的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.使用者定義型別(類型別)
結果上相同,但是效能上存在很大的差別。
因為類型別的資料成員物件在進入函式體前已經構造完成,也就是說在成員初始化列表處進行構造物件的工作;
呼叫建構函式,在進入函式體之後,進行的是對已經構造好的類物件的賦值,又呼叫個拷貝賦值操作符才能完成(如果並未提供,則使用編譯器提供的預設按成員賦值行為)
初始化列表的成員初始化順序:
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將有一個不可預測的值。有兩種方法避免它,一個是總是按照你希望它們被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。