在前面的C++基础语法中没有讲解到内联函数,这里就介绍一下内联函数
内联函数:
概念 :以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。
从底层逻辑上来看,inline确实能做到像宏一样,并且没有宏的缺点。下面来看宏和内联函数的区别:
内联函数完美的解决了宏的缺点,并且具有宏的优点
宏的优缺点 :
优点: 1.增强代码的复用性。 2.提高性能。
缺点: 1.不方便调试宏。(因为预编译阶段进行了替换) 2.导致代码可读性差,可维护性差,容易误用。 3.没有类型安全的检查 。
我们可以通过内联函数的特点更好的了解内联函数:
1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会 用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运 行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建 议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不 是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性
随着C++逐步的学习,类和对象这个重要的东西就需要我们学习
面向过程和面向对象初步认识:
之前学习的C语言是面向过程的,而C++是面向对象的。
所谓的面向过程其实是把一件事分成多个步骤,然后每一步再封装函数去解决,然后main函数就调用即可,面向对象是把一件事分成多个对象,每个对象分别做些什么。这就是面向过程和面向对象的区别。
类:
在C语言中并没有类这一说法,C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如: 在数据结构中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数。下面是代码演示:
就拿之前写过的栈来说,在这个struct类里面实现也是比较方便的
但是C++中更加喜欢使用class这个关键字去命名,而class和struct的区别就在于class中如果不使用public这个关键字,类外的成员是没有办法访问到类里面的成员的,但是struct是公开的,都是可以访问的。这样就使class来定义类更加的优越。至于为什么还有使用struct这是在于C++是为了兼容C才这样做的。
类的定义:
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分 号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者 成员函数。
类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内 联函数处理。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
一般情况下,更期望采用第二种方式。因为在工程里面来说这么大型的一个项目,声明和定义分开能使观看者更快的学习。而且这样更加有逻辑性,每个函数做什么的都十分清晰。
下面我以数据结构栈作为例子来演示C++语法中要注意的地方。
这里我用工程上的写法来书写Stack
这里要注意print打印的时候不能使用范围for去解决,因为这里传过来的是一个数组首元素的地址,其类型是指针,与e的类型不对应,编译器会报错。
类的访问限定符及封装:
在上面的代码中也能看到private,以及public这两个关键字的使用,那么它们代表什么意思呢?访问限定符:C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选 择性的将其接口提供给外部的用户使用。
访问限定符注意点:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的
) 3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
封装:
首先我们要知道面向对象的三大特性:封装、继承、多态。
这里只介绍封装,之后再学习继承多态。
封装的概念:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。 封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用 户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日 常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。计算机用户并不需要过多的了解计算机内部的构建,会正常的使用即可。计 算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以 及键盘插孔等,让用户可以与计算机进行交互即可。
类的作用域:
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。这里的函数在.h文件中声明,对于类,我们需要使用这个操作符才能正确使用。
类的实例化:
用类类型创建对象的过程,称为类的实例化
1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没 有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个 类,来描述具体学生信息。
2.一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
3.类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设 计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象 才能实际存储数据,占用物理空间
下面这段代码就能很好的 理解:
就是我们只有图纸,是没有实际的空间的,更加不可能对其赋值。
类对象模型:
如何计算类对象的大小:这里计算类对象的大小其实与结构体计算大小相似。也是存在结构体的内存对齐。计算方法大同小异,这里主要说明处理空类的时候,类对象的大小为1个字节,这个字节是用来占位的。
这里我们再复习一下结构体内存对齐规则:
1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
其次我们在看看类对象的存储方式:
其中有3种设计方案:第一种就是成员和函数放在一起,虽然每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一 个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。所以方案1自然被抛弃。
下面来看看方案2:代码只保存一份,在对象中保存存放代码的地址,方案2是可行的,相比方案1大大的节省了空间,但这个指针确实多此一举,因为对于编译器来说在常量区(也就是操作系统中的代码段)找到类成员函数表很简单,根本没有必要多使用一个指针。所以也就有了方案3
方案3,目前在使用的版本:
this指针(难点)
先看看这段代码:
看看结果:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函 数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
这里就涉及了this指针的知识了,C++编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成。
事实上这里的参数应该写为:
this指针的特性 :
1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递
有两个题:
1. this指针存在哪里? 2. this指针可以为空吗?
对于第1题,我们可以通过this指针作为函数的形式参数就可以知道它是存在栈区的,
对于第二个 问题,这里又引申两个题目:
下面是解答:
通过这两题自然的解答了this指针能不能为空。答案是可以。