C++闲谈01——String

C++闲谈01——手写String

String内部函数介绍

4

  • 实现构造函数
  • 实现析构函数
  • 实现拷贝构造
  • 实现赋值构造

3

  • 实现 “>” 运算符重载

  • 实现 “<” 运算符重载

  • 实现“==” 运算符重载

2

  • 实现获取长度 length()

  • 实现c_str()

1

  • 实现 "+"运算符重载

2

  • 实现"[]"运算符重载

  • 实现 << 运算符重载

字符串处理函数

字符串函数:strlen函数,strcpy函数,strcat函数,strcmp函数

  • strlen() 计算字符串长度 以‘\0’结束
  • strcpy() strcpy(src, dest) 将dest的内容拷贝到src 前提是开辟好了对应类型的内存
  • strcmp() >0 <0 == 0
  • strcat() 追加拷贝 追加到目标空间后面,目标空间必须足够大,能容纳下源字符串的内容

实现代码

#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;
}

C++闲谈01——String_第1张图片

野指针问题

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掉

  • delete之前 return
  • new 没有分配对应的delete

如何解决内存泄漏

  • 用智能指针去管理
  • 一些内存泄漏检测工具 clion中集成的valgrint asan

内存泄漏检测工具asan

delete new详解

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 和 memecpy

strcpy和memcpy的区别

1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

你可能感兴趣的:(C++学习,c++,开发语言)