目录
在面向对象编程中,类和对象是基本的概念。类是一个蓝图或模板,它定义了创建对象时所拥有的状态(属性)和行为(方法)。而对象是根据这个蓝图创建的实例,具有类定义的属性和方法。C++中也引入了这个概念。
类的定义
关键字class和struct
在C++中,创建自定义类型类一般使用关键字class。但是C语言中存在struct关键字以构建结构体,C++为了适配C语言,也将struct升级成了定义类的关键字。它俩唯一的区别就是struct创建的类中成员默认为public(公有),而class创建的类成员默认为private(私有)。
public和private
在类中,你可能想让其成员可以被外界访问或者不愿意,以保持类成员的安全性,所以C++中使用了以上两个访问限定符来确保每个成员变量或者函数的可以被访问的权限,访问限定符的限定范围是从一个访问限定符到下一个访问限定符,或者到类的末尾。例如
struct A
{
public:
int _c;//这两个可以在类外被访问
void print()
{
cout << c<< endl;
}
private:
//这个则不可以
int _a;
};
另外,C++规定在类内定义的成员函数默认是inline,而在类内声明但是在类外面定义的函数没有这个特点。
并且为了区分类的成员变量和普通的变量,一般规定类的成员变量加上特殊的标识,例如_year,_a等在前面添加一个下划线_。
类的实例化
被我们定义出来的类,只是一个蓝图,就像房间的设计图,只能看不能住,而真正想入住,必须先把房间装修出来。而对于类,这个过程是类的实例化。
对于类的实例化,有一个重要问题就是:构建出来的对象占多大内存呢?学习过C语言的同学们都知道C语言中的struct自定义对象有一套内存对齐规则,而在C++中,这个规则被沿用了下来。
内存对齐
元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小(通常它为4或8)来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始,这就是所谓的内存对齐。
由于之前大家都一定了解过内存对齐的详细了解,所以这里只是简单的给出内存对齐的规则。
-
第一个成员的起始地址为0,即与结构体变量的偏移量相同。
-
其他成员变量应该对齐到编译器默认的对齐数或该成员大小的较小者的整数倍的地址上。在Visual Studio中,默认对齐数为8,而在Linux中,默认为4。
-
结构体的总大小应该是最大对齐数的整数倍。
-
如果结构体中嵌套了其他结构体,那么嵌套的结构体应该对齐到其最大对齐数的整数倍的地址上。
实例化对象的内存大小
C语言的结构体中只有成员变量,所以算内存时只需要考虑变量,但是C++中类中还增加了成员函数,难道对象中还是只存着成员变量,而不存成员函数的地址吗?答案是:是的
每个对象中存储的成员变量不同,但是其调用函数时,调用的都是同一个函数,因此不必将函数存入对象中。事实上,成员函数和普通的非成员函数一样都存放在代码区。
struct A
{
public:
void print()
{
cout << _c << ' ' << _a << endl;
}
private:
int _a;
int _c;
};
int main()
{
A aa;
cout << sizeof(aa) << endl;//8
return 0;
}
C++空类构造出来的对象大小为多少呢?
class A
{};
int main()
{
A aa;
cout << sizeof(aa);//结果为1
return 0;
}
显然,上述程序的输出结果为1,这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址。
this指针
在上文的A类中,有函数print,但是其中没有写任何参数,我们实例化出来的对象是如何调用它的呢?
cout << aa.print() << endl;
C++中成员函数的调用和成员变量一样都是用符号:.
实际上,每一个成员函数都有一个隐藏的参数this,这个指针指向调用这个函数的对象。它在函数的参数列表中不能显式的写,在函数传参中也不能显式的传,但是在函数体内部可以使用。
struct A
{
public:
//void print(A* this)不能这样写
void print()
{
cout << (const A*)this << endl;//可以在函数内部使用
}
private:
int _a;
};
int main()
{
A aa;
//不能这么传
//aa.print(&A);
}
其中,this的类型是const A* ,说明不能改变其指向的对象。而且,this指针也不存在对象内部,它存储在栈中(有些编译器将其优化在了寄存器中)。