作者介绍:
关于作者:东条希尔薇,一名喜欢编程的在校大学生
主攻方向:c++和linux
码云主页点我
本系列仓库直通车
作者CSDN主页地址
我们以前已经对c语言进行了学习,但我们都一定听过。
c语言是面向过程的语言,而c++是基于面向对象的编程语言,那么什么是面向对象,如何进行面向对象编程,这是我们这一章需要解决的问题。
那么,面向过程和面向对象到底有什么区别?
首先画两个草图
面向过程采用自顶向下的思想,将待解决问题分解成一个个方法(函数),通过函数方法的调用来解决这个问题
比如,我们组织到西藏的旅行,我们怎么去,带什么东西去,怎么带,我们全部把它分解成一个个步骤去完成
下面是面向过程
面向对象我们将待解决问题拆解成一个个的对象,对象间通过方法(函数)来进行交互,通过对象间的交互来完成我们待解决的问题
例如,假如我们在学校里面上课,面向对象思想把我们划分成一个学生对象,然后把教学内容也划分成一个个对象,通过我们这两类对象,可以完成上课这个动作
因为我们要把一个个特征相同的对象划分在一起,所以在面向对象编程里面引入了类这一个东西
而在c++里面的类,首先是通过c的结构体来完成的
在c语言中,如果我们要定义成一个学生类型,可以这么定义
struct Stu
{
char name[20];//姓名
char sex[5];//性别
int age;//年龄
};
我们在主函数中这么定义,一个学生对象就诞生了
struct Stu s1={"张三","未知",18};
当然我们可以对其进行访问
printf("%s\n",s1.name);//打印姓名
而在c++里面,struct已经升级到了具有类的特性,所以为了适应面向对象中对象间的交互,c++的struct内可以定义函数
比如我们要初始化和访问学生的信息
struct Stu
{
void Init(const char*name,char* sex,int age)
{
strcpy(_name,name);
strcpy(_sex,sex);
_age=age;
}
void Print()
{
cout<<_name<<" "<<_sex<<" "<<_age<<endl;
}
//以上都是结构体成员函数
char _name[20];//姓名
char _sex[5];//性别
int _age;//年龄
};
我们可以直接使用
Stu s1;//c++结构体的声明不用加struct
Stu.Init("张三","未知",18);
Stu.Print();
而上面这种结构体的定义,在c++通常使用class来代替
结构体里面定义的变量,通常叫成员变量
结构体里面定义的函数,通常叫成员方法或成员函数
结构体中的元素被称为类的成员
下面我们就来详细介绍一下class
类的标准定义如下:
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
而成员函数定义的位置,有两种方法,要么把函数定义在类内,要么把函数定义在类外,而声明在类内
我们前一节就是用的第一种方法
在第一种方法中,编译器通常会将函数当做内联函数处理
但在大型项目中,我们通常采用第二种方法,因为项目中我们要采取定义和声明分离的策略,方便管理
但是我们要在类外定义时,一定不能忘记在函数名前加作用域限制符
因为类的作用域仅在类内部,在外面定义必须加上
void Stu::print()
{
}
在了解访问限定符前,我们首先来了解一下封装
封装是c++类的三大特性之一,它实现的功能是可以对某些成员属性实施对外隐藏封闭管理,仅实现一些对外的接口来访问类的一些属性和进行对象间的交换,被封装的成员在外不可被访问
封装证明了c++类之间管理较严格
封装本质上是一种管理:我们如何管理兵马俑呢?比如如果什么都不管,兵马俑就被随意破坏了。那么我们首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通
道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,我们使用类数据和方法都封装到一下。不想给别人看到的,我们使用把成员封装起来。
如何实现封装和开放呢?c++提供了三种访问限定符
限定符名 | 作用 |
---|---|
public | 在类内类外可以被访问 |
private | 只能在类内访问,类外不可被访问 |
protected | 只能在类内访问,类外不可被访问 |
其中private和protected有什么区别呢?我们学了继承和友元后再进行详细讲解~
那么,有限定符后,我们可以对我们定义好的学生类型进行改造了
假如我们不想让外界看到我们的成员变量,仅提供打印和初始化两个接口
class Stu
{
public:
void Init(const char*name,char* sex,int age)
{
strcpy(_name,name);
strcpy(_sex,sex);
_age=age;
}
void Print()
{
cout<<_name<<" "<<_sex<<" "<<_age<<endl;
}
private:
char _name[20];//姓名
char _sex[5];//性别
int _age;//年龄
};
Stu s1;
s1.Init();
s1.name;//编译报错,因为private不可在类外被访问
s1.Print();//只能通过这种方式访问类中元素
补充:
我们定义的类,其实相当于是设计房子的图纸,它是不会占实际内存的
而我们把图纸变成实际的房子,让它占有一定内存的过程,叫类的实例化
实例化方式:
Stu s1;将stu类实例化
我们在计算内存时,要首先考虑一个问题,类中的成员变量存在哪里,类里面的函数存在哪里?
我们不妨先计算一下下面这个类的大小
class A
{
public:
void Init()
{
}
void Print()
{
}
private:
int _a;
int _b;
int _c;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
大小如下
是12字节,我们算算,3个int刚好12个字节,为什么没有算函数的大小呢?
我们可以想象,一个类可能要实例化多个对象出来,它们之间的成员变量可能不一样,而每个对象之间调用的函数却是一样的,所以就没有必要在每个对象中间存储函数了,把函数专门存储在某个地方,一起用就行了
而在c++中,成员函数存在公共代码区中,存放的是对应的函数表
特别地,为了占位表示其存在,空类会占据1字节的大小
类也要对齐,对齐规则与c的结构体一模一样
假如还是学生类,我们要使用学生类的方法
Stu s1;
Stu s2;
s1.Init();
s2.Init();
在初始化的时候,既然函数都是在公共区,那么怎么知道函数在设置s1对象,而不是s2对象呢
其实,在成员函数中,隐藏了一个this变量,而this变量是决定问题的关键
this变量的本质其实是一个形参,它传入的是调用该函数对象的地址,this不用用户定义,它在编译器中会被自动定义,自动传入
函数实际的调用如下
s1.Init(Stu* this);//自动传入,不用用户写入
函数修改如下
Stu::Init()
{
this->_a=;//代替_a=a;
}
因为this指针本质是形参,所以指针存放在栈区,而不是类中
this一般用于需要修改对象并且要返回对象本身的时候,可以返回*this.