类的引入
C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
而为了区分C和C++我们将结构体重新命名成class去定义
类的定义
标准格式:
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
注意:结构体不是函数,不管是C的struct还是C++的class 最后大括号完了都得加上“;”
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。 类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。
类的两种定义方式:
声明和定义都在结构体中:
同一个.cpp文件下
#include <iostream>
using namespace std;
//. 声明和定义全部放在类体中
class Date
{
public:
//结构体中的函数
void Init(int year,int month ,int day)
{
_year = year;
_month = month;
_day = day;
}
void ShowDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
public:
//结构体中的变量
int _year;//年
int _month;
int _day;
};
int main()
{
Date dt;
dt.Init(2024, 4, 1);
dt.ShowDate();
return 0;
}
声明在.h,定义在.cpp(常用):
.h声明 person
#include <iostream>
using namespace std;
//. 声明和定义全部放在类体中
class Date
{
public:
//结构体中的函数
void Init(int year, int month, int day);
void ShowDate();
public:
//结构体中的变量
int _year;//年
int _month;
int _day;
};
.C++ person
#include "person.h"
//结构体中的函数
void Date::Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Date::ShowDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
.C++ main
#include "person.h"
int main()
{
Date dt;
dt.Init(2024, 4, 1);
dt.ShowDate();
return 0;
}
为什么使用域作用限定符?
回答:当在不同文件中声明定义,可能会出现相同函数,但是又因为结构体和命名空间等的包装,就需要专门::去打开。
成员变量前加“_”
成员变量(结构体里的变量)和函数参数变量都为相同的时,无法区分,为了更明显表现在代码上各变量关系,常用“_”来表示成员变量
类的访问限定符及封装:
上图一个会报错一个不会报错,其中最关键的在于public。由此我们能看出这个结构体默认是不让你胡乱在结构体外修改他的结构体内的变量的。
我们并不希望结构体内的有些东西会被修改,例如成员变量。
访问限定符:
说明:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class的默认访问权限为private,struct为public(因为struct要兼容C)
封装:
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其 接口提供给外部的用户使用。
面向对象的三大特性:封装、继承、多态
定义:封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。
本质:通过将我们想要保护的隐私东西用保险箱保护(封装)起来。
通过不同的保护容器来实现不同的效果,例如,可以公开的信息就用透明箱子等;
类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符 指明成员属于哪个类域。
类的实例化
定义:用类类型创建对象的过程,称为类的实例化(由图纸->实物)
1.类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
2.. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
简单理解:类只是给你设计了一个住宅区的建筑图纸,你可以用这份图纸在任何地方去建造住宅区,这个过程(给类申请空间)就叫做实例化过程;
//类--设计图纸
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void ShowDate()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
public:
int _year;
int _month;
int _day;
};
int main()
{
Date dt;//类的实例化--开实际空间
dt.Init(2024, 4, 1);
dt.ShowDate();
return 0;
}
类的对象大小的计算
结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。
类的存储位置
注意:类里面函数运行的指令是存在代码段里,但是运行过程中的参数是存在栈里
结构体内存对齐规则:
1. 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是 所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
类成员函数this指针
由来:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性:
1.this指针的类型:类类型* const
2. 只能在“成员函数”的内部使用
3.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
4.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this 形参。所以对象中不存储this指针。
this存在哪里?
1.栈,因为他是隐含形参
2.VS下,是通过ecx寄存器
空指针问题:
//测试空指针问题
class Date
{
public:
void Init(int year, int month, int day)
{
cout << this << endl;
this->_year = year;
this->_month = month;
this->_day = day;
}
void Show()
{
cout << this << endl;
cout << "func()" << endl;
}
public:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(1111, 11, 1);
Date* ptr = nullptr;
ptr->Show(); //正常运行
(*ptr).Show(); //正常运行
ptr->Init(3333, 33, 3);//运行崩溃
return 0;
}
重点:解引用使用时主要看我们所申请的这个对象里包不包括我们要调用的函数或者变量。
解读代码:
结构体的解引用不一定都是有效的,例如ptr->Init()这里进去后要修改_year的数据,此时ptr解引用后并无实际存储数据空间,只是单纯的申请了了一个地址空间。所以此时解引用后是无法进行操作的。
show函数他只进行显示地址等并没有数据的操作过程