文章目录
C语言是面向过程的,关注的是过程,分析出解决问题的步骤,然后通过函数的调用来解决问题,而C++关注的是对象,将一件事情拆分成不同的对象,通过对象之间的交互来达到目的。在C++中的类机制可以定义自己的类型,在该类型中包含了成员变量和成员函数,而这也正是实现C++相关特性的基石。
类
- 访问限定符及封装
-
访问限定符
- 类把对象的属性和操作方法结合到一块,通过访问权限有选择的将某些可以公开的接口展示出来,这样就封装了隐私接口,但是访问限定符只在编译时起作用,因为当数据映射到内存后,就没有什么访问限定符的区别了,而且限定符只对类外起作用,类内并无影响。
有时候面试还会问到C++中class和struct的区别,在这儿就是因为C++要兼容C,所以一方面struct可以定义结构体,与C并无区别,另一方面struct也可以用来定义类,只不过struct定义类的成员默认访问方式是public,而class是private,一般都用class定义类,因为这样便于区分你定义的是类还是结构体。
- 类把对象的属性和操作方法结合到一块,通过访问权限有选择的将某些可以公开的接口展示出来,这样就封装了隐私接口,但是访问限定符只在编译时起作用,因为当数据映射到内存后,就没有什么访问限定符的区别了,而且限定符只对类外起作用,类内并无影响。
-
封装
- 面向对象有三大特性:封装,继承和多态。在这儿我们只谈封装,封装的本质就是管理和保护,保护的就是我们不想让别人看到的代码和数据。保护私有数据,开放共有的成员函数用作访问数据的接口,以实现其管理的目的。
-
- 类对象模型
- 类对象的存储方式
- 在C++中的类的对象的存储方式是只保存成员变量,将成员函数存放到公共的代码段,这样就避免了每个对象都保存形同的代码多次,浪费空间。而类的大小实际就是该类中的“成员变量”之和,通过内存对齐的原则我们可以计算类的大小。在VS中经过验证后,注意空类的大小,编译器扔给了空类一个字节来标识这个特殊的类。
运行结果如图:#include <iostream> #include <stdlib.h> using namespace std; class C1 {public: void F1(){} private: int _d; }; class C2 {public: void F1(){} }; class C3 { }; int main() { cout << "C1: " << sizeof(C1) << " " << "C2: " << sizeof(C2) << " " << "C3: " << sizeof(C3) << endl; system("pause"); return 0; }
- 这里的内存对齐原则也就是C语言中的结构体对齐规则:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
- VS中默认的对齐数为8,gcc中的对齐数为4
- 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
这里相关的面试题:
① 结构体怎么对齐? 为什么要进行内存对齐?
对齐方式是根据C语言中的结构体对齐规则进行对齐,而之所以要进行内存对齐一方面是为了提高访问效率,内存访问方式的默认原则是一次按照某个类型的大小的整数倍来进行访问(根据硬件条件决定)除过数组外,基本上都不太大。也就是说CPU读取内存时是按块读取的。这样牺牲了空间就换取了时间;另一方面就是便于在不同的平台上进行移植。
②如何让结构体按照指定的对齐参数进行对齐
编译器中提供了#pragma pack(n)
来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量。
③如何知道结构体中某个成员相对于结构体起始位置的偏移量?
这个有两种方法:一种是用这个成员的地址减去结构体的地址即可得到偏移量的大小;另一种是利用宏offsetof()
来计算。相关代码如下
④什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景什么是大小端?
大小端的存在是为了多字节数据的存储,数据在底层的存储都是二进制存储的,那么就有数据的高低位和地址的高低存在对应关系,小端就是数据的低位存放在低地址,高位存放在高地址,而大端与此相反,数据高位放低地址,低位存放于高地址。测试大小端的方法有很多,代码如下。我在ARM嵌入式州做实验时很多次都要考虑到大小端,这种在不同类型的机器之间通过网络传送二进制数据时,考虑大小端就显得尤为重要了。
运行结果如图://偏移量 class Student {public: Student(string n, int a, char s) :name(n), age(a), sex(s){} void show(); string name; int age; char sex; }; void Student::show() { cout << "name:" << name << endl << "age:" << age << endl << "sex:" << sex << endl; } int main() { Student s1("Li Hua", 20, 'M'); s1.show(); //计算偏移量 //1.成员地址 - 类的地址 int offset_num = (int)&(s1.sex) - (int)&s1; cout << "offset_num: " << offset_num << endl; //宏offsetof() cout << "offset_num: " << offsetof(Student, sex) << endl; system("pause"); return 0; } //判断大小端 //int CheckSystem() //{ // union Check{ // int i; // char ch; // }uc; // uc.i = 1; // return uc.ch; //} //int CheckSystem() //{ // int i = 1; // if (*((char*)(&i)) == 1) // { // return 1; // } // return 0; //} typedef unsigned char byte; int CheckSystem() { unsigned int d = 0; unsigned int* p = &d; *((byte*)p) = 0xf; if (0xf == d) { return 1; } return 0; } int main() { int ret = CheckSystem(); if (ret == 1){ cout << "小端模式" << endl; } else cout << "大端模式" << endl; system("pause"); return 0; }
- 类对象的存储方式
- this指针
- C++函数体中的所有成员变量的操作都是通过this指针来完成的,this指针的类型是:类类型* const,其本质是一个成员函数第一个隐含的指针形参,编译器通过ecx寄存器自动传递。
this指针存在哪里?
this指针是作成员函数的形参的而形参是存放在栈上的。
this指针可以为空吗?
可以的。
- C++函数体中的所有成员变量的操作都是通过this指针来完成的,this指针的类型是:类类型* const,其本质是一个成员函数第一个隐含的指针形参,编译器通过ecx寄存器自动传递。