写一个String类,实现一下功能:
int main()
{
String s1();
String s2("hello");
String s3(s2);
cout<<s3<<endl;
s3 = s2;
cout<<s3<<endl;
}
1 Big Three 三个特殊函数
class String
{
public:
String (const char* cstr = 0);//构造函数
String (const String& str);//拷贝构造函数 返回类型就是String
String& operator = (const String& str);//运算符重载 但是是拷贝重载
~String();//析构函数
char* get_c_str() const{return m_data;}
private:
char* m_data;//使用指针,动态的创建空间
}
2 构造函数和析构函数
2.1 析构函数
inline
String::String (const char* cstr = 0)
{
if(cstr)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data , cstr);
}
else
{
m_data = new char[1];
*m_data = '\0';
}
}
inline
String::~String()
{
delete[] = m_data;//array new 必须搭配 array delete
}
class有指针,多半需要做动态分配,既然有了动态分配,对象死亡之前,析构函数被调用,把动态分配的内存释放
2.2 拷贝构造和拷贝赋值
class with pointer members 必须有 copy ctor和copy op=
原因:假设,String a = (“Hello”) String b = (“World”) 想要实现b = a
如果只是传统意义上b = a,那么仅仅只是把b指向字符串内容的指针,指向了a
2.2.1拷贝构造函数:
inline
String :: String(const String& str)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str.m_data);
}
2.2.2拷贝赋值函数
一个盒子a和一个盒子b,里面都放满东西。现在,要把b里的东西放到a
(1)清空a (2)b里开一个空间和a一样大(3)把a放进去
inline
String& String :: operator=(const String& str)
{
if(this == &str)检测自我赋值,必写 否则执行下面三点
会先杀掉自己,后面就没法执行了
{
return *this;
}
delete[] m_data; (1)
m_data = new char[strlen(str.m_data) + 1];(2)
strcpy(m_data,str.m_data);(3)
}
2.3 output函数
重载 << 操作符 , 显然,重载为全局函数。
#include <iostream.h>
ostream& operator << (ostream& os , const String& str)
{
os<<str.get_c_str();
把str的指针放到os
return os;
}
3 stack and heap
class Complex{...};
...
{
Complex c1(1,2);
Complex* p = new Complex(3);
}
第一个 c1的创建,是一个local变量,在栈创建,离开这个作用于自动销毁
第二个c2是new了一个内存,由堆分配,必须手动delete
3.1stack object生命周期
c1便是如此,离开作用域就结束。又名 auto object,析构函数会被自动调用
3.2 static local objects 生命周期
class Complex{...};
...
{
static Complex c2(1,2);
}
离开作用域后,c2依然存在,直到整个程序结束
3.3 global objects生命周期
class Complex{...};
...
Complex c3(1,2);
int main()
{}
c3在整个程序结束后才结束
3.4 heap objects
class Complex{...};
...
{
Complex* p = new Complex(3);
...
delete p;
}
p在离开作用域后,p所指向的heap object还存在,但是P的生命结束了。内存还没有被释放。所以必须手动delete,否则会内存泄露
4 new 和 delete
4.1 new
先分配内存,再调用ctor(构造函数)
Complex* pc = new Complex(1 ,2);
分三步:
(1)void* mem = operator new(sizeof(Complex));//operator new 本质就是 malloc(),分配内存
(2)pc = static_cast<Complex* >(mem);//转型 mem是void*,pc是Complex*
(3)pc->Complex::Complex(1,2);//构造函数
4.2 delete
先调用dtor,再释放memory
delete ps;
分两步:
(1)String :: ~String(ps);
(2)operator delete(ps);
首先第一步,看string析构函数干嘛的:
public:
...
String :: ~String()
{
delete[] m_data;
}
private:
char* m_data;
字符串只是一个指针,指向了一块区域,所以第一步,是先把字符串里动态分配的沙雕。第二步,才把字符串本身杀掉。