在c语言中,我们已经学习了自定义类型中的结构体,而在c++中包含着结构体功能的新自定义类型就被称为类.
类的定义
类的定义格式
要想定义一个类首先就要了解类的定义格式,下面我将用c++重写下c语言中写过的Stack来简述一下c++中类的定义格式.
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef int StDataType;
class Stack
{
public:
void Init(int n = 4)
{
_capcity = n;
_top = 0;
_st = (StDataType*)malloc(sizeof(StDataType) * n);
if (_st == nullptr)
{
cout << "初始化失败" << endl;
}
}
void Push(int a)
{
if (_top == _capcity)
{
int newcapcity = _capcity * 2;
StDataType * ptr= (StDataType*)malloc(sizeof(StDataType) * newcapcity);
if (ptr == nullptr)
{
cout << "扩容失败" << endl;
}
_st = ptr;
_capcity = newcapcity;
}
_st[_top] = a;
_top++;
}
bool Empty()
{
return _top == 0;
}
StDataType Top()
{
if (Empty())
{
cout << "栈空" << endl;
}
return _st[_top - 1];
}
void Pop()
{
if (Empty())
{
cout << "栈空" << endl;
}
_top--;
}
void Destroy()
{
free(_st);
_capcity = 0;
_top = 0;
}
private:
int _capcity;
int _top;
StDataType* _st;
};
int main()
{
Stack st;
st.Init();
st.Push(1);
cout << st.Top() << endl;
st.Destroy();
return 0;
}
正如上述代码所示,类的定义要用关键字"class"加上类名(上面的案例为Stack)并在下面用{};来包含类中的东西来定义.
与c语言不同的是,在c语言中结构体中不可以有函数,而c++将其升级为可以包含函数的类.类中的参数被称为成员变量,而类中的函数则被称为成员函数.定义在类中的函数默认为inline内联函数.
大家在看上面代码时可能已经发现了,我在所有的成员变量的命名前都加了"_",其实这不是规定,只是一个习惯,为了更好的区分成员变量和其他变量,为之后写成员函数多提供一些方便,例如下面的日期类和它的初始化函数,如果没有在成员变量的命名前加"_",那么在初始化函数上就会出现形参和成员变量名字相同而导致无法初始化的事情.
class Date
{
public:
void Init(int day,int month,int year)
{
_day = day;
_month = month;
_year = year;
}
private:
int _day;
int _month;
int _year;
};
访问限定符
在上面两个类中,我们都看到了和c语言不同的地方,在类的里面总是有"public"和"private"两个词.
他们两个就是访问限定符,"public"修饰的成员可以在类的外面直接被调用,"private"修饰的则不可以,其实还有"producted",他的作用暂时和"private"相同.他们的作用于从他们出现开始一直到遇到他们中其他的停止,若没有遇到其他则一直到这个类的}截止.
但是他们有什么用处呢?c++增加了他们来防止他人对类内的成员变量的非法访问,使他人只能通过调用类中的成员函数去访问类内的成员变量,增加了c++的规范性,减少了错误的发生.
补充:为了兼容c所以c++中class不写访问限定符就标志着private,而struct中不写则默认为public.
类域
类定义了一个作用域,将所有的成员都扩在这个作用域内,在类外定义成员时需要用::表明成员属于哪个类.
类域影响编译查找规则,如果在成员函数进行生命定义分离时不在定义处表示函数属于哪个类,编译器就会默认函数为全局函数,在编译时就无法在类中找到函数的成员变量,从而报错.例如下面若未标注Date::就会因找不到 _day,_month...而出错.(如下图片
class Date
{
public:
void Init(int day, int month, int year);
private:
int _day;
int _month;
int _year;
};
void Date::Init(int day, int month, int year)
{
_day = day;
_month = month;
_year = year;
}
实例化
在了解完了类之后,不禁会想类只是一给类型,那么这个类型的变量改如何去建立呢.
实例化的概念
用类型在物理内存中创造出对象的过程就是实例化,也就是建立类所对应的对象.
类只是一个抽象的描述,限定了类有哪些成员,但是类里面只是将它们做一个声明而没有给他们分配空间.在用类实例化处对象时才会给这些成员变量分配空间.一个类可以实例化很多个对象.
可以用建筑图纸和由这个建筑图纸建成的房子来比喻,类就像是图纸,用这个类实例化的对象就像是由这个建筑图纸建成的房子.
class Date
{
public:
void Init(int year,int month,int day)
{
_day = day;
_month = month;
_year = year;
}
void Print()
{
cout << _year << "." << _month << "." << _day << "." << endl;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date d1;
Date d2;
d1.Init(2024, 3, 8);
d2.Init(2025, 3, 8);
d1.Print();
d2.Print();
return 0;
}
上面代码的结果为下图,d1和d2是两个不同的对象.
对象的大小
首先类中有成员函数和成员变量,我们需要思考他们在实例化时是否在每个对象中都要给他们新开一次空间.根据c中所学,成员变量时一定要在,每次实例化时都开空间的,但是成员函数有这个必要吗?
成员函数不储存对象的成员变量,是这个类的使用方法,所以所有对象是都可以用同一个方法去实现想要实现的目的的,所以不用在每次实例化时都开空间.只开一次空间即可,在每个类中直接存储函数的地址就可以了,要是反复去开会浪费大量空间.
搞明白了什么需要开空间,剩下的就是如何存放了,和c语言的结构体一样,对象也要遵循内存对齐规则.
若类中只有成员函数或类中没有任何成员,在实例化时,编译器也会分给对象1字节的空间,来标志着这个类被实例化了.
补:内存对齐规则
1.第一个放在0位置上
2.以后的每个成员变量都要对齐到对齐数的整数倍上(对齐数是默认对齐数与这个成员变量的类型大小取小的那个)
3.整体要是最大对齐数的整数倍(最大对齐数是默认对齐数与这个类里面所有类型里面大小最的大取小的那个)
4.以及最后在补一个修改默认对齐数的方法:#pragma pack(4),设置默认对齐数为4.
class Date
{
public:
void Init(int year, int month, int day)
{
_day = day;
_month = month;
_year = year;
}
void Print()
{
cout << _year << "." << _month << "." << _day << "." << endl;
}
private:
int _day;
int _month;
int _year;
};
class N
{
void Print()
{
cout << "xxxxxxxx" << endl;
}
};
class M
{
char b;
int a;
};
class Y
{
};
int main()
{
Date d1;
N n1;
M m1;
Y y1;
cout << sizeof(d1) << endl;
cout << sizeof(n1) << endl;
cout << sizeof(m1) << endl;
cout << sizeof(y1) << endl;
cout << sizeof(Date) << endl;
cout << sizeof(N) << endl;
cout << sizeof(M) << endl;
cout << sizeof(Y) << endl;
return 0;
}
上述代码的结果如下:
this指针
在类的定义中用c++写的Stack的成员函数中可以发现比起c中的代码,在函数的形参处都少了Stack &st这个形参.那在调用时该如何如何区分是那个对象呢?这就要提到c++给了一个隐含的this指针.
在编译器编译之后,成员函数第一个形参的位置上将会是一个this指针,Stack的Init在编译后就会变为void Init(Stack*const this,int n = 4).类中的成员函数访问时也都是通过this指针来访问,给_capcity赋值就是用this->_capcity.
但c++规定不可以在形参和实参显示的去写this指针,但在成员函数中可以显示的去使用this指针.
最后有两个有意思的题可以很好的检测以上的知识,详解在我的这篇博客上~
今天的学习就到这里吧,大家晚安.