C++之类和对象1:望尽天涯路
目录
- 前言
- 1.面向对象和面向过程之辨
- 2.类的引入——从struct到class
- 3.类的定义
- 4.类的访问限定及封装
- 4.1访问限定符
- 4.2封装
- 5.类的作用域
- 6.类的实例化
- 7.类的对象模型
- 7.1如何计算类对象的大小
- 7.2类对象存储方式的猜测
- 8.this指针
- 8.1 this指针的引出
- 总结
前言
hello,大家好,今天我们来继续分享关于C++的知识——类和对象。我们这个部分的知识将会分为三部分来介绍,今天这篇是第一部分。王国维言读书有三境界,今天我们就先来这第一重境界:昨夜西风凋碧树,独上高楼,望尽天涯路。闲言少叙,让我们开始吧。
1.面向对象和面向过程之辨
在这个专栏的第一篇文章中,我们就已经介绍过这个问题,我们可以点击链接回顾一下哦C++之坦白说:我与C语言不得不说的那些事
在这里我们就不过多介绍啦。
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
2.类的引入——从struct到class
在C语言中,我们已经学习过了结构体。我们来用一个代码回顾一下:
#include<iostream>
using namespace std;
struct student
{int id;int test_score;
};
void fail(student st)
{if (st.test_score < 60)cout << "yes" << endl;elsecout << "no" << endl;
}int main()
{student sst;cin >> sst.test_score;fail(sst);
}
这是一个检验是否挂科的小程序,我们将一个学生的id和考试成绩定义在一个struct里,又在下面定义了一个检查是否挂科的函数。其实我们也可以将fail函数写在struct里面,这在C语言里面是不允许的,但是在C++里面是可以的,因为C语言中,结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
所以我们可以将上面的函数修改如下
#include<iostream>
using namespace std;
struct student
{int id;int test_score;void fail(){if (test_score < 60)cout << "yes" << endl;elsecout << "no" << endl;}
};int main()
{student sst;cin >> sst.test_score;sst.fail();
}
那么,我们发现,在这个struct的结构体中,我们既定义了一个学生的id和成绩,还定义了一个检验是否挂科的fail的函数,也就是说,我们不仅定义了成员变量(id和score)我们还定义了成员变量的行为(fail),这在C++中,我们有一种更好地比表示方法——类。从此,struct暂时退隐,class正式登上舞台。
3.类的定义
定义一个类的关键字是class。记住它,在我们的C++学习过程中,这个关键字将无比关键。类的定义与struct相似,在花括号结尾也不要忘记加一个分号哦
class classname
{};
类的定义方式有两种,一种是声明和定义全部放在类体中。还有一种是 声明放在.h文件中,类的定义放在.cpp文件中。
需要注意的是,在第一种方法中,成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
下面我们将演示这两种方法:
一般情况下,我们更推荐第二种方式。这种方式更适合书写大型程序。
4.类的访问限定及封装
4.1访问限定符
认识过了类,我们再来认识三个新朋友,private(私有)、public(公有)、protected(保护)。这三个是C++中的访问限定符,决定访问权限。那么我们为什么要设置访问权限呢?
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给用户。
那么,我们先来看一三个限定符各自决定的访问权限吧。
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- class的默认访问权限为private,struct为public(因为struct要兼容C)
好的,明确以上概念我们来举例说明一下为什么要进行访问限定。
比如你有一座很漂亮的庄园,而你又是一位热情好客的主人,所以你经常邀请客人来你的家里做客。客人们可以随意观赏庄园内的花园啊,亭子啊,假山啊(public)但是他们却不能进入你的私人领地比如你的卧室啊你的浴室等地(private)更不能打开你藏在书房里的保险柜和日记(protected)。这就是我们要进行访问限定使用访问限定符的原因。
那么我们来演示一下public和private。由于protected我们在后面进行介绍,我们就先不演示。
首先,我们来观察这个程序:
我们将这个程序做一个小小的改动:
通过以上两个演示,你有没有get到一点二者的神奇?
通常,我们在定义数据成员的时候设为private,在定义函数成员的时候设为public。这样的设计会在类的使用中发挥许多妙用
4.2封装
我们在上一节中提到了封装,那么究竟什么是封装呢?我们先来看定义:
将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
那么如何来理解呢?
其实封装的本质是一种管理举一个例子,期末考试到了,老师如何控制管理期末考试?首先,试卷是严格保密的,老师不会直接把试卷给当代大学生们,所以要放在试卷袋里密封,等到考试才能打开,但是,这么严格保密将会导致一大批人挂科,老师于心不忍啊,那怎么办?于是要划重点,小小地透透题,这样保证大多数学生还是能会几道题的,不至于集体挂科。老师用(private/protected)将试题封装起来确保试卷是保密的,但又通过划重点(public)等你懂的方式泄露了一部分天机,使大家不至于集体挂科。
所以,你get到了吗?
5.类的作用域
我们之前在讲命名空间的时候提到过作用域,也提到过作用域限制符,那么在这里,他们又要重出江湖啦。
类相当于定义了一个新的作用域,类的所有成员都在类的作用域中。那么在类体外定义成员,则需要使用类作用域解析符::来表明属于哪一个类。
上一个例子:
6.类的实例化
什么叫做类的实例化呢?
用类类型创建对象的过程,称为类的实例化
- 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
- 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类>就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
7.类的对象模型
7.1如何计算类对象的大小
我们知道,类中既有成员变量,又有成员函数,那么,我们该如何计算类的大小呢?
7.2类对象存储方式的猜测
1.对象中包含类的各个成员。既包括成员变量,又包括成员函数。
这种猜测是有道理的,但是也是有缺陷的,我们来看看下面这段代码
#include<iostream>
using namespace std;
class student
{int id;int score;int age;void fail(){if(score>60)cout<<"no"<<endl;elsecout<<"yes"<<endl;}
};
int main()
{student st1;student st2;return 0;
}
在这个代码中,我们创建了s1,s2,两个对象,但是,当我们试图通过s1,s2,来调用fail函数时,每一个对象大都会保存一份函数代码,这样会造成空间的浪费。貌似不妥。
2.只保留成员变量,成员函数存在公共的代码段。
这样似乎就可以解决上面我们提出的问题。那么事实的真相到底是什么呢?
我们来验证一下:
我们可以发现:
一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐。
那么,我们不得不要考虑一种特殊情况,空类怎么办呢?
#include<iostream>
using namespace std;
class A
{};
int main()
{A a;cout << sizeof(a) << endl;return 0;
}
输出结果会是什么呢?根据我们上面得出的结论,可能会是0?因为空类里面没有成员变量啊,真的是这样吗?我们来验证一下:
答案是1!这是为什么呢?我们来分析一下:
如果大小为0的话,当我们利用A创建了a1和a2两个对象时,我们该如何区分他们?所以,我们要用一个字节来“占位”,来表示对象存在过。
所以综上所述,我们可以总结出:
一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。
8.this指针
好的,接下来就来到了我们的this指针环节啦。
8.1 this指针的引出
首先,我们先来定义一个日期类。
#include<iostream>
using namespace std;
class Date
{public:void Display()//输出{cout << _year << "-" << _month << "-" << _day << endl;}void SetDate(int year, int month, int day)//初始化{_year = year;_month = month;_day = day;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.SetDate(2018, 5, 1);d2.SetDate(2018, 7, 1);d1.Display();d2.Display();return 0;
}
我们来看一下运行结果:
那么我们这里有一个疑惑哈,Date类中有SetDate与Display两个成员函数,函数体中没有关于不同对象的区分,那当d1调用SetDate函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
其实在这个过程中,编译器会增加一个隐隐含的参数——this指针。所以,如果我们把this指针表现出来,程序就会变成这个样子:
#include<iostream>
using namespace std;
class Date
{public:void Display()//输出{cout << _year << "-" << _month << "-" << _day << endl;}void SetDate(Date* this,int year, int month, int day)//设置{this->_year = year;this->_month = month;this->_day = day;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.SetDate(&d1,2018, 5, 1);d2.SetDate(&d2,2018, 7, 1);d1.Display();d2.Display();return 0;
}
所以,我们可以总结出:
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成
注意:
总结
好的,今天我们的分享就先到这里,感谢大家支持,也欢迎大家批评指正。我们接下来会继续介绍类和对象的相关知识,希望对大家有所帮助。最后,我们分享文章标题中的那首诗晏殊的《蝶恋花》
槛菊愁烟兰泣露,
罗幕轻寒,燕子双飞去。
明月不谙离恨苦,斜光到晓穿朱户。昨夜西风凋碧树,
独上高楼,望尽天涯路。
欲寄彩笺兼尺素,山长水阔知何处?
C++之类和对象1:望尽天涯路相关推荐
- java_B站_面试题
Java面向对象有哪些特征,如何应用 面向对象编程是利用类和对象编程的一种思想.万物可归类,类是对于世界事物的高度抽象 ,不同的事物之间有不同的关系 ,一个类自身与外界的封装关系,一个父类和子类的 ...
- 史上最详细的23中设计模式解析,一个不落,理论搭配简单案例,更好理解哦
目录 一.软件设计模式的产生背景 二.软件设计模式的概念与意义 1. 软件设计模式的概念 2. 学习设计模式的意义 三.23 种设计模式的分类和功能 1. 根据目的来分 2. 根据作用范围来分 3. ...
- 在kotlin companion object中读取Bean,注入Bean对象
在kotlin companion object中读取Bean,注入Bean对象 在使用kotlin时,或多或少地会使用到一些公共组件,如 http. mongo. redis相关的组件. 使用组 ...
- IDEA自动生成对象所有set方法
idea中有一款插件能够生成对象所有的set方法,GenerateAllSetter :下载地址 步骤1:将下载好的压缩包放在自己记得的文件夹中,在idea中进行导入 步骤2:在本地选中刚才的压缩包, ...
- 基于Golang的对象序列化的程序包开发——myJsonMarshal
基于Golang的对象序列化的程序包开发--myJsonMarshal[阅读时间:约10分钟] 一.对象序列化概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 三.具体程序设计及 ...
- java 捕获异常并存入数据库_java异常处理,报异常的话怎么处理对象值,并持久化到数据库中...
展开全部 //没看到有人回e68a843231313335323631343130323136353331333365646233答你,我还没学到框架,不知道那个是不是可以很便捷操作你说的这样过程 / ...
- python程序如何执行死刑图片_如何判断对象已死
已死的对象就是不可能被任何途径使用的对象,有以下几种方法判断一个对象是否已经死了: 引用计数 给对象添加一个引用计数器,每当有一个地方引用他,计算器就加 1:当引用失效时,计数器减 1:任何时刻计数器 ...
- Go 学习笔记(64)— Go error.New 创建接口错误对象、fmt.Errorf 创建接口错误对象、errors.Is 和 errors.As
1. error 接口定义 除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态.error 接口是 Go 原生内置的类型,它的定义如下: // $GOROOT/s ...
- OpenCV 笔记(06)— Mat 结构、像素值存储方法、创建 Mat 对象各种方法、Mat 对象的运算
数字图像中的每个点都称为像素(对于图像元素),并且每个像素可以存储一个或多个值,这取决于它是否是仅存储一个值的黑白图像(也称为二进制图像,比如只存储0或1),还是存储两个值的灰度图像,或者是存储三个值 ...
最新文章
- linux系列服务总结之四:SAMBA共享设置完整介绍
- .NET开发者省份分布排名
- error C2440 “static_cast” 无法从“void (__thiscall CPppView )(void)”转换为“LRESULT (__thiscall
- 关于MacBook Pro 15 usb连接iPhone反复重连的解决办法
- 智能化改造!AI技术在传统企业大有可为!
- windows访问mysql57_windows下 Mysql5.5升级5.7(其实就是安装了两个版本的mysql)
- 浅谈java封装xml报文,XML报文转JAVA对象-JAVA对象转XML报文
- ajax异步提交数据库,yii表单ajax异步提交,数据保存到数据库
- PyTorch:数据读取1 - Datasets及数据集划分
- [BZOJ 3531][Sdoi2014]旅行(树链剖分+线段树)
- 理解J.U.C中的ReentrantLock
- 数据质量评价体系(附思维导图)
- 用matlab画圆极化波,应用HFSS-MATLAB-API设计圆极化微带天线
- 微软同步工具 for linux,使用SyncToy 同步Windows数据到linux
- 分享一个在线转码工具网站 文档音视频压缩等
- maven项目-加载不到spring文件,BeanFactory not initialized or already closed - call 'refresh' bef
- 思科PC远程控制交换机和路由器
- 持久层的EAO颗粒封装
- 最新《JPA入门到精通JAVA进阶项目实战》
- IDEA 出现 Exception in thread main java.lang.OutOfMemoryError: Java heap space 解决方式