博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C/C++面向对象编程之封装
阅读量:4092 次
发布时间:2019-05-25

本文共 6327 字,大约阅读时间需要 21 分钟。

C/C++面向对象编程

目录:

C/C++面向对象编程之封装

前言:

什么是对象:

世界万物皆可称为对象,对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是方法。对象则将一类事物的的特性和行为抽象出来,并封闭起来对待,在计算机里就是是数据和方法的结合体。
什么是面向对象:
简单来说就是以特性和行为去分析一个东西的思想叫面向对象,关注事物的整体属性和行为。
什么是面向对象编程:
就是采用某种编程语言按照面向对象的思想去编程,从而解决问题,就叫做面向对象编程。

1、面向对象编程

面向对象编程思想的三大特点是:封装,继承和多态。这三种机制能够有效提高程序的可读性、可扩充性和可重用性。

嵌入式Linux操作系统虽然是使用 C 语言作为主要的编写语言,但里面的设计大部分都使用了面向对象的编程思想。
编程语言只是一种工具,编程思想才是用好这个工具的关键。 类和对象是面向对象编程的特有属性,虽然C语言不支持类和对象的概念,但是面向对象的编程思想,依然可以指导我们更好的用好C语言这个工具。
注:以下内容把C语言中的结构体和C++的类放在一起对比学习,有助于有C语言基础的同学更好的理解C++中类的概念,另外本文的目的只在于抽取面向对象编程的思想,不对C++的内容做过多讲解。

2、什么是类和对象

在C语言中,结构体是一种构造类型,可以包含若干成员变量,每个成员变量的类型可以不同;可以通过结构体来定义结构体变量,每个变量拥有相同的性质。

在C++语言中,类也是一种构造类型,但是进行了一些扩展,可以将类看做是结构体的升级版,类的成员不但可以是变量,还可以是函数;不同的是,通过结构体定义出来的变量还是叫变量,而通过类定义出来的变量有了新的名称,叫做对象(Object)
在 C++ 中,通过类名就可以创建对象,这个过程叫做类的实例化,因此也称对象是类的一个实例(Instance)
类的成员变量称为属性(Property),将类的成员函数称为方法(Method)
在C语言中的使用struct这个关键字定义结构体,在C++ 中使用的class这个关键字定义类。
C语言中的结构体:

//通过struct 关键字定义结构体struct rt_object{
char name[8]; char type; char flag; //指向函数的指针类型 void (*display)(void); };

C++语言中的类:

//通过class关键字类定义类class rt_object{
public: char name[8]; char type; char flag; //类包含的函数 void display(){
printf("123456789"); } void display(int a){
printf("a=%d",a); }};

注意到类中有两个同名函数display,一个带参,一个不带参,这在C++中叫函数的重载

函数的重载的规则:

  • 函数名称必须相同。
  • 参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
  • 函数的返回类型可以相同也可以不相同。
  • 仅仅返回类型不同不足以成为函数的重载。

C++和C语言的编译方式不同。C语言中的函数在编译时名字不变,或者只是简单的加一个下划线_(不同的编译器有不同的实现),例如,func() 编译后为 func() 或 _func()。

而C++中的函数在编译时会根据它所在的命名空间、它所属的类、以及它的参数列表(也叫参数签名)等信息进行重新命名,形成一个新的函数名。这个新的函数名只有编译器知道,对用户是不可见的。对函数重命名的过程叫做名字编码(Name Mangling),是通过一种特殊的算法来实现的。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)。
函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。

3、内存分布的对比

不管是C语言中的结构体或者C++中的类,都只是相当于一个模板,起到说明的作用,不占用内存空间;结构体定义的变量和类创建的对象才是实实在在的数据,要有地方来存放,才会占用内存空间。

对单片机内存分配不熟悉的可以看这篇文章:
结构体变量的内存模型:
结构体的内存分配是按照声明的顺序依次排列,涉及到内存对齐问题。
为什么会存在内存对齐问题,引用傻孩子公众号裸机思维的文章《漫谈C变量——对齐》加以解释:

