跟着侯捷老师学C++☛从string函数定义中学到了什么

118 阅读2分钟

作为一个初学者,看侯捷老师的视频学习C++
第二个demo讲解,带指针的类

代码

class String
{
public:                                 
   String(const char* cstr=0);                     
   String(const String& str);                    
   String& operator=(const String& str);         
   ~String();                                    
   char* get_c_str() const { return m_data; }
private:
   char* m_data;
};

分析

为什么放指针

image.png

字符串大小不固定,初始化构造时没办法确定大小。所以设计成class内拥有一个指针,在真正需要内存的时候,再创建一个空间来放字符,然后把指针指向这个字符地址

函数分析

从上往下,函数依次是:

  1. 构造函数,传进来一个指针,默认是0
  2. 构造函数,传进来自己本身这类东西,拷贝构造
  3. 赋值的动作,操作符=重载,拷贝赋值
  4. 析构函数,对象销毁时要执行

在含有指针的class,上述几个函数都是必须的

  1. 普通成员函数,返回指针

代码

inline
String::String(const char* cstr)
{
   if (cstr) {
      m_data = new char[strlen(cstr)+1];
      strcpy(m_data, cstr);
   }
   else {   
      m_data = new char[1];
      *m_data = '\0';
   }
}

inline
String::~String()
{
   delete[] m_data;
}

分析

字符串长度

字符串默认是以 \0 结尾的,所以字符串长度需要加1,如上述代码 m_data = new char[strlen(cstr)+1];

new

new 关键字,动态分配内存,如上述的 m_data = new char[1];

注意这里, m_data 本身是一个char类型的指针

delete

析构函数里,删除这个指针m_data。因为这个m_data实际是由new动态分配的内存,所以一定delete。 而且由于这个指针指向的是一个char的数组,所以delete[]

代码

{
  String s1();
  String s2("hello");
  
  String* p = new String("hello");
  delete p;
}

分析

创建了3个字符串,但最后只delete了p。

这是因为p是new出来的,动态分配了一块内存,来让p指向这块内存。 而字符串本身,在销毁的时候会调用析构函数,析构函数里执行了delete[] m_data;将动态分配的m_data销毁掉了。所以在外面不需要主动的去delete(而且也delete不掉?)

代码

inline
String::String(const String& str)
{
   m_data = new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
}

调用

String s1("hello");
String s2(s1); // 相当于 String s2 = s1;

分析

如果没有拷贝构造函数,这里会采取默认构造函数。那么接收的将是s1的引用地址。会产生如下的内存泄露问题

image.png

代码

inline
String& String::operator=(const String& str)
{
   if (this == &str)
      return *this;

   delete[] m_data;
   m_data = new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
   return *this;
}

// 调用
String s1("hello");
String s2(s1);
s2 = s1;

分析

步骤

把s1的值赋给s2,s2本来的值不需要了,所以需要先杀掉。然后根据s1的大小,新建内存空间,然后再copy内容:

  1. delete[] m_data; 杀掉自己本来的内存
  2. m_data = new char[ strlen(str.m_data) + 1 ]; 计算新值的大小
  3. strcpy(m_data, str.m_data); 拷贝值

检查是否是自己

这里的检查 this == &str一定要做。不然会发生第一步delete自己的内存,第二步获取内存的时候,由于前面已经delete掉了,所以会报错!