一、实例化的概念
用类类型在屋里内存中创建对象的过程,称为类实例化出对象
类是对对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。
一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
举个例子:类实例化出对象就像现实中使用建筑设计图建造出房子,类就是设计图,设计图并不是实体化的建筑,只有当使用设计图修建出房子后,这个房子才能住人。类同理,它不能存储数据,实例化出的对象分配物理内存数据。
class Data
{
public:
void Print(int year, int month, int day)
{
cout << year << "/" << month << "/" << day << endl;
}
private:
//此时并没有真正的开辟空间
int _year;
int _month;
int _day;
};
//在这里会输出12个字节的大小,原因是此时编译器在编译期间需要确定占据的空间大小
//并不是真正的开辟空间,只有当对对象进行实例化的时候才会开辟相对应的空间
void Print()
{
cout << "sizeof(d): " << sizeof(Data) << endl;
}
int main()
{
Print();
//在这里创建了Data d 这样的一个对象后Data类中的成员变量才是真正的开辟了空间
Data d;
cout << "sizeof(d): " << sizeof(d) << endl;
d.Print(2024, 9, 14);
return 0;
}
在 C++ 中,即使没有显式地对类的成员变量进行 “实例化”(初始化),但当定义类的对象时,依然会为对象分配内存空间以容纳类的成员变量。
在上述代码中,虽然没有显式地创建Data
类的对象,但在Print
函数中使用sizeof(Data)
来获取Data
类的大小,这并不意味着真正地为对象开辟了空间,只是在编译期确定这个类在内存中需要占据的空间大小。
类的大小取决于其成员变量的类型和布局,即使成员变量没有被初始化,它们在类的布局中仍然占据一定的空间,所以sizeof(Data)
会返回一个非零的值,这并不代表实际开辟了对象的存储空间。只有在使用Data d;
这样的语句创建类的对象时,才真正在运行时为对象分配内存空间。
二、对象大小
类实例化出的每个对象都有独立的数据空间,所以对象中包含成员变量。
函数在被编译后是一段指令,对象中没办法存储,所以一般情况下类的对象本身不直接包含函数指针。
同时C++中规定,类实例化的对象也要遵守内存对齐的规则。
class A
{
public:
private:
char c;
int i;
};
class B{};
class C{};
int main()
{
A a;
B b;
C c;
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;
}
上述代码中A类根据内存对齐的规则应当是8个字节的数据,输出的是没有问题的;
但是B类 和 C类的1个字节是什么原因呢?
原因如下:给定一个字符作为占位符的存在,否则无法证明类的存在,这个字节的空间不存储任何数据,仅作为占位符的作用存在。
三、this指针
在C++中存在一个隐藏的this指针,他的作用就是在编译器对代码进行编译后,在函数的参数中默认调用这个指针,用于解决函数中对不同对象的区分。
我们来看下面这段代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
// 编译报错:error C2106: “=”: 左操作数必须为左值
// this = nullptr;
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
// Date类实例化出对象d1和d2
Date d1;
Date d2;
// d1.Init(&d1, 2024, 3, 31);
d1.Init(2024, 3, 31);
d1.Print();
d2.Init(2024, 7, 5);
d2.Print();
return 0;
}
在上述代码中Data类又Init和Print两个成员函数,函数体中的没有关于不同对象的区分,此时就是使用了this指针来解决的这个问题。
编译器将其编译后,类的成员函数默认会在形参的第一个位置,增加一个当前类类型得到指针,叫做this指针。
也就是说其原型是Data(Data* const this,int year,int month,int day);
类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给成员的赋值。
注意!C++中规定不能在实参和形参的的位置显示的写this指针,但是可以在函数体内显示使用this指针。
this指针的存放位置:内存中的栈区。
原因:this指针本身是函数的一个参数,而创建函数就会创建栈,当函数体销毁时,栈区也随之销毁,同时栈中的参数也会随之销毁,由此可见,this指针在内存中存放的位置是栈区。
四、C++和C语言实现Stack的区别
面向对象的三大特征:封装、继承、多态
区别:
1、C++中的数据和函数都放在了类里面,通过访问限定符进行了限制,不能再随意通过对象直接修改数据,这是C++封装的一种体现,这个是最重要的变化。
封装的本质就是一种更为严格规范的管理,避免出现乱访问修改数据的情况。
2、C++中有一些相对方便的语法,比如Init给的缺省参数会方便很对,成员函数每次不需要传对象地址,因为this指针隐含了传递,使用类型不再需要typedef来进行重定义,直接使用类名即可。