好的我们重生c++系列的前两期已经介绍完了c++祖师爷针对C语言补充的几个新功能,现在我们进入c++的真正课题学习——类与对象:
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
比如说我们洗菜做饭,C语言关注的是 拿盆子 倒水 洗菜 放油 炒菜 等等等这些细致的步骤
一共有四个对象 :人 菜 锅 盘子 我们不需要关心菜是如何炒出来的
类的引入
//c++兼容C语言,C语言的用法可以接着用,也有新用法struct Stack
{
int* a;
int top;
int capacity;
};
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
struct Stack
{
int* a;
int top;
int capacity;
};
int main()
{
struct Stack st1;
Stack st2;
return 0;
}
主函数中两种建立类的方式不同,第一种是C语言的玩法,第二种是c++的玩法,明显要更简便一点
同时类中不仅仅可以定义成员变量,并且可以定义成员函数,比如说初始化之类的函数,就没必要在外边写了
上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
struct Stack
{
//成员函数
void Init()
{
a = nullptr;
top = 0;
capacity = 0;
}
//成员变量
int* a;
int top;
int capacity;
};
int main()
{
Stack st2;
st2.Init();
return 0;
}
这样调用Init初始化函数,是不是比C语言阶段在外部传参进去容易多了!
类的定义
class className{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号
大家注意看,在将struc换成class之后,就无法访问我们在类中定义的函数了
这是因为我们没有加上访问限定符:publi private
c++的类是很任性的,我可以选择给你看给你用,也可以选择不给你看,不像struct随便访问,想给你看的加上publi(共有),不想给你看的就加上private(私有)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Stack
{
public:
//成员函数
void Init()
{
a = nullptr;
top = 0;
capacity = 0;
}
void Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
a = (int*)realloc(a,sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
}
private:
//成员变量
int* a;
int top;
int capacity;
};
int main()
{
Stack st2;
st2.Init();
st2.Push(1);
st2.Push(2);
st2.Push(3);
st2.Push(4);
return 0;
}
这样,我们在class中写的函数就可以用了,因为我们在他们的前面加上了public访问限定符
很多同学学到这里会有这样一个疑问,private限制类外访问,那限制类里的访问吗,答案是当然可以,就像是你家有一个保险柜,肯定用来防外人,自己家里人肯定知道密码的对吧。
成员变量的风格
我们定义一个新的日期的类
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Date d;
d.Init(2022,7,13);
return 0;
}
大家看这段代码,思考一下有没有初始化成功
答案:
编译是没有任何问题的,但是:
我们调试就会发现,这三个变量都是随机值,没有被初始化
这就是我们在C语言时期讲过的局部优先原则,忘了或者没听过的小伙伴去C语言的博客里就可以找到,所以我们只需要改一下成员变量
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
d.Init(2022,7,13);
return 0;
}
这样就可以啦,这就是成员变量的风格
封装
类的作用域
class date
{
public:
void Printdate();
private:
char _year;
char _month;
int _day;
};
// 这里需要指定Print是属于date这个类域
void date::Printdate()
{
cout << _year << " "<< _month << " " << _day << endl;
}
类的实例化
想问大家一个问题,像下面代码这样可以访问吗?
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data::_year;
return 0;
}
答案是肯定不可以,所以接着问大家一个问题,private下边的三个变量,是声明还是定义
这是一个声明,不是定义,他们两个的区别就在于声明没有开空间,所以要定义变量,开空间来使用
类的大小模型
看代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
cout << sizeof(Date) << endl;
cout << sizeof(d1) << endl;
return 0;
}
大家猜猜这两个的内存大小分别是多少,一个是图纸,一个是根据图纸建造的房子
小tips:大家想想C语言期间学的结构体内存对齐
答案:
有这个结果可以知道,函数是没有存在类里的
大家想想,如果我们直接定义五个类变量,难道要生成五个函数用的空间吗,大家可以把函数想象成公用的,建立的那个类变量用的函数都是一个
类的大概存储模式
每一个成员变量都是不一一样的, 但是函数放在公共代码区
内存对齐规则
前边在C语言阶段其实我们说过内存对齐的规则,这里再给大家写一下:
大家一定要熟记这个规则并且会运算,这个面试的时候很有可能会用到的,这里小编给大家整理的几个面试上关于内存对齐的面试题:
如果有问题可以在评论区提出来
this指针
上代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void Printf()
{
cout << _year << '/' << _month << '//' << _day << endl;
}
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
return 0;
}
还是之前我们写的代码没有什么心意,但是大家看这一区域
我们在调用初始化函数的时候,使用的是我们在private中定义的三个变量吗?
答案:不是
我在之前说过,private里是声明,根本就没有开辟空间,并且在主函数部分我们开辟了两个变量,如果调用的是private里的变量,那么又是怎么一份变成两份的呢?所以我们根本没用这里的变量这就涉及到我们接下来要学的新知识点this指针。
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
这是我们刚刚写的初始化函数,里边传了三个参数年月日完成传参赋值,其实在这个函数里,含有一个我们看不见的隐藏参数,他真正的原型应该是这样的:
void Init(Date *this ,int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
对比一下,多了个this指针,加粗的部分都是编译器自己加的
函数主体多了个参数,在在主函数调用时编译器也是自动给我们加了东西
我们自己写的:
int main()
{
Date d1;
d1.Init(2023, 7, 23);
Date d2;
d2.Init(2013, 7, 23);
return 0;
}
实际上编译器底层编译的:
int main()
{
Date d1;
d1.Init(&d1,2023, 7, 23);
Date d2;
d2.Init(&d2,2013, 7, 23);
return 0;
}
可以说我们可以运行代码,使用变量和函数都多亏了this指针
有一点值得注意,this指针在形参和实参的位置并不能由我们自己显示的写出来,但是可以在类里边显示的用
如:
class Date
{
public:
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Printf()
{
cout << this->_year << '/' << this->_month << '//' << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
像这样,但是我们不写编译器会自动给我们加,写了反而多此一举,大家了解一下就行
this指针的特性
this指针的存储
我们都知道电脑的内存分为栈,堆,常量区,静态区,那么this指针存在哪里呢,大家不妨猜一猜
this指针是一个形参,一般都存在栈区
好了关于类和对象(上篇)就写到这,大家敬请期待下一期内容!!!