1、面向对象与面向过程
1.1 C语言是面向过程的
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
1.2 C++是面向对象的
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完
成。
2、类
2.1 解释:C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
2.2 类中可以定义
变量(成员变量,成员属性)
函数(成员函数,方法)
struct:为了兼容C,用struct定义的对象,可以直接通过对象访问成员,因此其成员默认为public
class :默认为private
3、定义
3.1 声明
- 函数声明了可以不定义
- 变量是声明还是定义?就看是否开辟了空间,在类的实例化的时候是开辟空间
- 全局变量的声明与定义
- 头文件定义全局变量
- 全局变量:如果在头文件里面定义了全局变量,当预处理的时候会将同名全局变量直接嵌入到cpp文件头部,他们会在头文件里面有一个全局变量,其变量就会在编译的时候加在每个文件的字符表里,具体可以看往期文章,在多个cpp文件有一个同名的全局变量,此时符号表里面有两个或多个同名的变量,就会报错重定义
- extern:如果想在头文件里面定义全局变量,就使用extern修饰声明,这样就不会在每个文件里面创建同一个变量,就不会导致重定义了,代表是按c的规则去创建,在cpp文件里面去定义
- static:放在静态区中,当有一个CPP文件使用了头文件定义的static变量之后,该变量就只会在该文件中能有效使用
- 区别:
- 他们的链接属性不一样,extern、全局变量在全部文件都可以使用,每个同名变量都是指代的同一个,会放进符号表
- static只会在当前文件可见,当前文件即在定义的文件内使用,不会放进符号表,其他文件就看不到该变量,不同文件的相同变量都是不同的
- 变量命名方式
- 成员函数命名规范:
- 单词和单词之间首字母大写间隔 (驼峰法)GetYear
- 单词全部小写,单词之间_分割 (分割法)get_year
- 驼峰法原则:
- 函数名、类名等所有单词首字母大写 DateMgr
- 变量首字母小写,后面单词首字母大写 dateMgr
- 成员变量,首单词前面加_ _datemgr
- 成员函数命名规范:
3.2 定义
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分
号不能省略。
class className
{
// 类体:由成员函数和成员变量组成
// 在声明变量的时候最好用驼峰式
}; // 一定要注意后面的分号
定义方式一
类中定义,如果函数满足内联函数的要求将会被当成内联函数
定义方式二
头文件中声明,CPP文件中定义,定义时函数名前面需要加::,eg:void Stack::Init();
4、类的访问限定符及封装
4.1 public
类外可以直接被访问
4.2 protect
类外不能直接访问,需要提供接口
4.3 private
类外不能直接访问,需要提供接口
4.4 struct与class
struct:默认public,兼容C,C中可以直接访问类成员
class:默认private
区别:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
4.5 访问权限
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 } 结束
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
4.6 面向对象3大特性
封装,继承,多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。将所要实现的功能进行封装成函数,仅对外提供函数调用接口,不像使用者展示内部实现细节,保护数据,方便管理类。这种函数会受到权限的限制
5、类的作用域
类定义了一个新的作用域,类在创建对象的时候,所有成员都在类的作用域中,这个作用域通常是在栈帧中,如果由static修饰,则是在静态区中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
域会影响编译器的搜索规则,先从局部域开始查找,然后从全局域查找,然后从命名空间查找
生命周期:数据的生命周期,跟存储的位置有关系,栈区,堆区,静态区,常量区。在不同的域里面可以创建同样的函数
5、类的作用域
类定义了一个新的作用域,类在创建对象的时候,所有成员都在类的作用域中,这个作用域通常是在栈帧中,如果由static修饰,则是在静态区中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
域会影响编译器的搜索规则,先从局部域开始查找,然后从全局域查找,然后从命名空间查找
生命周期
数据的生命周期,跟存储的位置有关系,栈区,堆区,静态区,常量区。在不同的域里面可以创建同样的函数
6、类的实例化
类是对对象的描述,是通过实体属性所抽象出来的,定义一个类,并没有分配空间,实例化出来的对象是占用实际空间的,具体占用空间的是成员变量在占用而不是函数占用空间。
解释:定义类就好比一幢楼的图纸,但并没有实际去修建,而实例化则是去修建,调用封装好的函数则是在怎么修建
7、类对象的大小计算
- 类对象的大小计算只计算成员变量
- 存储方式
- 每个对象都保存独立的变量与独立的函数
- 缺点:实例化的每个对象的成员变量是不同的,每个对象会单独开辟一片空间存储不同的变量和相同的函数,但是调用的函数本质是同一个,因此设计方法一会造成空间浪费,不可行
- 每个对象保存独立的变量与函数的首地址
- 实现方式:将函数建立成虚表,每个实例化对象去指针指向虚表中的函数即可
- 缺点:这样的方法,所有的函数只需要去这表里面去找就可以了,只需要存储一份就可以了,不会存储多份,但这里没有采用
- 只存储变量,不存储函数,函数单独存储在代码区
- 编译链接时,不会在对象里面去找函数,而是根据函数名去类的相关公共代码区去找函数的地址,然后call 函数地址
- 每个对象都保存独立的变量与独立的函数
- 大小计算
-
遵循c语言的内存对其规则空类域和无成员变量的类域的大小是1,如果太小,那就不能区分他们的地址,因此会给1个字节用来占位,表示这个类的实例化对象存在
-
内存对其规则:链接:http://t.csdn.cn/P3h6n
-
第一个成员在与结构体偏移量为0的地址处。
-
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8 -
结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
-
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
-
-
8、类成员函数的this指针
this指针是默认加在成员函数声明的第一个变量前,实例化在调用成员函数的时候,也会默认在第一个变量前加一个实例化的地址。注意:如果类里面有函数为static,那么该函数将没有this指针
在实际调用的时候,我们不能把这两项添加进去,这是编译器自动添加的,在实参和形参位置,不能显示传递和接收this指针,但是可以在成员函数的内部使用this指针,this指针存储在栈区,因为this是形参
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用,static类型函数没有this指针
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
- 在进行运算符重载的时候,this指针为运算符重载函数的左参数
注意:
this指针存储在栈帧里面
this指针可以为空,为空的时候可以直接调用部分函数,原因是函数与变量是单独存储的,this指针可以为空指针,调用函数不会受该this影响,A a = nullptr; a.print();
实际:a.print(&a);