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

目录:

C/C++面向对象编程之封装
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<<this->name<<"的年龄是"<<this->age<<",成绩是"<<this->score<<endl;
}

创建对象:
创建对象和结构体定义变量非常类似,也可以使用指向对象的指针,不同是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://c.biancheng.net/cplus/

联系作者:
欢迎关注本人公众号:

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章