A Simple String Class

  1. #pragma once
  2. #include <iostream>
  3. #include <cstring>
  4. class MyString
  5. {
  6. private:
  7. int buf_len;
  8. char *characters;
  9. public:
  10. MyString(int buf_len = 64, const char *data = NULL)
  11. {
  12. std::cout << "Constructor(int, char*)" << std::endl;
  13. this->buf_len = 0;
  14. this->characters = NULL;
  15. create(buf_len, data);
  16. }
  17. ~MyString()
  18. {
  19. delete[] this->characters;
  20. }
  21. bool create(int buf_len, const char *data)
  22. {
  23. this->buf_len = buf_len;
  24. if (this->buf_len != 0)
  25. {
  26. this->characters = new char[this->buf_len]{};
  27. if (data)
  28. strncpy(this->characters, data, this->buf_len);
  29. }
  30. return true;
  31. }
  32. friend std::ostream &operator<<(std::ostream &os, const MyString &ms)
  33. {
  34. os << "buf_len = " << ms.buf_len;
  35. os << ", characters = " << static_cast<void *>(ms.characters);
  36. os << " [" << ms.characters << "]";
  37. return os;
  38. }
  39. };

采用动态内存,再构造函数中申请内存,再析构函数中释放内存,是不是就万事大吉呢?我们运行一下程序

  1. #include <iostream>
  2. #include "mystring.hpp"
  3. using namespace std;
  4. // Why memory leak and memory double free?
  5. int main()
  6. {
  7. MyString str1(10, "Shenzhen");
  8. cout << "str1: " << str1 << endl;
  9. MyString str2 = str1; // 调用了copy constructor,str1和str2 指向的相同的内存空间
  10. cout << "str2: " << str2 << endl;
  11. MyString str3; // 调用了默认的构造函数,
  12. cout << "str3: " << str3 << endl;
  13. str3 = str1; // 这时赋值操作,编译器会生成=的运算符重载,把str1的成员拷贝给str2
  14. cout << "str3:" << str3 << endl;
  15. return 0;
  16. }

Video_2021-12-12_131534.wmv这时这三个对象指向的内存空间相同,再程序即将完成退出之前,要执行析构函数对他们的内存销毁,执行完一个对象内存销毁之后,再进行第一个对象内存销毁时,就会出错。再str3创建的时候,会开辟一块内存空间,但是当str3 = str1之后,str3的变量characters修改了内存地址,所以没有变量指向这块内存地址,就会造成内存泄漏。

这种内存泄漏的bug非常难排查。那么应该如何避免呢?