众所周知,C++是一门面向对象的语言,其关注的是对象,是将一件事情拆分成不同的对象,靠对象之间的交互完成的。就相当于,我们建一栋房子,有专门的打地基的,专门盖房子的,专门装潢的等等,而盖房子的东家只用和他们的负责人沟通就行,而至于这些负责人怎么把活干出来则是他们底下自己的事情,这样的形式便称作面向对象,如此将大大降低使用者的难度。这里我们将介绍C++中的一个特殊的结构——类。
类
还记得C语言中用struct修饰的结构体,在C++中是可以兼容使用的,其实它可以看作是类的一种,但是我们说的类的准确的定义是使用class为定义关键字,classname为类的名字,{}中为类的主体,类中的元素称为类的成员:类中的数据称为类的属性或者成员变量;类中的函数称为类的方法和成员函数。而struct中只能包括变量而不能包括函数方法。
class className
{
//类体,由成员变量和成员函数组成
};//类定义后加分号,否则会报错
类的定义
类的定义方式有两种:
1.声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处
理
- 放在.h文件中,类的定义放在.cpp文件中,与函数在头文件中声明,在.cpp中实现的过程相似。
为了增加程序的可读性,我们一般使用第二种方法。
访问权限限定字符
访问权限限定字符可以很好的这样理解,它是对于不同等级信息进行权限限制,就像是一个家庭,一些东西是大家都可以知道的,比如一家有几个人,都有谁,但是有一些东西是外人不能知道的,比如自己家的存款。因此,类比生活中社交的规则,在类中也对不同权限的信息进行了限制,使不同的对象在交互时只能访问到允许其访问到的内容,这便是C++进行封装的方式,即用类将对象的属性和方法结合到一块,让对象更加完善,,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符主要有三类:public(公有)、protected(保护)、private(私有)。
访问限定符 | 功能 |
---|---|
public | public修饰的成员变量在类外可以直接访问 |
protect | project修饰的成员变量在类中可以进行访问,在类外不能进行访问,其功能与private相似 |
private | private修饰的成员只能在类中和其类的友元进行访问,即被类中的成员函数进行访问 |
【说明】
* 访问权限作用域从访问权限符出现的位置开始直到下一个访问限定符出现为止
* class的默认权限为private,struct的默认权限为public(因为struct要兼容C)。
* 访问权限旨在编译时有用,当数据映射到内存后,没有任何访问权限限定符上的区别
例子
#include<iostream>
using namespace std;
class student {
public://公有,外部可以进行访问
char* school;
void ccout()
{
cout << name << sex << age << endl;
}
private://私有,外部不能进行访问,需要接口
char* name;
char* sex;
int age;
};
int main()
{
student st;
st.school = "college";//允许访问与修改
st.ccout();//允许访问
st.name = "xiaowang";//不能进行访问,会进行报错
st.sex = boy;//不能进行访问,会进行报错
st.age = 20;//不能进行访问,会进行报错
return 0;
}
运行截图如下:
【面试题1】
question:
C++struct 和class 的区别是什么?
answer:
C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中struct还可以用来定义类,和class是定义类是一样的,区别是struct的成员默认访问方式是public,class是struct的成员默认访问方式是private。
【面试题2】
question:
面向对象的三大特性是什么?
answer:
封装、继承、多态。
【面试题3】
question:
在类和对象阶段,我们只研究类的封装特性,那什么是封装呢?
answer:
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理:我们如何管理兵马俑呢?比如如果什么都不管,兵马俑就被随意破坏了。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,我们使用类数据和方法都封装到一下。不想给别人看到的,我们使用protected/private把成员封装起来。开放一些共有的成员函数对成员合理的访问。所以封装本质是一种管理。
类的实例化
我们所说的面向对象,是要建立起一个个对象,通过对象进行交互。而我们的类的主要作用就是建立对象,就好像是一个个做月饼的模型,其只是一个没有分配实际内存空间的虚拟概念,但是我们可以通过这个模型去生产好多好多的月饼,这里的月饼就是类的实例化,同时也拥有了占据实际物理空间的属性。
类的大小
一个类的大小,实际就是该类中”成员变量”之和,一般采用内存对齐的存储方法,在这里要特别注意一个特殊的情况,那便是空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。
对齐规则:
1.第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
关于内存对齐的具体方法及规则请参考字节对齐方法
【面试题】
question:
结构体怎么对齐? 为什么要进行内存对齐?
answer:
第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。我们知道 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。同时可以提升CPU的处理效率,是哪空间换取时间的做法。
question:
如何让结构体按照指定的对齐参数进行对齐
answer:
1.使用#pragma pack (n)来指定数据结构的对齐值,编辑器将按照n个字节对齐。
2.使用__ attribute __ aligned属性指定数据结构的对齐值,__ attribute((aligned (n))) 让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
3. 使用__ attribute__ ((packed)) 取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
question
如何知道结构体中某个成员相对于结构体起始位置的偏移量?
answer
使用地址相减,注意地址的类型需要一样
question
什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景?
answer
其方式与C语言相差无异,详见链接 C语言 大小端 存储
类的特殊成员——this指针
this在英文中的解释是“这,这个”,在C++中我们引用了这个单词的意思,从而使其在程序中具有表示当前对象的意思。C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的相关注意点
1.首先要知道,this在类中的类型是 类类型 * const
2每一个非静态成员函数中都有一个this指针
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
【面试题】
question
. this指针存在哪里
answer
属于函数的形参,一般放在函数栈上,但是由于其使用的太多,所以一些编译其器会进行优化将其放在ecx寄存器中
欢迎各位大佬批评指正。