Classes的两个经典分类:
- Class without pointer member(s)
complex - Class with pointer member(s)
string
String class
String s3(s1); // s3初值为s1,拷贝
s3=s2; //拷贝,引发与刚才不一样的函数
编译器给的默认的可以用,就用
但是这里不可以用,用的话会传指针,不是真的复制。
Big Three,三个特殊函数
动态分配,而不是在类里面放一个数组。
所以是一个指针。
不能用编译器的,所以要写出来。
234是比 Complex 新增加的,Complex 里不曾出现
2.String 接受的是自己这种东西,所以叫拷贝构造
3.复制的是自己这种东西,拷贝赋值
所有的类的定义,对 big3都不会加 const,因为必然要修改
ctor 和 dtor(构造函数和析构函数)
2-1
要想到最后有一个结束符'\0'
最后一步,清理的函数。
动态内存,所以要清理。否则会造成溢出。
class 里有指针,多半要使用动态分配,有动态分配,就要在退出时释放这部分内存。
class with pointer members 必须有 copy ctor 和 copy op=(赋值assignment operator)
如果使用编译器默认,则在赋值时会拷贝指针,而不是数据。
看起来像是内容相同,但是 a 和 b 指向了相同的数据,改变 a 同时也会改变 b。
copy ctor(拷贝构造函数)
2-2
alias 叠名
是一件危险的事
copy assignment operator(拷贝赋值函数)
2-3
赋值过程:
先把左边清空,然后建立一个和右边一样的空间,再把右边拷贝到左边。
首先,检测是不是自我赋值,不只是同名赋值,很可能是指针指向同一个类。如果相同,就直接返回指针。
然后再拷贝:
- 删掉
- 建立空间
-
拷贝内容
如果不检测自我赋值,会发生什么:
- 删掉了唯一值
- 无法建立空间,因为检测不出长度,出错
output 函数
要写操作符重载
有什么可以直接丢给 cout呢?有,写在 inline 里面的 get_c_str()
所谓 stack(栈),所谓 heap(堆)
放在栈里的内容,在离开作用域后会自动删除
放在堆里的内容,需要自己手动删除
stack objects 的生命期
auto object
static local objects 的生命周期
其生命在作用域结束之后仍然存在,直到整个程序结束。
globe object 的生命周期
heap objects 的生命周期
new:先分配 memory,再调用 ctor
绝大多数的 new 被分解为三个部分:
- 调用
operator new
,其内部调用malloc(n)
,分配内存的函数 - 转换类型
- 调用构造函数,全名是
Complex::Complex(this,i,j)
delete:先调用 dtor,再释放 memory
- 执行析构函数
~String(ps)
,删除字符串占用的空间 - 释放内存
operator delete(ps)
,调用free(ps)
动态分配所得的内存块(memory block),in VC
VC调试中Complex占用的,复数占用8B,调试占用32+4,cookie,42,共52B,总共占用64B(必须是16的倍数)所以有34B 的填充物
Release Mode,8B,cookie 占用42,共16B
cookie 内容00000041,4代表416,1代表已分配
VC调试中String 占用的,指针占用4B,调试占用32+4,cookie,42,共48B,没有填充物
Release Mode,4B,cookie 占用42,共12B,总共占用16B,其中4B为填充物。
动态分配所得的 array
2-1
Complex* p=new Complex[3];
array new 一定要搭配 array delete
不正确的用法少了[],少了[]并不会发生array占用的内存泄漏。
有[]调用3次析构函数
没有[]调用1次析构函数,有两个并不回收
编程实例
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;
};
inline
String::String(const char* cstr=0)
{
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;
}
inline
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1];
strcpy(m_data,str.m_data);
}
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;
}
进一步补充:static
静态成员函数没有 this pointer,所以不能使用 this pointer 的功能。
静态的变量要在外面赋初值
进一步补充:把 ctors 放在 private 区
只用一次的函数。构造函数放在 private 中,
只有第一次调用的时候才会定义,这以后就会存在,并且只会有一份。
进一步补充:cout
进一步补充:class template,类模板
进一步补充:function template,函数模板
把责任分开,很有必要
这种东西叫做算法
进一步补充:namespace
- using directive
using namespace std;
- using declaration
using std:cont;
- 用全名
更多细节与深入
- operator tyge() const;
- explicit complex(…):initialization list{}
- pointer-like object
- function-like object
- Namespace
- template specialization
- Standard Library
- variadic template
- move ctor