前言
在c++里 std::cout << 和 std::cin >>
用多了可能都要忘了<<>>
本质上是位移运算符。
只不过std本质上是一个命名空间,可能在里面也定义了类也带上了重载。
正文
1 2 3 4 5
| 返回类型 operator>>(类型 操作数);
返回类型 operator>>(类型 左操作数,类型 右操作数);
|
这里还是以昨天hstring为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #pragma once class hstring{ private: char *c_str; unsigned short hsLen; unsigned short hsmLen; unsigned short gethsLen(const char *str) const; void copyStrs(char *dest, const char *source);
public: hstring(); hstring(const char *str); hstring(const hstring &str); hstring &operator=(const hstring &str);
char *rtstr(){ return c_str; }
};
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include "hstring.h" #include<iostream>
hstring::hstring(){ hsmLen = 0x32; hsLen = 0; c_str = new char[hsmLen]; }
hstring::hstring(const char *str){ copyStrs(c_str, str); } hstring::hstring(const hstring &str){ copyStrs(c_str, str.c_str); } hstring &hstring::operator=(const hstring &str){ copyStrs(c_str, str.c_str); return *this; } unsigned short hstring::gethsLen(const char *str) const{ unsigned short len = 0; while (str[len++] != '\0');
return len; }
void hstring::copyStrs(char *dest, const char *source){ unsigned short len = gethsLen(source); if (len > hsmLen){ c_str = new char[len]; hsmLen = len; } memcpy(c_str, source, len); hsLen = len; }
|
小问题1
再次之前,先解决下之前有个函数可能存在内存泄漏
1 2 3 4 5 6 7 8 9 10
| void hstring::copyStrs(char *dest, const char *source){ unsigned short len = gethsLen(source); if (len > hsmLen){ delete[] c_str; c_str = new char[len]; hsmLen = len; } memcpy(c_str, source, len); hsLen = len; }
|
就是重新分配内存前,应该清理掉原有的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| hstring &operator<<(const hstring &str);
hstring &hstring::operator<<(const hstring &str){ unsigned short len = gethsLen(str.c_str); len += hsLen - 1; if (len > hsmLen){ delete[] c_str; c_str = new char[len]; hsmLen = len; } memcpy(this->c_str + hsLen -1, str.c_str, len - hsLen + 1);
hsLen = len;
return *this; }
|
小问题2
在调试的时候发现了一个问题,就是构造函数,我们初始化的时候只写在了默认构造函数里,导致使用其它构造函数实例化的时候,成员变量没有初始化的问题,会有随机值产生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| hstring::hstring(){ hsmLen = 0x32; hsLen = 0; c_str = new char[hsmLen]; }
hstring::hstring(const char *str){ hsmLen = 0x32; hsLen = 0; c_str = new char[hsmLen]; copyStrs(c_str, str); } hstring::hstring(const hstring &str){ hsmLen = 0x32; hsLen = 0; c_str = new char[hsmLen]; copyStrs(c_str, str.c_str); }
|
所以要注意,无论是哪个构造函数都要进行初始化,毕竟实例化的时候不管这些。
当然这样写看着又很雷同,于是可以用列表初始化替换掉一些:
1 2 3 4 5 6 7 8 9 10 11
| hstring::hstring(char ch=0x32){ hsmLen = ch; hsLen = 0; c_str = new char[hsmLen]; } hstring::hstring(const char *str):hstring(){ copyStrs(c_str, str); } hstring::hstring(const hstring &str):hstring(){ copyStrs(c_str, str.c_str); }
|
这样一来解决了一些重复的事情~~~~也确实是我上一章没注意到。
实际效果也ok,能够完整的显示出来。
而且重载可以连续套用。
那么说<<
相当于拼接字符串了,数学角度跟加法就差不多了。所以加号运算符重载的话可以套用了
1 2 3 4 5
| hstring &operator+(const hstring &str);
hstring &hstring::operator+(const hstring &str){ return *this << str; }
|
效果也是一样的,毕竟底层一样了。
缝缝补补
然后是析构函数,析构函数也不用干啥,就是释放c_str就行
1 2 3
| hstring::~hstring(){ if (c_str != nullptr) delete[] c_str; }
|
还有个就是std::cout 的问题,他不能直接输出,而是通过我们的函数返回的。
学了重载就要给它优化一下。
看到std::后面其实也是个参数,一个流的称呼。
1 2 3 4
| std::ostream &operator<<(std::ostream &_cout, hstring _str){ _cout << _str.rtstr(); return _cout; }
|
通过重写输出流的<<重载,将hstring的参数直接拼接到输出流,然后返回。
调用的时候也能看到是用的我们写的重载
实际效果也没有差别。
1
| std::cout << str << "1111" << "222" << std::endl;
|
这个时候,str后面的字符串所用的<<
重载是属于std::cout的
它本质上就是operator<<(operator<<(operator<<(std::cout,str),"1111"),"222");
指定了输出流,然后参数一个一个通过<<
写入。
指定了输出,就可以尝试一下输入
1 2 3
| std::istream &operator>>(std::istream &_cin, hstring &_str){
}
|
流要使用istream,i和o的区别就是input和output,输入输出的意思。
只不过这里输入的时候其实要注意缓冲区。。默认的输入流还真不知道要给多少合适。
1 2 3 4 5 6 7
| std::istream &operator>>(std::istream &_cin, hstring &_str){ char _len[0xff]; _cin >> _len; _str = _len;
return _cin; }
|
缓冲区不好定,就随便给个大的先做一下效果。
这样通过这个缓冲的字符串,可以给str有空计算然后写入。
效果就是这样了~~~
大致完善后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #pragma once class hstring{ private: char *c_str; unsigned short hsLen; unsigned short hsmLen; unsigned short gethsLen(const char *str) const; void copyStrs(char *dest, const char *source);
public: hstring(char ch); hstring(const char *str); hstring(const hstring &str); hstring &operator=(const hstring &str); hstring &operator<<(const hstring &str); hstring &operator+(const hstring &str); ~hstring();
char *rtstr(){ return c_str; }
};
std::ostream &operator<<(std::ostream &_cout, hstring _str); std::istream &operator>>(std::istream &_cin, hstring &_str);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include "hstring.h" #include<iostream>
hstring::hstring(char ch=0x32){ hsmLen = ch; hsLen = 0; c_str = new char[hsmLen]; }
hstring::hstring(const char *str):hstring(){ copyStrs(c_str, str); } hstring::hstring(const hstring &str):hstring(){ copyStrs(c_str, str.c_str); } hstring &hstring::operator=(const hstring &str){ copyStrs(c_str, str.c_str); return *this; } hstring &hstring::operator<<(const hstring &str){ unsigned short len = gethsLen(str.c_str); len += hsLen - 1; if (len > hsmLen){ delete[] c_str; c_str = new char[len]; hsmLen = len; } memcpy(this->c_str + hsLen -1, str.c_str, len - hsLen + 1); hsLen = len;
return *this; } hstring &hstring::operator+(const hstring &str){ return *this << str; } hstring::~hstring(){ if (c_str != nullptr) delete[] c_str; } unsigned short hstring::gethsLen(const char *str) const{ unsigned short len = 0; while (str[len++] != '\0');
return len; }
void hstring::copyStrs(char *dest, const char *source){ unsigned short len = gethsLen(source); if (len > hsmLen){ delete[] c_str; c_str = new char[len]; hsmLen = len; } memcpy(c_str, source, len); hsLen = len; }
std::ostream &operator<<(std::ostream &_cout, hstring _str){ _cout << _str.rtstr(); return _cout; }
std::istream &operator>>(std::istream &_cin, hstring &_str){ char _len[0xff]; _cin >> _len; _str = _len;
return _cin; }
|
结语
字符串最重要的就是缓冲区的问题,尽量减少内存分配浪费的时间和内存。
同时也要注意安全。