在ARM Compiler里面,结构体内的成员并不是简单的对齐到字(Word)或者半字(Half Word),更别提字节了(Byte),结构体的对齐使用以下规则:

  • 整个结构体,根据结构体内最大的那个元素来对齐。比如,整个结构体内部最大的元素是WORD,那么整个结构体就默认对齐到4字节。
  • 结构体内部,成员变量的排列顺序严格按照定义的顺序进行。
  • 结构体内部,成员变量自动对齐到自己的大小——这就会导致空隙的产生。
  • 结构体内部,成员变量可以通过 attribute ((packed))单独指定对齐方式为byte。

strut对象的内存模型:

//通过struct 关键字定义结构体struct {
uint8_t a; uint16_t b; uint8_t c; uint32_t d;};

在这里插入图片描述

对象的内存模型:

假如创建了 10 个对象,编译器会将成员变量和成员函数分开存储:分别为每个对象的成员变量分配内存,但是所有对象都共享同一段函数代码,放在code区。如下图所示:
在这里插入图片描述
成员变量在堆区或栈区分配内存,成员函数放在代码区。对象的大小只受成员变量的影响,和成员函数没有关系。对象的内存分布按照声明的顺序依次排列,和结构体非常类似,也会有内存对齐的问题。

可以看到结构体和对象的内存模型都是非常干净的,C语言里访问成员函数实际上是通过指向函数的指针变量来访问(相当于回调),那么C++编译器究竟是根据什么找到了成员函数呢?

实际上C++的编译代码的过程中,把成员函数最终编译成与对象无关的全局函数,如果函数体中没有成员变量,那问题就很简单,不用对函数做任何处理,直接调用即可。
如果成员函数中使用到了成员变量该怎么办呢?成员变量的作用域不是全局,不经任何处理就无法在函数内部访问。
C++规定,编译成员函数时要额外添加一个参数,把当前对象的指针传递进去,通过指针来访问成员变量。
这样通过传递对象指针完成了成员函数和成员变量的关联。这与我们从表明上看到的刚好相反,通过对象调用成员函数时,不是通过对象找函数,而是通过函数找对象。
这在C++中一切都是隐式完成的,对程序员来说完全透明,就好像这个额外的参数不存在一样。

4、封装的思想

C和C++项目组织方式的对比:

在这里插入图片描述
在这里插入图片描述
不要小看类(Class)这一层封装,它有很多特性,极大地方便了中大型程序的开发,它让 C++ 成为面向对象的语言。
面向对象编程在代码执行效率上绝对没有任何优势,它的主要目的是方便程序员组织和管理代码,快速梳理编程思路,带来编程思想上的革新。
面向对象编程是针对开发中大规模的程序而提出来的,目的是提高软件开发的效率。不要把面向对象和面向过程对立起来,面向对象和面向过程不是矛盾的,而是各有用途、互为补充的。对于C语言同样可以利用面向对象的编程思想开发中大规模的程序。

4.1 C++语言中类的定义和对象的创建

类的定义:

在 C++里 struct 关键字与 class 关键字一般可以通用,只有一个很小的区别。 struct 的成员默认情况下属性是 public 的,而 class 成员却是 private 的。既然 struct 关键字与 class 关键字可以通用,所以在C++的编译环境下,结构体内也是可以放函数的。
一个简单的类的定义:

