String class (如果class里面带指针, 就不能用编译器里面默认的参数,一定要自己写)
#ifndef __MYSTRING__
#define __MYSTRING__
class String
{
...
};
String::function(...) ...
Global-function(...) ...
#endif
int main()
{
String s1(),
String s2("hello");
String s3(s1); //拷贝以s1作为初值
cout << s3 << endl;
s3 = s2; //赋值拷贝
cout << s3 << endl;
} //就又两个指针射向同一个地方,所以不用编译器给我们的特殊函数(赋值,拷贝)
Big Three, 三个特殊函数
class String
{
public:
String(const char* cstr = 0); //传进来一个指针,接口默认给0
String(const String& str); //拷贝构造:构造函数接收自己的东西,收到的参数就是自己这种类型
String& operator=(const String& str);
//拷贝赋值:操作符重载,赋值的动作也是它自己的东西
~String(); //析构函数:当它死亡的时候就会被调用
char* get_c_str() const { return m_data; }
private:
char* m_data; //让字符串拥有一根指针,再需要内存的时候才创建空间来放这个字符本身
//字符串里面的东西有大有小,设计为动态分配字符
}
ctor 和 dtor ( 构造函数和析构函数)
字符串:指针指着头,后面有一串,最后面有一个 0 这样的字符串(结束字符串)
1.不知道有多长,但最后面有一个结束符号(C和C++用)
2.后面没有结束符号,但前面有一个长度这样的整数(10,15,等等)(C++不用,有一种语言这样用)
//动态分配 ,所以class要死亡之前的那一刻,就会调用析构函数,把动态分配的内存释放掉
inline
String::String(const char* cstr = 0)
{
if (cstr) {
m_data = new char[strlen(cstr)+1]; //加 1,相当于结束符号
strcpy(m_data, cstr); //拷贝
}
else { // 未指定初值
m_data = new char[1]; //分配一个数组
*m_data = '\0'; //放结束符号
}
}
inline
String::~String()
{
delete[] m_data;
}
{
String s1(),
String s2("hello"); //传出值为6
String* p = new String("hello");
delete p;
}
class with pointer members 必须有 copy ctor(拷贝构造) 和 copy op=(拷贝赋值)
浅拷贝 (使用default copy ctor 后default op=) default (默认)
希望赋值以后,两端都有相同的内容,但是右端的内容不见了(还在,就是没有指针指向它了),内存泄漏
而左边有两个指针指向它,非常的危险,后期改变一个指针另一个都会被改动
copy ctor (拷贝构造函数)
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;
}
copy assignment operator (拷贝赋值函数)
深拷贝(一定要检测自我赋值)
我要把右边的东西赋值或者拷贝到左边上来,先把左边的东西清空,然后创建出和右边一样大的空间,再把右边赋值到左边身上
inline
String& String::operator=(const String& str) // str 就是 s2 , s2 是自己
{
if (this == &str) //检测自我赋值(看起来没有,但是可能会发生)
return *this; // s1 是传进来的引用(this pointer),this 就是 s1
//看他们两个的指针是不是指向同一个东西就知道是不是自己指向自己
delete[] m_data; //1.把 s1 作用在 s2 身上 ,所以 s2 就要把自己先杀掉
m_data = new char[ strlen(str.m_data) + 1 ]; //2.重新分配一个和 s1 一样大的空间
strcpy(m_data, str.m_data); //3.拷贝 s1 过来
return *this;
}
{
String s1("hello ");
String s2(s1);
s2 = s1; //把 s1 作用在 s2 身上 ,所以 s2 就要把自己先杀掉
}
一定要在 operator= 中检查是否 self assignment
要不然后面完删除,就无法访问hello的存在啦
output函数
char* get_c_str() const { return m_data; } 在类里面定义过
#include <iostream.h>
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
{
String s1("hello ");
cout << s1;
}
stack(栈) heap(堆)
stack(栈)( 离开时生命自然就消失):是存在于某一个作用域(scope)的一块内存空间(memory space)。
heap(堆)(system heap)(动态获得,就有责任去delete它):是指操作系统提供的一块(全局)内存空间。
1.stack objects 生命在作用域结束之前结束 (它的析构函数会被自动调用起来)
2.stack local objects 生命在作用域结束之前仍然存在,直到整个程序结束(静态对象) static Complex c2(1,2);
3.global objects 的生命在整个程序结束之后才结束(全局对象)
heap objects :P 所指的便是 heap object,其生命在它被 deleted 之后结束。
class Complex { … };
...
Complex c3(1,2); #global objects
{
Complex c1(1,2); #stack(栈) #stack objects
static Complex c2(1,2); #static local objects
Complex* p = new Complex(3); #heap(堆) #heap objects
delete p;
}
new :先分配 memory(内存)(operator new()), 再调用 ctor(构造函数)
构造函数pc->Complex::Complex(1,2); 等价于全名 Complex::Complex(pc,1,2);
如下分配内存:
delete :先调用 dtor(构造函数), 再释放 memory(内存)(operator delete())