C++ 学习笔记 - Week2 【Boolan.com】
Week2 :String class
1 三个重要函数(Big Three)
含有指针的类,必须要有这三个函数
1.1 拷贝构造函数
- 拷贝构造函数用于使用已知的类对象来初始化新定义的类对象。
- 拷贝构造函数的名称必须和类名称一致。
- 拷贝构造函数的参数必须以引用传递。
1.1.1 函数定义
class String
{
public:
String(const String& str);
};
inline String&
String::(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
1.1.2 函数的调用(下面三种对象需要调用拷贝构造函数)
-
对象以值传递的方式传入函数
int main()
{
String s1("Hello");
String s2(s1);
} -
对象以值传递放方式从函数返回
-
对象需要通过另外一个对象进行初始化
1.1.3 浅拷贝和深拷贝
- 浅拷贝:系统自动调用默认拷贝构造函数;
- 深拷贝:自定义拷贝构造函数并调用;
注:在对含有指针成员的对象进行拷贝时,必须自定义拷贝构造函数并调用(即深拷贝),使拷贝后的对象指针成员右自己的内存空间,避免发生内存泄漏。如果只是采用默认拷贝构造函数(即浅拷贝),则可能发生两个指针指向同一块内存。
1.2 拷贝赋值函数
1.2.1 函数定义
class String
{
public:
String& operator = (const String& str);
};
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;
}
1.2.2 函数调用
int main()
{
String s1("Hello");
String s2 = s1;
}
1.3 析构函数
- 当对象结束其生命周期时,调用析构函数以释放内存。
- 一个类函数中只能右一个析构函数,如果未自定义,则系统自动调用默认析构函数。
1.3.1函数定义
class String
{
public:
~String();
}
inline String
::~String()
{
delete[] m_data;
}
1.3.2函数调用
- 析构函数不需要用户调用,只需要再类中定义,在对象生命周期结束时,系统会自动调用析构函数。
1.4 为什么含有指针的类要用拷贝构造或拷贝赋值?
如果再含有指针的对象中不使用拷贝构造或拷贝赋值,则新建立的对象会和已存在的对象,同时指向一个内存地址,发生内存泄漏。
1.5 构造函数、析构函数、赋值函数是每个类最基本的的函数。每个类只有一个析构函数和一个赋值函数。但是有很多构造函数(一个为复制构造函数,其他为普通构造函数。对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数.
2 Class String的构造函数和析构函数
-
含有指针的类函数,必须使用动态分配内存,使用动态分配后必须使用析构函数释放内存。
inline
String::String(const char* cstr = 0)
{
if(cstr) // 判断传入的参数cstr是否为0;
{ // 如果不为0,则创建一个新到对象,其长度为传入的参数的长度+1;
m_data = new char[strlen(cstr) + 1];
strcpy(m_data,cstr); // 将传入的参数复制到新创建的对象中;
}
else
{
m_data = new char[1]; // 如果传入的参数为0,则新创建一个对象,长度为1,并给新创建的对象赋值为结束符‘\0’;
*m_data = '\0';
}
}//strlen()函数测量的字符串的长度不包含结束符(\0);inline
String::~String()
{
delete[] m_data;
}
3 堆、栈与内存管理
3.1 Stack & heap
- Stack是存在于某作用域(scope)的一块内存空间。例如当你调用函数,函数本身即会形成一个stack用来放置他锁接受的参数和返回的地址。
- Heap是指操作系统提供的一块global内存空间,程序可动态分配从中获得若干区块。
3.1.1 Static object的生命期
-
以下列代码为例,c2即是static object,其生命期再作用域结束之后任然存在,直到整个程序结束,程序结束时会自动调用析构函数释放内存。
class complex{...};
...
{
static complex c21,2);
}
3.1.2 global objects的生命期
-
c3即global object,其生命再整个程序结束之后才结束。
class Complex{...};
...
Complex c3(1,2);int main()
{
...
}
3.1.3 heap objects的生命期
-
p 即是heap object,其生命再被delete之后才结束。
-
如果在指针后不delete,则会发生内存泄漏。
class Complex {...};
...
{
complex* p = new Complex;
...
delete p;
}
3.1.4 new函数的实际运行步骤
先分配内存(operator mew()),再调用构造函数
3.1.5 delete函数的实际运行步骤
先调用析构函数,再释放内存(operator delete())
3.1.6 动态分配所得的内存块(VC编译器下)
-
调试模式下
-
非调试模式下
备注:
【1】new 用于分配一块内存
【2】strlen(str); 测量字符串str的长度
【3】strcpy(str1,str2); 将字符串str2的值拷贝到str1中
【4】delete[] str;释放内存
【5】参考:http://blog.csdn.net/lwbeyond/article/details/6202256

浙公网安备 33010602011771号