用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的时候出现问题如下:

用C++写MTP程序遇到的知识_第1张图片

一大串错误说实话很不想看,但我猜了一下估计是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将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

你可能感兴趣的:(用C++写MTP程序遇到的知识)