作为一个初学者,看侯捷老师的视频学习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;
};
分析
为什么放指针
字符串大小不固定,初始化构造时没办法确定大小。所以设计成class内拥有一个指针,在真正需要内存的时候,再创建一个空间来放字符,然后把指针指向这个字符地址
函数分析
从上往下,函数依次是:
- 构造函数,传进来一个指针,默认是0
- 构造函数,传进来自己本身这类东西,
拷贝构造 - 赋值的动作,操作符
=重载,拷贝赋值 - 析构函数,对象销毁时要执行
在含有指针的class,上述几个函数都是必须的
- 普通成员函数,返回指针
代码
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的引用地址。会产生如下的内存泄露问题
代码
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内容:
- delete[] m_data; 杀掉自己本来的内存
- m_data = new char[ strlen(str.m_data) + 1 ]; 计算新值的大小
- strcpy(m_data, str.m_data); 拷贝值
检查是否是自己
这里的检查 this == &str一定要做。不然会发生第一步delete自己的内存,第二步获取内存的时候,由于前面已经delete掉了,所以会报错!