class Student{
public: public: void setname(char *name); void setage(int age); void setscore(float score); void say(); //声明构造函数 Student(); Student(char *name, int age, float score);private: //成员变量 char *name; int age; float score;};//定义构造函数Student::Student(){
name = NULL; age = 0; score = 0.0;}//定义构造函数Student::Student(char *name, int age, float score){
this->name = name; this->age = age; this->score = score;}void Student::setname(char *name){
this->name = name;}void Student::setage(int age){
this->age = age;}void Student::setscore(float score){
this->score = score;}void Student::say(){
cout<
name<<"的年龄是"<
age<<",成绩是"<
score<

创建对象:

创建对象和结构体定义变量非常类似,也可以使用指向对象的指针,不同是class 关键字可要可不要。以下几种方式都可以创建对象:

class Student stu;  //在栈上创建对象Student stu;  //在栈上创建对象Student stu("小明", 15, 92.5f);//在栈上创建对象,并初始化成员变量Student *pStu = &stu;//在栈上创建对象Student *pStu = new Student;//在堆上创建对象

访问类的成员:

通过对象名字访问成员使用点号.,通过对象指针访问成员使用箭头->,这和结构体非常类似:

int main(){
//创建对象 Student stu("小明", 15, 92.5f); stu.say(); return 0;}
int main(){
Student *pStu = new Student; pstu -> setname("李华"); pstu -> setage(16); pstu -> setscore(96.5); pstu -> say(); delete pStu; //删除对象 return 0;

4.2 C语言实现类的定义和对象的创建

在C语言的编译环境下,不支持结构体内放函数类型,除了函数外,就和C++语言里定义类和对象的思路完全一样了。

以下暂且把结构体称之为类,把结构体定义的变量称之为对象,以方便于理解面向对象的编程思想。
类的定义:

struct Student{
//成员变量 char *name; int age; float score; //成员函数用指向函数的指针替代 void (*say)(struct Student * stu);};void show(struct Student * stu){
printf("%s的年龄是%d,成绩是%f\n",stu->name,stu->age,stu->score);}void setname(struct Student * stu,char *m_name){
if(NULL != stu){
stu->name = m_name; }}void setage(struct Student * stu,int m_age){
if(NULL != stu){
stu->age= m_age; }}void setscore(struct Student * stu,float m_score){
if(NULL != stu){
stu->score= m_score; }}void setsay(struct Student * stu,void (*say)(struct Student * stu))//设置回调{
if(NULL != stu){
stu->say= say; }}

创建对象:

以下几种方式都可以创建对象:

struct Student stu; //在栈上创建对象struct Student stu = {
"小明",15,92.5f,show}; //在栈上创建对象,并初始化成员变量struct Student *pStu = &stu;//在栈上创建对象struct Student *pStu = (struct Student *)malloc(sizeof(struct Student));//在堆上创建对象

访问类的成员:

通过对象名字访问成员使用点号.,通过对象指针访问成员使用箭头->

int main(){
//创建对象 Student stu= {
"小明",15,92.5f,show}; stu.say(&stu); setname(&stu,"小王"); setage(&stu,18); setscore(&stu,99.0f); stu.say(&stu); return 0;}
int main(){
//创建对象 struct Student *pStu = (struct Student *)malloc(sizeof(struct Student));//在堆上创建对象 setname(pStu,"小王"); setage(pStu,18); setscore(pStu,99.0f); setsay(pStu,show); pStu->say(pStu); free(pStu); //删除对象 return 0;}

参考资料:

联系作者:

欢迎关注本人公众号:

在这里插入图片描述

转载地址:http://pdnii.baihongyu.com/

你可能感兴趣的文章
动态规划法(六)鸡蛋掉落问题(一)
查看>>
LeetCode 887.鸡蛋掉落(C++)
查看>>
Dijkstra‘s algorithm (C++)
查看>>
奇异值分解(SVD)的原理详解及推导
查看>>
算法数据结构 思维导图学习系列(1)- 数据结构 8种数据结构 数组(Array)链表(Linked List)队列(Queue)栈(Stack)树(Tree)散列表(Hash)堆(Heap)图
查看>>
求LCA最近公共祖先的离线Tarjan算法_C++
查看>>
Leetcode 834. 树中距离之和 C++
查看>>
【机器学习】机器学习系统SysML 阅读表
查看>>
最小费用最大流 修改的dijkstra + Ford-Fulksonff算法
查看>>
最小费用流 Bellman-Ford与Dijkstra 模板
查看>>
实现高性能纠删码引擎 | 纠删码技术详解(下)
查看>>
scala(1)----windows环境下安装scala以及idea开发环境下配置scala
查看>>
zookeeper(3)---zookeeper API的简单使用(增删改查操作)
查看>>
zookeeper(4)---监听器Watcher
查看>>
zookeeper(2)---shell操作
查看>>
mapReduce(3)---入门示例WordCount
查看>>
hbase(3)---shell操作
查看>>
hbase(1)---概述
查看>>
hbase(5)---API示例
查看>>
SSM-CRUD(1)---环境搭建
查看>>