目录
一、类的定义
1、类的定义格式
2、访问限定符
public(公有访问)
private(私有访问)
protected(保护访问)
3、类域
二、实例化
1、实例化概念
2、对象大小
三、this指针
四、部分笔记
一、类的定义
1、类的定义格式
class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省 略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或 者成员函数
为了区分成面变量,会在成员变量前面加一个特殊标识,如前面或者后面加 _ 或者 m
与C不同的是,C++兼容C中struct的用法,并且将struct升级为了类,明显变化就是struct中可以定义函数
但是class定义的类和struct定义的类的不同是
class默认的访问限定符是私有,struct默认的访问限定符是公有
先看一下下面简单的类的定义
定义了Data类,以及成员变量_year _month _day,还有成员函数Init
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
private:// 为了区分成员变量,⼀般习惯上成员变量// 会加⼀个特殊标识,如 _ 或者 m 开头int _year; // year_ m_yearint _month;int _day;
};int main(){Date d;d.Init(2024, 3, 31);return 0;}
2、访问限定符
在面向对象编程(如 C++)中,访问限定符(也称为访问控制符或访问修饰符)用于控制类的成员(包括数据成员和成员函数)的可访问性。通过使用访问限定符,可以限制类外部代码对类内部细节的访问,这有助于实现信息隐藏和封装的原则,使得代码更加模块化、安全且易于维护。
需要注意的是:
访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有 访问限定符,作⽤域就到}即类结束。
⼀般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。
访问限定符有三类
public(公有访问)
被声明为public
的类成员可以在类的内部、外部以及派生类(继承该类的类)中被访问。这提供了最广泛的访问权限,通常用于定义类的接口,即外部代码可以使用的成员。
private(私有访问)
private
成员只能在所属类的内部被访问,类外部的代码(包括其他类和函数)无法直接访问private
成员。这有助于隐藏类的内部实现细节,防止外部代码意外地修改或依赖于这些内部状态。
protected(保护访问)
protected
成员类似于private
成员,不能被类外部的代码直接访问。但是,protected
成员在派生类(继承该类的类)中是可以被访问的。这在继承关系中用于控制派生类对基类成员的访问权限。
3、类域
类定义了⼀个新的作⽤域,类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤::作 ⽤域操作符指明成员属于哪个类域。
类域影响的是编译的查找规则。
对于下面这个代码,程序中Init如果找不到指定类域Stack,那么编译器就认为Init这个函数是全局函数,编译时找不到array等成员的定义,就会报错
#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;}
二、实例化
1、实例化概念
实例化就是用类类型在物理内存中创建对象的过程。
类是对对象的一个抽象描述,对于这些成员变量只是声明,没有分配空间。
如果说类是一个房屋的建造说明书,实例化就是把房屋建造出来。
2、对象大小
当我们实例化一个对象,对象中是否有存储指针的必要?
比如,当我们Data实例化一百个对象出来,那这100个对象里面都要存Init,Print成员函数的指针吗?这样做太浪费了。
C++规定,实例化的对象也要符合内存对齐规则
内存对齐规则:
第⼀个成员在与结构体偏移量为0的地址处。
其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
注意:对⻬数=编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值。
VS中默认的对⻬数为8
结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩ 就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍。
下面是一个简单的练习
#include<iostream>
using namespace std;
// 计算⼀下A / B / C实例化的对象是多⼤?class A
{
public:void Print(){cout << _ch << endl;}
private:char _ch;int _i;
};
class B
{
public:void Print(){//...}
};
class C
{};
int main()
{A a;B b;C c;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;return 0;
}
对于成员变量A对象的大小是8,首先char类型1个字节,int类型四个字节。
char放在0处,int放在4~7处,这样总共需要8个字节
上⾯的程序运⾏后,我们看到没有成员变量的B和C类对象的⼤⼩是1,为什么没有成员变量还要给1个 字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识 对象存在。
三、this指针
思考一下,Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和 Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?
那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这⾥的问题
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。⽐如Date类的Init的真实原型为, void Init(Date* const this, int year, int month, int day)
类的成员函数中访问成员变量,本质上都是通过this指针访问
C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显 ⽰使⽤this指针。
#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和d2Date 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;
}