坚持看完,结尾有思维导图总结
这里写目录标题
- C语言和 C++ 的区别
- 类的定义
- 类的初认识
- 类的内容
- 访问限定符
- 类的作用域
- 类的实例化
- 类中的 this 指针
- 总结
C语言和 C++ 的区别
C++ 的祖师爷除了在 C语言的基础上化简了一些复杂操作
更为重要的是,两个语言实现的过程是不一样的
C 语言是一种面向过程的语言,而 C++ 是一种面向对象的语言
如果是洗衣服
C 语言会这样洗
那盆子,放水,放衣服,加洗衣粉,手搓,晾干
而 C++ 会先分析有哪些对象
会有 人 衣服 洗衣机 洗衣粉
通过这些对象的交互完成洗衣服
人只要做把衣服放进洗衣机,加洗衣粉,打开洗衣机就可以
不需要关注衣服是怎么洗的
类的定义
类的初认识
上面我们如果是用 C 语言来写,就要定义一个盆子,然后放水是一个函数
放衣服是一个函数,加洗衣粉是一个函数,手搓衣服是一个函数,晾干是一个函数
然后一步步地走
但是 C++ 不同
他会把人 衣服 洗衣粉 洗衣机分别定义成一个个的类
人有哪些特性,功能,归到人这个类里面
衣服有哪些特性功能,归到衣服这个类中
洗衣机有哪些特性功能,归到洗衣机这个类中
我们很自然地联想到,一个自定义的类型,在 C语言中我们常使用结构体定义
struct 但是 在 C 语言里面,结构体内部并不支持函数
然而在 C++ 中,我们可以在结构体里头写函数了 ,这样于 c语言结构体类似但是不同的东西 我们把他叫做类
类的定义是这样的
class Name
{
//成员函数和成员变量
};// 注意分号
类的内容
类中的内容可以分成两类,一类是 成员变量 , 一类是成员函数
如果我们认识一个人,我们能知道这个人是谁,叫什么名字,是什么性别
class Person
{
//成员函数
public:
void personinfo()
{
cout << _name << ' ' << _age << ' ' << _sex << endl;
}
// 成员变量
private:
char* _name;
int _age;
char* _sex;
};
这里的实现是声明和定义放在一起的
有时候,类中声明和函数放在一起的情况,编译器可能会把成员函数看做是内联函数
如果我们想要把声明和实现分离
在实现的位置,一个是不要忘记头文件的应用
在引用完头文件后,要说明实现的函数属于哪一个类,这里是类的作用域的问题
声明则放在类里面
访问限定符
有一件奇怪的事情
为什么成员变量前面要加上 private 而 成员函数前面要加上 public
这里就设计到访问限定符的问题
我们都用过洗衣机,也大概都知道怎么用洗衣机
就那几个按钮的事情对吧
我们不会为了使用洗衣机去研究洗衣机的结构
类也是这样,除了设计者
使用的人只要会用成员函数就可以了,并不会去关注成员变量有哪些
public 变式公共的 ,能够为外界调用
private 表示私人,就是不能被类之外的操作访问,但是类内的函数(成员函数)是可以访问的
那我们就能够调用 public 的函数来间接按照特定的方式访问 private 的成员
还有 一个访问限定符 叫 protected 因为是 简略说明 ,所以暂时不介绍
其中需要注意的是
class的默认访问权限为private,struct为public(因为struct要兼容C)
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止, 如果后面没有访问限定符,作用域就到 } 即类结束。
而这种通过访问权限,隐藏类的成员变量结构,显示类的访问方式的做法,就是封装
就好像是洗衣机内部被洗衣机外壳包起来,我们看不到内部结构的方式,就是封装
而封装是面向对象 的三大特性之一
类的作用域
之前在初识C++ 中,我们说为了防止多个程序员使用同名变量,同名函数
我们可以用不同的作用域,来圈定哪些变量属于程序员 A ,哪些变量属于程序员 B
而当我们定义一个类的时候,会自动生成一个类的作用域
不同的类是可以使用同名函数和变量的,使用类的作用域能够很好地应对这种情况
因为成员变量已经在类里面了不用再表明属于哪一个类
而当函数声明和实现分离时,在实现函数的时候,必须指明属于哪一个类,防止实现的混淆
类的实例化
前面我们解决的是类的声明的问题
类的声明,就好像把动物分成一个个种类,比如像熊猫,这是一类动物
但是其中的每一只熊猫都有自己的特征,自己的名字等
这种就叫做实例化
在计算机中,我们说类有 成员函数,成员变量,都是类的声明
而在使用中调用的类,才是类的实例化
我们可以简单理解为为类开空间就是类的实例化
既然为类开空间了,那就说明有空间大小
空间大小是如何计算呢?
尤其是一个类中有成员函数,整个类大大小计算更加奇怪了
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
void DateInit(Date& date, int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
void Date::DateInit(Date& date,int year,int month,int day)
{
date._year = year;
date._day = day;
date._month = month;
}
int main()
{
Date d1;
d1.DateInit(d1,2023,2,14);
cout << sizeof(d1) << endl;
return 0;
}
我定义了一个日期类,其中内部成员有年月日
通过计算大小,发现
计算大小是 12 ,说明 ---- 内部只给内部成员开辟了空间,但是没有给函数开辟空间
那函数的空间开辟到哪里去了呢?
实际上,类的空间开辟,是这样的
事实上,每一个成员虽然不同,但是调用的成员函数传递的参数都是一样的
也就是说,对于类的成员函数来说
每个类只要把成员函数需要的参数传给我就可以了
成员函数是不管你是哪一个成员
我们在观察一下对应的汇编代码
可以看到调用的是同一个函数
既然编译器都帮你对应的函数调用转换了,和调用一个普通函数差不多
又何必开辟空间储存对应的函数指针甚至是整个函数栈帧呢,答案是没有必要浪费这个空间
但是要注意的是,如果实例化一个空类(一个没有内容的类)
他的空间大小是 1 ,用来占位
类中的 this 指针
在类的成员函数中,我们可以这样写初始化函数
#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;
d1.Init(2023,2,16);
return 0;
}
在调试中我们能够看到 d1 被初始化为 2023 2 16
但是传参中,我可是没有传递任何与 d1 相关的参数
只是之前的 d1.Init 指定了一下而已
成员函数是怎么样找到 d1 然后将 d1 初始化的呢?
实际上,编译器会自动帮我们传递一个 this 指针 的参数交给成员函数
这个 this 指针始终指向 调用这个成员函数的对象
比如我们 d1.Init 调用 Init 成员函数
编译器会把 this 指针传参
然后对应的成员 变成 this->成员 的解引用
但是我们不能直接把 this 指针当成一个参数写在函数上面
如果这样写,编译器会传递自己在函数自动传参的 this 指针
但是 用户写的 this 这个形参的坑就没有人填了,导致形参数量过多,出现问题
总结
希望大家看完,能够有所
如果有错误,请指出我一定虚心改正
动动小手点赞
鼓励我输出更加优质的内容