文章目录
- 类的概念
- struct
- class
- class和struct的区别是什么呢?
- 类中成员函数的两种定义方式
- 声明和定义都在类中
- 声明和定义分离
- 类的访问控制和封装
- 类的封装特性
- 类的大小
- 结构体内存对齐规则
- 类的存储方式
- this指针
类的概念
在C++中,类可以说是最重要的东西,因为C++一开始的定位就是c with class,也正是因为引入了类,才让c++从c的面向过程思想转变为面向对象的思想,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
C++有两种能够创建类的方法
struct
第一种是struct,也就是从C继承而来的,在C语言中,struct可以用来创建结构体,但是结构体中只能保存变量,而不能保存函数,在C++中,struct不仅可以用来创建类,还保留了C语言中结构体的用法
struct Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
class
class是C++中定义类的关键字,他的用法和struct类似。
在类中,类的元素被称为类的成员,类中的数据被称为成员变量或者类的属性,类中的函数被称为类的方法或者成员函数
class Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
class和struct的区别是什么呢?
- C++兼容C语言,所以struct不仅可以定义类,还可以当成结构体使用。
- 访问限定不同,struct的成员默认访问方式为public,class为private, 并且struct默认继承为public继承,,class为private
类中成员函数的两种定义方式
声明和定义都在类中
如果成员函数在类内部定义,则其是隐式的inline函数。
class Date
{void PrintDate(){cout << _year << '-' << _month << '-' << _day << endl;}int _year;int _month;int _day;
};
声明和定义分离
在类内声明,在类外定义,这样能让类更加简洁,不那么臃肿,通常都是使用这种方式。
class Date
{void PrintDate();int _year;int _month;int _day;
};//需要通过作用域运算符指定这个方法属于Date类
void Date::PrintDate()
{cout << _year << '-' << _month << '-' << _day << endl;
}
类的访问控制和封装
访问限定符有三种,分别是public,private,protected
- public修饰的成员在类外可以直接被访问。
- protected和private修饰的成员在类外不能直接被访问,protected修饰内容派生类可以访问,private不可以。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
类的封装特性
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。
封装本质上是一种管理:我们如何管理兵马俑呢?如果什么都不管,兵马俑可能会被随意破坏。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的并不是全封装起来,不让别人看。所以我们开放了售票通道,可以通过买票突破封装、在合理的监管机制下进去参观。类也是一样,我们将类数据和方法都封装一下。不想给别人肆意修改,我们使用protected/private把成员封装起来。同时开放一些共有的成员函数对成员合理的访问机制。所以封装本质是一种管理。
一般对于对象的属性,我们不希望别人对它进行修改,所以会用private将其封装,但是我们又需要给别人提供获取它的接口,所以会对方法和接口给与public属性。
类的大小
class Date
{
public:void PrintDate();private:char a;int _year;int _month;int _day;
};
我们可以看到,类中有成员函数,有成员变量,那么类的大小是如何计算的呢?
int main()
{Date d;cout << sizeof(d) << endl;
}
这里用sizeof查看大小,发现只有成员变量的大小(三个int类型一个char类型,3*4+1=13,这里的16是因为内存对齐偏移了3个字节),而没有成员函数的大小
结构体内存对齐规则
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小 两者中的较小值。(如int大小为4,VS中默认的对齐数为8,取4) - 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小,本例中所有变量类型最大者是int,大小为4,与默认对齐参数取最小,则为4)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
类的存储方式
为什么会只保存了成员变量呢?这里就要引申出类的存储方式。
对于上面的类,如果我们将类实例化为多个对象,实际上这些对象不同的地方只有它的属性,它们调用的方法都是完全相同的, 为了不重复保存这些代码,C++只保存了属性,而将方法放到了公共的代码段中,来避免了空间的浪费。
同时,如果一个类中什么都没有,编译器会给这个类一个字节,来唯一标识这个类。
总结:类的大小就是成员变量之和(需要考虑内存对齐)
this指针
void Date::PrintDate()
{cout << _year << '-' << _month << '-' << _day << endl;
}
对于类中的成员函数,我们并没有将对象的属性传给他,那么他是如何能够获取这些属性的呢?
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数(this指针),让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有对成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
其实类的成员函数中隐含了一个this指针,所以在编译器处理的时候,上面的代码等价于
void Date::PrintDate(Date * const this)
{cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
}
- 这个this指针的类型是类类型 * const this,之所以是顶层const的原因是因为this指针唯一指向对象地址。
- this指针只能在成员函数中使用
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针,this指针存在寄存器中。
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递,
同时,如果this指针为空时,成员函数使用到了this指针,则会崩溃,如果没使用,则正常运行