C++类和对象(上)

1.面向过程和面向对象初步认识

什么是面向过程?C语言就是面向过程的,在C语言中分析出求解问题的步骤,通过函数调用逐步解决问题。

举个例子(人工洗衣服):

准备好盆子——》放水——》放衣服——》放入洗衣粉——》放衣服——》拿板凳——》坐下——》手搓——》肥皂——》手搓——》换水——》手搓——》换水——》手搓——》拧干——》拿衣架——》晾衣服

正如这个(人工洗衣服)例子这样,面向过程就是需要逐步完成,最后达到目的。

 什么是面向对象?C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

举个例子(洗衣机洗衣服):

我将衣服放进洗衣机,倒入洗衣液,启动洗衣机,后面的过程将由洗衣机完成,洗完衣服后,我在将衣服晾起。

上述例子,产生了4个对象:人、衣服、洗衣液、洗衣机

人、衣服、洗衣液、洗衣机之间互相交互完成了工作。人把要洗的衣服丢给洗衣机,不用管洗衣机是如何洗衣服的,只需要等待结果就好。

以后别人想要洗衣服也只用套这个公式,即可完成,这个公式可以理解成为类。

 2.类的引入

C语言结构体中只能定义变量。但在C++中,结构体不仅可以定义变量,也可以定义函数,此时,由struct定义的东西,是C++中的一个类。(因为C++兼容C语言,所以struct原来的用法,在C++中也可以实现。)

比如用C++实现数据结构的栈,直接在struct中定义相关的函数。

typedef int DataType;
struct Stack
{
 void Init(size_t capacity)
 {
     _array = (DataType*)malloc(sizeof(DataType) * capacity);
 if (nullptr == _array)
 {
     perror("malloc申请空间失败");
     return;
 }
 _capacity = capacity;
 _size = 0;
 }
 void Push(const DataType& data)
 {
     // 扩容
     _array[_size] = data;
     ++_size;
 }
 DataType Top()
 {
    return _array[_size - 1];
 }
 void Destroy()
 {
     if (_array)
 {
 free(_array);
 _array = nullptr;
 _capacity = 0;
 _size = 0;
 }
 }
 DataType* _array;
 size_t _capacity;
 size_t _size;
};
int main()
{
 Stack s;
 s.Init(10);
 s.Push(1);
 s.Push(2);
 s.Push(3);
 cout << s.Top() << endl;
 s.Destroy();
 return 0;
}

因为C++兼容C语言, 所以struct可以定义类,但在C++中更习惯用class来定义类。

3.类的定义

class className
{
 // 类体:由成员函数和成员变量组成
 
};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。

什么是类?

比如人,这就是一个类;

五官、四肢、躯干是人的属性(成员属性);

这个人能干什么,会什么技能,是这个人的成员函数

拥有上述相同属性和技能的人很多,可以具体到某个人,比如说王某明(也就是实例化出一个对象)

(这个解释看不懂没关系,后面还会有解释)

类的两种定义方式:

1.声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。(什么是内联函数?C++内联函数-CSDN博客

2.类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名。

一般情况下,更期望采用第二种方式。平时的练习为了方便可以采用第一种方式。

成员变量命名规则建议:

若成员函数需要调用成员变量,且成员变量的名字与函数中形参的名字相同(如下图)

上图中的这种情况,看着是不是很变扭?a到底是形参还是成员变量呢?

为了解决这个问题,这边建议成员变量前面加上"_"或者“m”(下图),或者其他区分命名方式。

还有一种方法可以解决这个问题,就是在成员变量前面加上this(为什么呢?这边只是先提一嘴,后面会具体讲)如下图所示。

但是为了便于区分还是在成员变量中加上“_”或者“m”。

4.类的访问限定符及封装

4.1访问限定符

【访问限定符说明】

1.public修饰的成员在类外可以直接被访问

2.protected和private修饰的成员在类外不能直接被访问

3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现位置为止

4.如果后面没有访问限定符,作用域到 } 即类结束

5.class的默认访问权限为private,struct为public(因为struct兼容C)

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

问:C++中struct和class的区别是什么?

答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类 默认访问权限是private。注意:在继承和模板参数列表位置,struct和class也有区别。

 4.2封装

面向对象的三大特性:封装、继承、多态。

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

封装本质上是一种管理,让用户更方便使用类。

比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件,用户根本不用关心。因此计算机外部都套着壳子,将内部细节隐藏起来(封装起来)。

在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。

在类体外定义成员时,需要使用 :: 作用域操作符知名成员属于哪个类域。

 6.类的实例化

用类类型创建对象的过程,称为类的实例化

        1.类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的存储空间来存储它。

        2.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储成员变量。

        就像下面的图片所示,类就相当于一个图纸,没有实际的存在,根据这个图纸造出实际的房子(实例化对象),房子需要实际的空间,就像实例化出来的对象需要物理空间一样。

7.类对象模型

7.1如何计算类对象的大小

                根据上图可以看出计算类的大小,只和成员变量有关和成员函数没有关系,成员变量之和就是这个类对象的大小。

7.2类和对象的存储方式

根据7.1,我们知道计算类的大小,只和成员变量有关,与成员函数没有关系。

那么为什么会这样呢?不是都在一个类里面吗?

因为只保存成员变量,而成员函数存放在公共的代码段。

(因为每个对象的成员变量是不同的,但是调用的成员函数都是相同,若成员函数不是在公共的位置上的话,一个类创建多个对象的时候,每个对象都会保存一份代码,相同的代码保存多次,浪费空间)

根据7.1和7.2的讲解,我们来计算下面三个类的大小。

这个三个类的大小分别为什么呢?

不是说类的大小和成员函数没有关系吗?怎么会是1?

因为一个类中即使没有成员变量,类大小至少为1。

一个空类既然被定义出来了,肯定有它的作用。若大小为0的话,相当于这个东西不存在,也就找不到。可是我们确实是定义了,不可能不存在,所以至少为1

 8.this指针

8.1this指针的引出

我们定义一个日期类Date

上述代码中,我们实例化出了两个对象d1,d2。分别用d1和d2调用了Init函数和print函数。

我们知道在函数体中没有关于不同对象的区分,那d1调用函数的时候,编译器怎么知道是d1对象调用,而不是d2对象调用呢?

C++中通过引入this指针解决该问题。

(如下图)d1和d2调用print函数的时候,相当于传了对应对象的地址,就如①和②那样,然后print成员函数用this指针进行对象地址的接收,正如③那样,于是就能清楚的知道是哪个对象调用这个函数。

但是①和②和③中的this都是隐含的,编译器会帮我们完成操作,不需要我们手打。

但是可以在成员函数内部,也只能在成员函数内部使用this指针(如下图)

(下图中) this=nullptr   这个正确吗?this指针的指向可以更改吗?

this指针的指向是不能被更改的,即在成员函数中,不能给this指针赋值。因为真正的this是“类类型+*const+this”(如下图)(这边的const修饰的是this,所以this指向不能更改)

【思考一下】

答案如下:

为什么第一个对了,而第二个错了?

因为第一个中虽然是空的,但是对象是实际存在的,且在调用成员函数的时候,成员函数没有使用这个空指针,所以正常运行。

第二个因为nullptr的情况下,使用了这个空指针去调用了空的对象的变量,都不存在的东西去调用,当然调不到,所以运行出错。

 未完待续......

下期预告(构造函数、析构函数、拷贝构造函数、赋值运算符重载、const成员函数等)

如果对你有帮助,我感到很荣幸~

如果有错误,欢迎指正批评~

我们下次见~

  • 45
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值