1. 类和对象概念
2. 怎么去定义一个类
3. 类的实例化是什么
4. 封装以及访问限定符
5. 类的对象大小的计算
6. 类成员函数的this指针是什么
本节主要的内容就是上面的八个部分,这是学习我们c++的一个最重要的知识点,如果在这里理解不了我们的类和对象的话在后面就不能更好的理解c++的特性了。
-
类和对象的概念
在前期学习中我们都知道c语言是一门面向过程的语言,关注的是过程,而我们的c++就是一门面向对象的语言,关注的是对象。比如我们外面系统用c语言来描述就是使用很多的函数,比如我们的顾客的下单函数,店家的接单函数和通知骑手的函数,以及骑手的接单函数以及送货函数。但是在我们的c++中变不关心我们的过程了,只关心我们的对象。c++就会形成顾客,商家,骑手三个类。然后再类中形成为对象。对象中含有属性和方法(在封装的时候讲解)。
那什么是一个类呢,类就是我们从现实世界中抽象出来的东西,将一些食物的特征集合在一起形成一个类,对象就是我们的现实世界的实体,就是一个类的实例化的事物。 -
怎样去定义一个类
在c语言中我们接触到了struct定义的结构体,其实我们的c语言中struct也就是一个封装数据的集合,也可以使用我们的struct也可以定义一个类。在c++中我们使用class关键字来定义一个类
class className(){
//含有成员函数和成员变量
};
class是定义一个类的关键字,className为类的名字,在{}中包含了类的成员变量和成员方法。(成员变量就是类中的数据,成员函数(方法)就是在类中定义的函数)
类具有两种定义的方法
- 类的声明和定义全部都在类中实现
class Teacher{
public:
void teach(){
cou t<< "上课" <<endl;
}
public:
string _name;
string _teach_book;//所教科目
};
- 类的声明和定义分开
就是类的声明在.h文件中只是进行声明,在.cpp文件中进行实现
class Teacher{
public:
void teach();
public:
string _name;
string _teach_book;//所教科目
};
void Teacher::teach{
cou t<< "上课" <<endl;
}
这里需要注意的是必须加上我们的作用域运算符+类名。因为一个类也是一个命名空间,这样会防止我们的命名冲突。
什么是声明和定义呢?
声明就是我们只进行函数或者变量的声明,含义就是告诉我们有这样一个东西,但是我们并不为他分配空间。如果只声明就去使用的话会报错,因为我们的声明都还没有给与地址,所以不能访问。
定义:就是我们给声明的东西分配内存空间。
那为什么需要声明和定义分开呢?
第一个原因是安全,举个例子,当我们的代码非常珍贵的时候当有不得不给别人使用的使用我们只需要使用声明和定义分开,将我们只有声明的头文件制作成静态库或者动态库给别人使用就好了。就不需要暴露源码给别人。
第二个原因是可读性好,当我们代码声明定义分开的时候我们就只看我们的函数的定义,如果需要了解函数的具体再进入函数体中。
struct定义的类和class定义的类有什么区别?
因为c++需要去兼容我们的c,所以我们的struct同样是可以定义一个类。struct定义的类中的成员默认访问方式是public(共有),但是class定义的类的成员默认方式是private(私有)。
3. 类的实例化
前面提到了我们的类就是显示世界中抽象出来的东西,类的实例化就是对我们抽象出来的东西进行具体化,比如我们的一个类是一个猫类,再实例化的时候就是实例出来一个具体的猫,猫可能具体到颜色,性别,等等都能实打实的看出来。
class Cat{
public:
string color;
string sex;
string name;
public:
void eat(){
cout << "猫再吃东西" << endl;
}
void push(){
cout << "猫在叫" << endl;
}
};
当我们实例就是下面的方法进行实例,并且可以对我们的颜色,性别和姓名进行设置,也可以调用我们类中的成员函数。
Cat cat;
cat.color = "黄色";
cat.sex = "公";
cat.name = "小小";
- 封装
在上面我们提及到了我们的c语言的封装,就是struct就是一个数据的封装,但是在c中只能把数据进行封装,在我们的c++中我们便可以使用类来进行封装,
封装是什么
封装的第一个层面就是将数据和方法结合在一个类中,方便我们管理以及更好的保护这些属性和方法,第二个层面能够通过访问限定符来限制我们的属性和方法向外界的暴露。
在c++中使用public(共有),protected(保护),private(私有)三个访问限定符来控制。
访问限定符的使用说明:
- public修饰的成员函数和属性在类外可以直接访问到
- protected和private修饰的成员函数和属性在类外不能直接访问(在继承的时候我们可学习到他们的不同)
- 访问权限作用域从该访问限定符出现的位置开始到下一个访问限定符出现为止,如果没有下一个就到我们的类的结束
- class默认的访问权限是private,但是struct为public。
封装其实是一种管理的方式,也是从我们的生活中去得到的,当我们需要管理一个重要的东西的时候我们应该怎么办呢,也许建一个房子把这些东西锁在里面不就好了吗,说的没错,封装其实就是这个概念,把重要的东西用其他的封装起来,然后对外面部分人提供一个“开锁”的功能便能得到我们的封装。
- 类的大小的计算
类计算大小同样是和我们计算结构体的大小是相似的,只是有少于的不同,因为在结构体中没有成员函数,在类中含有成员函数。
在c++中其实所有的成员函数都存储在我们的代码段,因为我们每一个成员只是我们的属性不同,但是都是调用的同一个成员函数。所以我们成员函数存储在代码段能够最大程度的节省我们的内存空间。在c++中就使用成员属性和成员函数分开存储的方式。(可以在我们上面的代码中调用两个猫)
#include <iostream>
#include <string>
using namespace std;
class Cat{
public:
string color;
string sex;
string name;
public:
void eat(){
cout << "猫再吃东西" << endl;
}
void push(){
cout << "猫在叫" << endl;
}
};
int main(){
Cat cat;
cat.color = "黄色";
cat.sex = "公";
cat.name = "小小";
Cat cat2;
cat2.color = "红色";
cat2.sex = "母";
cat2.name = "haha";
cat.eat();
cat2.eat();
system("pause");
return EXIT_SUCCESS;
}
打开我们汇编代码得到下面的结果,在汇编中我们能看到我们的call函数是指向我们同一块地址的,所以我们可以得到我们的成员函数是存储在与成员变量不同的空间,所以的对象都是用同一个成员函数,
特别的一些:
class A2{
public:
void fun(){
};
};
class A3{
};
得到结果是注意:空类的大小是1,是一个占位,表示这个类存在过。
其他的同样使用我们的内存对齐的原则。这里不解释,不懂的童靴可以看看c语言的内存对齐章节。
- this指针。
上面提到了我们的成员函数存储在代码段,但是我们不同对象调用的使用为什么会出现不同的结果呢,比如下面的代码
为什么会出现这样的情况呢,打开汇编的时候你仍然会发现我们的call指令调用的是同样的地址,那么这种情况只能出现在我们不同的对象调用的时候传入了不一样的参数,但这个参数是隐含的。在c++中成员函数的参数中隐含着一个变量,这个变量就是this指针,this指针就是对象的指针对象。比如上图的Cat,此时的this指针就是我们的Cat* const this。this指针就是指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问,只不过所有的操作对于用户时透明的,用户不需要传递,编译器自动完成。
this指针存在与那个位置
许多人只知道this指针的存在,那么this指针存在哪里呢,时类中,还是其他地方了?,答案不是类中,如果在类中我们计算我们的类大小的时候就会算出我们的this指针的大小,我们只是在调用函数的时候才会出现这个this指针,所有不难想到我们的this指针就是在我们的栈上,当我们调用函数的时候我们就会开栈,并且都我们的参数进行拷贝(不是引用类型的时候),这是我们就拷贝了一个this指针。但是在不少编译器中他是存在与寄存器中的,比如vs上是存储在寄存器中,存储在寄存器中便于我们经常访问,能够减少开销。
this指针的特性
- this指针的类型:类类型* const
- 只能在成员函数的内部使用
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递个this形参。所以对象中不存储this指针
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。