目录
1.类的引入
在C语言中,描述一个事物的属性时通常使用结构体,结构体方便我们统一调度这些变量属性.但是C语言结构体中是不能放函数的。我们只好将功能函数定义在结构体外,在遇见大项目的时候,各种函数名非常容易记混,会增加出错的风险。所以在C++中,引入类概念,就很好的解决了问题。它规定结构体中是可以使用定义变量和函数的,这极大的提升了我们的代码幸福感。
那么,类应该如何定义呢?
c++在定义时有两种方式,第一种是函数声明放在类外,另一种是函数声明和定义都放在类体中,这里推荐第一种,声明定义分离能增加代码规范性和可读性。
C语言中仿类定义 struct student{ char name[20]; int age; char sex; char num[20]; }; void exam(){ printf("%s",name); } void homework(){ printf("%d",name); } c++类的定义 class student{ //此处为了方便演示,将声明和定义放在类中 private: char name[20]; int age; char sex; char num[20]; public: void exam(){ cout<<name<<endl; } void homework(){ cout<<sex; } }; //这个封号不能省略哦
c++将数据与方法封装在一起,逻辑明显比C语言清晰,内聚性更高。在大型工程中,这个优势会更加明显。
此处体现了面向对象三大特性(继承、封装、多态)的封装特性,将数据与与操作数据的方法有机结合,对外暴露出公开接口以供使用。
2.类成员的访问
因为函数内聚性,大多数情况我使用的是类内直接操作变量,我们可以直接像访问普通变量一样访问,在类外进行访问时有以下方法。
1.在创建对象以后可以使用点号来访问成员变量和成员函数,和结构体访问他的成员类似
class student{ char name[20]; int age; char sex; char num[20]; public: void exam(){ cout<<name<<endl; } void homework(){ cout<<sex; } }; int main(){ student stu; stu.name="小米"; stu.age=12; stu.num="128"; return 0; }
2.使用new关键字在堆上创建对象
int main(){ //new操作符创建是匿名对象,需要一个对象指针来访问它 student *stu=new student; *name="小米"; *age=12; *num="128"; return 0; }
3.访问限定符
在上边代码中,提到了public和private关键字,它们都是访问限定符。访问限定符可以对数据访问设置权限。比如在类中的sex变量,你不想让类外的方法访问到,就可以使用private关键字,增加了数据安全性。
常见访问限定符分为四种,default,public,private,protect。
使用方法:在变量之前加上访问限定符,访问作用范围是到下一个访问限定符之前
default:类定义中若未设置访问限定符,默认是default。
public:既可以在类中引用和修改数据,也可以在类外引用和修改
private:类中的private成员只能被该类的成员访问。
protected:类的protected数据成员只能被类成员函数、子类函数访问,不能被其他任何访问。
private和protected区别:它们都能被类成员访问,但是前者只能不能被任何除类成员外的函数访问,后者可以在继承时被子类访问。
举个栗子:private
#include<iostream> using namespace std; class student{ private: string name; int age; char sex; string num; void exam(){ cout<<name<<endl; } }; int main(){ student stu; stu.name="小米"; stu.age=12; stu.num="41909310221"; return 0; }
错误:name是私有成员,不能访问
4.类和对象的区别
类是一套模具,里边定义了各种标准和参数,用这个模具可以实例化出许多对象。对象是实实在在存在的,在对象身上可以附加各种数值属性,然后操作对象完成动作。类里边不附加数值,而对象实例化之后添加属性。
5.类对象模型
5.1 类的大小
#include<iostream> using namespace std; class student{ public: int age; char sex; }; class student1{ int age; char sex; void exam(){ int a=0; } }; int main(){ student stu; student1 stu1; stu.age=12; stu1.age=12; cout<<sizeof(stu)<<endl; cout<<sizeof(stu1)<<endl; return 0; }
结论:1.类的对象大小遵循内存对齐原则
2.与类中是否存在函数以及静态成员变量无关
3.空类实例化大小为1
解释:
内存对齐原则方便操作系统读取
静态成员变量和成员函数并不是每个对象都复制一份,而是内存中只有一份,所有对象共享使用,这样就能避免每个对象复制一份,节省空间。静态成员变量放在栈上,函数放在代码段。
对象在调用时会进行函数压栈,假如类的大小为0,那么下一个函数就会占用这个位置,系统调用时在这个地址找到的就是第二个函数。
5.2结构体内存对齐原则
在结构体中内存分布并不是像顺序表那样连续排列,而是遵守内存对齐规则进行排列,这样能提高读取效率。
对齐规则:
1.第一个成员在结构体变量偏移量为0 的地址处。(设置第一个变量时用的)
2.结构体总大小为最大对齐数的整数倍。(设置最后一个变量时用的)
3.其他成员变量要对齐到对齐数的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小中的较小值。vs中默认值是8 Linux默认值为4.(设置中间变量时用的)
4. 如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。(设置成员有结构体时用的)
举个栗子....
#include<stdio.h> int main(){ typedef struct A{ int a; double b; short c; }size; printf("%ld\n",sizeof(size)); return 0; }
解释:a为int型,占4个字节,根据规则1,占用0~3地址
b为double型,占8个字节,根据规则3,占用8~15地址
c为short型,占2个字节,根据规则1,占用16~18地址
根据 规则2,结构体总大小为最大对齐数的整数倍,最大对齐数位为8,目前排到18,下一个整数倍是24,所以结构体大小为24
6.this指针
6.1this引入
当我们实例化一个对象之后,我们势必会通过对象调用类中的方法。比如以下代码
#include<iostream> using namespace std; class This{ public: void a(){ cout<<"调用我!"<<endl; } }; int main(){ This d1; d1.a(); return 0; }
问题:前边我们讲到对象里边只有变量,函数是放在代码段的,那么为什么d1未给参数就可以调用不属于自己的成员函数a呢?
这是因为在调用成员函数时,编译器会偷偷在函数参数列表增加一个this指针,哪个对象调用成员函数,这个this指针就指向谁。实际编译执行的代码应该是下边代码,这个this指针就指向d1对象。
void a(*this){ cout<<"调用我!"<<endl; }
6.2验证*this
验证this存在性
既然编译器后台调用的是this指针,那就意味着我们也可以显式调用,this指针类型为
类类型* const
#include<iostream> using namespace std; class This{ public: int _a=10; void A(/*This* this */){ cout<<(*this)._a<<endl; } }; int main(){ This d1; d1.A(); return 0; }
结论:我们参数列表中没有this,但是我们显式调用,this指针确实存在。
验证this指向d1对象
#include<iostream> using namespace std; class This{ public: int _a=10; void A(/*This* this */){ cout<<"this存的地址:"<<this<<endl; //打印this存的地址 This* const & _this=this; cout<<"存this的地址:"<<&_this<<endl; //this地址不允许直接打印,这里给this取别名打印 } }; int main(){ This d1; d1.A(); cout<<"d1的地址"<<&d1<<endl; //打印d1地址 return 0; }
结论:this存的地址就是d1对象地址,题目可证
总结:今天我们认识了类的定义,类成员访问,访问限定符,类对象模型,以及this指针。
所以,接下来的知识,明天再来探索吧。