一.构造函数
第一种形式,使用字符指针赋值
为了防止修改,我们传入了常量字符串。但是这里的初始化列表出错了,因为_str是一个变量,将常量给到一个变量涉及到权限的放大,是错误的。那该怎么写呢?对_str的赋值只能使用strcpy字符串拷贝,如下:
这样对了吗??还是不对!!!因为_str没有开辟空间!!!!
体现了初始化列表与函数的结合
但是,main函数中创建一个字符串对象却出错了!!!!
为什么呢?注意初始化列表的顺序!!!!!是按照变量定义的顺序进行的!!!然而_str是最先定义的,所以在执行_str(new char[_chpacity+1]时,_capacity还是随机值(或者是vs下的0),所以就会出错!!!。所以,对_str的空间的开辟最好定义在函数体内,如下:
二.头文件包含问题以及预处理文件的查看
上面我们在写mystring.h的时候没有包含任何头文件,但为什么strcpy能够使用,能够编译运行通过呢?
这是因为,十以上如处理后的文件是将main所在文件中包含的头文件都展开了,也就是将iostream、string、std、mystring.h依次展开,所以当mystring.h中用到strcpy时,他就会主动去上面寻找定义。这可以根据预处理的文件证明。如下为查看预处理文件的方式:
右键项目-属性-c\c++-预处理器,进行如下修改:
找到要查看的文件的预处理文件,进行编译:
打开被编译文件所在文件夹:
找到debug文件夹:
进入debug文件夹并打开test1.i文件:
就可以看到了:
在main函数之上都是头文件的展开,包括了库里的头文件以及我们自己写的头文件。
注意,看完了记住将是改成否!!!
但注意,如果mystring.h的包含实在std上面的话,有些std中的东西在mystring.h中就用不了了,比如cout。
三.c_str
注意,返回值是常量指针类型。
四.无参构造
能否再写个如下的无参构造?
不可以,这样的话,c_str可能会崩掉。
那怎么写最好呢?只写一个构造函数,并且是全缺省的,如下:
五.赋值重载
如果没有重写赋值函数,就会出现下面的情况:
进行了浅拷贝,s和s2指向了同一块空间。所以要重写赋值重载函数:
六.operator[]
两个版本,可读可写。
七.迭代器
然后再写个const形式的:
这样对吗?显然不对
为什么呢?因为const iterator其实是等价于char* const,也就是说表示指针是一个常量的,他的指向不能修改,但是,他指向的空间的内容可以修改!!!,那就没有防止修改的意义了。应该如下修改
这样就会发现,const字符串调用了begin后,生成的迭代器不能给到一个变量,而只能给到一个常量迭代器。,这样,const字符串就不能被修改了。
八.push_back
if里面的内容其实就是reserve预留空间这个函数,所以我们可以封装起来:
但注意细节,将tmp给到_str前,要释放原来的空间:注意是显示放再赋值
还有一个错误:尾插后要让size加一,并且要在尾部添加‘\0'。,最后如下:
九.append
注意最后一个字符置为0,以防传参时仅仅传入一个字符的地址!!!
十.insert
这样写有一个弊端:向后移动的时候,字符串末尾的'\0'被覆盖了!!!怎么办??一种方式是在插入完毕后再规定_size位置为\0,还有一种方法是从第_size位置开始移动,也就是连着末尾的\0一起移动,如下:
这段代码还有要注意的地方:如果移动的代码是像下面一样写的,那么当头插的时候,由于i是size_t类型,i减到最后本应是-1,但是编译器认为这是无符号整型,是一个非常大的数,就会导致无限循环:
一定要注意细节。
十一.流插入重载
第一种方式,写成友元函数。但要注意,友元函数必须和对应的类写在一个命名空间中!!!!否则依然无法访问类中的私有成员,如下:
但其实,这里的流插入不需要访问类内部的私有成员,所以不是非得写成友元函数,也可以在明明空间外面这样写:
注意,参数有误,写到命名空间外面后,就要在string前面加上命名空间的名字,如下:
注意,这里的auto其实就是转成了迭代器的调用。那么它调用的是const迭代器还是非const迭代器呢?如下可以证明
调用的是常量迭代器
十二.流提取重载
注意,有个问题,cin会自动跳过空格,去获取下一个字符。这怎么办??
cin是istream类型的对象,其中有一个成员函数get,它可以拿到所有的字符
还有一个问题,输入流是要把s的内容全部改了,但是加等就是简简单单的追加。所以在操作之前要将s清空。如下加上这个clear函数:
但是这个流提取的写法还不够好,它得一直扩容。
解决方法一:使用reserve预留空间。但是,不知道需要多大,太大了浪费
解法二:使用一个字符数组:
十三.erase
这其实只是一个最主要步骤。但是还有其他的。首先,npos是自定义的,要定义成成员变量,为了防止修改,将他定义成const的,同时,npos应该是所有成员共享的,所以定义成static的。static成员要在类外初始化
其次,还要考虑pos+len>size的情况
最后,别忘记更新_size的值。
十四.拷贝构造
开辟一个一样大的空间,将数据拷贝过去。
深拷贝到本质:不能任由指针成员进行拷贝,但我们可以考虑让别人做
也可以封装成函数,注意命名空间
十五.回看赋值重载
可以调用reserve来开辟空间。
但也可以用到swap,借他人之手来进行
这种现代写法有个好处,原先自己的空间不用去主动释放,而是交换给了tmp,让tmp去释放。
最极致的现代写法:
直接交换s,但注意,一定要改成string变量,而不能使用const引用了