首页 > 编程 > C++ > 正文

[BoolanC++微专业] Week2笔记

2019-11-11 07:36:59
字体:
来源:转载
供稿:网友

一、类的分类:

类在包含内容上大致可以分为以下两种形式:

class with pointer members ——string类 在构造这种类的时候,拷贝构造函数,赋值与比较运算符重载,全部都要特殊考虑,很可能出现违背开发者意图的行为。

class without point members ——complex类

二、三大特殊函数:

String类的实现

三大特殊函数即为:

拷贝构造函数: String(const String &str);

赋值运算符重载:

String& Operator=(const String&str);

析构函数:

~String();

在class with pointer members里必须存在拷贝构造函数三大函数。如果使用编译器合成的默认函数的话会出现俗称的“浅拷贝”和“内存泄漏”。 举例说明:

String a("Hello"); String b(a);

若使用的是一个默认的拷贝构造函数,则会使两个类中的m_data指针指向同一个”Hello”,那么假设析构函数正常运行,a的lifetime终止的时候,会释放a->m_data指向的”Hello”内存块。但是b的lifetime并没有终止,那么b->m_data会指向一个已经被释放的内存块,形成空悬指针,万一在程序的某处使用b,则会出现内存错误。赋值函数同理。 我们都知道一个指针有new必有delete,类在lifetime结束的时候,会将所有的在栈上占用的内存还给计算机,但是class with pointer里指针指向的内存全部来自堆上,如果不主动释放,那么此内存块将会一直被占用,在小程序中可以并不会出现什么大的问题,但是如果一个类有足够多个实例,那么将会将堆中的内存全部占用,造成堆中无内存可用的状况,就是俗称的“内存泄漏”。

拷贝构造函数让我们发现一个很有意思的特性:

inline Stirng::String(const String &str) { m_data = new char[strlen(str.m_data)+1]; strcpy(m_data,str.m_data) } 有同学

可能发现了 str和我本身这个类的实例,并不是友元,我为什么可以访问它的PRivate成员? 因为从同一个类中衍生出的实例互为friend。

在拷贝赋值函数中,有一点非常重要,即是要检测rhs是否是类本身,如果是,可以节省拷贝的时间和空间。如果不是,不检查的话,可能产生不确定行为,如图所示:

三、所谓栈和堆: Stack,是存在某作用域的一块内存空间,随着你所使用的函数的调用和结束产生以及返回,在函数本体内声明的任何变量,所使用的内存块都取自栈(stack)。 这里写图片描述 heap,是指操作系统提供的一块global内存空间,程序可以动态分配从中获取若干区块。这里写图片描述 具体区别可以参照StackOverFlow 问题:What and where are the stack and heap? 栈中的变量,lifetime与包含它的函数同时存在,在函数声明周期结束时,它的生命周期同时存在。 stack object和global object的lifetime在作用域结束后依然存在,直到整个程序结束时才会结束。 heap object却不同,在new开始后,如果作用域结束,指针的lifetime结束,但是其指向的内存却一直没有被释放,一直到程序结束,但是我们已经没有能力再次通过指向它的指针对其进行delete等其他操作,这就造成了内存泄漏。

分配在heap上的数据,有new必有delete。 接下来看一下new和delete的实现。

void *operator new(size_t); //allocate an objectvoid *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an arrayvoid *operator delete[](void *); //free an array

 c++中new和delete函数是不允许重载的,属于特殊函数,虽然函数声明有点不太一样,但是同学们- - 这个真的是函数。new和delete通常需要成对出现,因为其底层实现的原理有所不同,在此不进行讨论,其底层最终都是调用c语言的malloc free来进行内存的分配与释放,但是在delete时 如果delete一个类的时候,会先调用类的析构函数然后再释放内存,而free并不会(这不废话么- - 有free的时候c++还不直到在哪里呢)。new先分配memory,再调用构造函数。   四、动态内存分配所得的array: 这里写图片描述

这是侯捷老师在debug模式下截取的一个动态分配所得的array内存块。一个complex内有2个double数据。其中关于debugger header的部分本人水平所限,理解有限。。。就扔一张图,不献丑了。

五、关于static的补充:     类的静态成员存在于任何对象之外,对象不包含任何与静态成员有关的数据,类似的,静态成员函数,也不与任何对象绑定在一起,他们不包含this指针,作为结果,静态成员函数不能声明成const的,而且我们也不可以在static函数内使用this指针。我们可以使用作用域运算符直接访问静态成员:  

   double r;    r = complex::static_function();

成员函数不通过作用域运算符可以直接访问静态成员。     定义静态成员:     我们可以在类的内部或者外部定义静态成员函数,在类的外部定义静态成员时,不能重复static关键字。     因为静态数据成员不属于类的任何一个对象,所以他们不是创建类的对象时被定义的。这意味着他们不是由类的构造函数初始化的。而且一般来说,我们不能在类的内部初始化静态成员。相反的,必须在类的外部定义和初始化每个静态成员。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表