类和对象(上)
- [一 类的定义](#一 类的定义)
-
- [1.1 类的命名规则](#1.1 类的命名规则)
- 1.2类的访问限定符
- [1.3 类域](#1.3 类域)
- 二、类的实例化
-
- [2.1 实例化的概念](#2.1 实例化的概念)
- [2.2 内存分配](#2.2 内存分配)
-
- [2.2.1 类的内存分配规则](#2.2.1 类的内存分配规则)
- [2.2.2 成员函数不分配内存的原因](#2.2.2 成员函数不分配内存的原因)
- [2.2.3 内存对齐规则](#2.2.3 内存对齐规则)
- 三、this指针
-
- [3.1 this指针的意义](#3.1 this指针的意义)
- [3.2 this指针的规则](#3.2 this指针的规则)
一 类的定义
1.1 类的命名规则
1、类的关键字是class,class后面的是类名,{}中的内容是类的主体,{}后面的分号不可省略
2、类中声明的变量叫做成员变量,类中声明的函数叫做成员函数
3、C++中struct也被升级了,也可以在struct中定义函数了,和class存在默认访问权限的区别
4、定义在类里面的函数木默认是内联函数
5、为了区分成员变量和其他变量,一般要在成员变量前面加上 _ 以作区分
以下是示例
cpp
#include<iostream>
using namespace std;
//在c++里面Stack即可表示类型,不用typedef了
class Stack
{
public:
// 成员函数
void Init(int n = 4)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
void Push(int x)
{
// ...扩容
array[top++] = x;
}
int Top()
{
assert(top > 0);
return array[top - 1];
}
void Destroy()
{
free(array);
array = nullptr;
top = capacity = 0;
}
private:
// 成员变量
int* _array;
size_t _capacity;
size_t _top;
//加上_以作区分
}; // 分号不能省略
int main()
{
Stack st;
st.Init();
st.Push(1);
st.Push(2);
cout << st.Top() << endl;
st.Destroy();
return 0;
}
1.2类的访问限定符
1、访问限定符体现了面向对象中的封装思想
2、类中存在三种访问限定符,分别是:private、protected、public
3、访问限定符的作用域是一直到下一个访问限定符,如果没有,就是到结尾处
4、prvate和protected的内容是受保护的,只能通过类的成员函数访问,他们的区别在继承方面体现(本篇不涉及)
5、public的内容是可以在全局访问的,一般是实现某些功能的函数
6、使用struct声明的类的默认权限是public,而使用class声明的类的默认权限是private
1.3 类域
类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤ :: 作⽤域操作符指明成员属于哪个类域。
类域影响的是编译的查找规则,如果我们没有显式写出函数属于哪个类域,编译器会认为这个函数是全局函数,这时候就找不到函数中使用的类的成员变量了,指定类域实际上就是在告诉编译器在当前域查找不到时应该去哪里查找
cpp
#include<iostream>
using namespace std;
class Stack
{
public:
// 成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
int main()
{
Stack st;
st.Init();
return 0;
}
二、类的实例化
2.1 实例化的概念
类的声明和定义就像是为一座房子画好设计图,而示例化就是将这个房子建出来。
具体体现就是:类的声明和定义是模板,并没有实际分配空间,实例化就是实际分配空间

以下是实例化的具体体现
cpp
int main()
{
Stack st;
st.Init();
return 0;
}
2.2 内存分配
2.2.1 类的内存分配规则
类中的成员变量全部分配内存,遵循内存对齐原则
类中的成员函数不分配内存,原因稍后讲解
我们在主函数中加入打印实例st的语句,观察内存分配情况
cpp
int main()
{
Stack st;
st.Init();
cout << sizeof(st)<<endl;
return 0;
}

可以看到st内存占24个字节
这是在x64环境下运行的,指针占4个字节,size_t占8个字节。经计算可得其大小符合内存对齐规则
2.2.2 成员函数不分配内存的原因
首先,成员函数编译后是一串指令,指令肯定是无法储存的,要储存肯定是储存函数指针
其次,相同类所创造出来的不同的实例所包含的成员函数是相同的,如果我们每实例化一次就储存一个函数指针,肯定是对空间的极大浪费
综上:在类的实例中储存函数指针是不经济的
解决方案是将成员函数储存在类成员函数表里面,需要时直接访问这个公共代码区

2.2.3 内存对齐规则
• 第⼀个成员在与结构体偏移量为0的地址处。
• 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
• 注意:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
• VS中默认的对⻬数为8
• 结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
• 如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。
三、this指针
3.1 this指针的意义
在当前阶段,this指针主要是起到表示当前对象的作用
cpp
#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;
_month = month;
_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;
}
在上面的Date类中,我们在分别调用d1和d2的Init过程中编译器应该如何区分他们两个的成员变量呢?
方法就是:创造一个this指针(隐式)
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类的指针,叫做this指针。
事实上的d1.Init应该是这样的:
cpp
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
3.2 this指针的规则
this指针不可以被修改
this指针不可以在成员函数的参数位置显式声明
this指针可以被显式调用