4
3
实现 “>” 运算符重载
实现 “<” 运算符重载
实现“==” 运算符重载
2
实现获取长度 length()
实现c_str()
1
2
实现"[]"运算符重载
实现 << 运算符重载
字符串函数:strlen函数,strcpy函数,strcat函数,strcmp函数
#include
#include
using namespace std;
class String {
public:
String(const char* p = nullptr) {
if (p == nullptr) {
_ptr = new char[1];
_ptr = '\0';
} else {
_ptr = new char[strlen(p) + 1];
strcpy(_ptr, p);
}
}
~String() {
delete[] _ptr;
_ptr = nullptr;
}
String(const String &src) {
_ptr = new char[strlen(src._ptr) + 1];
strcpy(_ptr, src._ptr);
}
String& operator= (const String &src) {
if (this == &src) {
return *this;
}
delete[] _ptr;
_ptr = new char[strlen(src._ptr) + 1];
strcpy(_ptr, src._ptr);
return *this;
}
bool operator> (const String &src) {
return strcmp(_ptr, src._ptr) > 0;
}
bool operator< (const String &src) {
return strcmp(_ptr, src._ptr) < 0;
}
bool operator== (const String &src) {
return strcmp(_ptr, src._ptr) == 0;
}
int length() const{
return strlen(_ptr);
}
const char* c_str() const{
return _ptr;
}
char& operator[] (int index) {
return _ptr[index];
}
private:
char* _ptr;
friend String operator+ (const String &lhs, const String &rhs);
friend ostream& operator<< (ostream &out, const String &str);
};
String operator+ (const String &lhs, const String &rhs) {
String tmp;
tmp._ptr = new char[strlen(lhs._ptr) + strlen(rhs._ptr) + 1];
strcpy(tmp._ptr, lhs._ptr);
strcat(tmp._ptr, rhs._ptr);
return tmp;
}
ostream& operator<< (ostream &out, const String &str) {
out << str._ptr;
return out;
}
int main() {
String str1;
String str2 = "aaa"; // string(const char*)
String str3 = "bbb";
String str4 = str2 + str3;
String str5 = str2 + "ccc";
String str6 = "ddd" + str2;
cout << "str6:" << str6 << endl;
// bool operator>(const String &str)
if (str5 > str6)
{
cout << str5 << " > " << str6 << endl;
}
else
{
cout << str5 << " < " << str6 << endl;
}
int len = str6.length();
for (int i = 0; i < len; ++i)
{
// char& str6.operator[](i)
cout << str6[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
delete或者free之后为什么要将指针指为NULL
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
(2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。参见7.5节。
(3)指针操作超越了变量的作用范围。这种情况让人防不胜防
new的内存没有被合理的delete掉
内存泄漏检测工具asan
c++ new[] delete[]底层分析
在new的时候在堆上多申请4个字节 存放数组长度 然后返回的new的地址是 首地址 + 4
在delete的时候 首先访问传回来的地址 - 4 获取整个数组的长度 然后释放内存
demo:
#include
int main()
{
auto pS = new std::string[10];
delete[] pS;
return 0;
}
new的大小为0x11c,十进制284,相当与10*28 + 4。4个额外申请的长度就是保存数组大小用的。
auto pS = new std::string[10];
009C970D push 11Ch
009C9712 call operator new[] (09C376Ch)
009C9717 add esp,4
009C971A mov dword ptr [ebp-0F8h],eax
在new返回的地址先写入的数组的长度10,注意返回给用户的地址是这个地址+4后的长度。
009C972E je main+9Ah (09C976Ah)
009C9730 mov eax,dword ptr [ebp-0F8h]
009C9736 mov dword ptr [eax],0Ah
数组对象开始调用构造函数初始化:
009C973C push 9C3FB9h
009C9741 push 9C31DBh
009C9746 push 0Ah //对象的个数
009C9748 push 1Ch //对象的大小
009C974A mov ecx,dword ptr [ebp-0F8h]
009C9750 add ecx,4
009C9753 push ecx //将new返回的地址+4就是第一个对象的首地址
009C9754 call `eh vector constructor iterator' (09C3EECh)
009C9759 mov edx,dword ptr [ebp-0F8h]
009C975F add edx,4
009C9762 mov dword ptr [ebp-10Ch],edx
//注意这里返回的地址是+4的,这是用户拿到的地址。复制流是这样的[ebp-10ch]->[ebp-104h]->ecx->pS
009C9768 jmp main+0A4h (09C9774h)
009C976A mov dword ptr [ebp-10Ch],0
009C9774 mov eax,dword ptr [ebp-10Ch]
009C977A mov dword ptr [ebp-104h],eax
009C9780 mov dword ptr [ebp-4],0FFFFFFFFh
009C9787 mov ecx,dword ptr [ebp-104h]
009C978D mov dword ptr [pS],ecx
所以我们调用new[]多个对象的时候,返回地址-4上保存着这个对象数组的大小。
接着我们看delete[]的时候是怎么用的:上来就是空指针检查,即使我们C++代码里面没有写。
delete[] pS;
009C9790 mov eax,dword ptr [pS]
009C9793 mov dword ptr [ebp-0E0h],eax
009C9799 mov ecx,dword ptr [ebp-0E0h]
009C979F mov dword ptr [ebp-0ECh],ecx
009C97A5 cmp dword ptr [ebp-0ECh],0 //检查pS是否为NULL
009C97AC je main+0F3h (09C97C3h)
009C97AE push 3
delete[] pS;
009C97B0 mov ecx,dword ptr [ebp-0ECh]
009C97B6 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::`vector deleting destructor' (09C4216h)
跟入vector deleting destructor函数看看:先[pS-4]取数组的的长度,调用析构函数,然后delete释放内存。
009C78DB push 9C3FB9h
009C78E0 mov eax,dword ptr [this] //取pS的值
009C78E3 mov ecx,dword ptr [eax-4] //取对象数组的长度
009C78E6 push ecx //string对象的个数10
009C78E7 push 1Ch //strig对象的大小
009C78E9 mov edx,dword ptr [this]
009C78EC push edx
009C78ED call `eh vector destructor iterator//调用string析构函数
009C78F2 mov eax,dword ptr [ebp+8]
009C78F5 and eax,1
009C78F8 je std::basic_string<char,std::char_traits<char>,std::allocator<char> >::`vector deleting destructor'+59h (09C7909h)
009C78FA mov eax,dword ptr [this]
009C78FD sub eax,4 //pS-4才是申请堆block的首地址,然后delete释放。
009C7900 push eax
009C7901 call operator delete[] (09C3136h)
009C7906 add esp,4
总结:delete[]参数不用输入数组的长度,是CRT实现new的时候在堆userdata首地址放上了数组长度,new返回的地址是申请到地址+4的值。然后delete[]释放的时候,用传入地址-4拿数组长度,实现数组释放。
strcpy和memcpy的区别
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。