当前位置:首页 > 公众号精选 > wenzi嵌入式软件
[导读]前言在上述教程中,我们已经完成了 C 相对于 C语言来说独特的语法部分,在接下来的教程中,我们将叙述 C

前言

在上述教程中,我们已经完成了 C 相对于 C语言来说独特的语法部分,在接下来的教程中,我们将叙述 C 中面向对象的语法特性。我们在学习面向对象的这种编程方法的时候,常常会听到这三个词,封装、继承、派生,这也是面向对象编程的三大特性,在本节我们将依次阐述封装、继承、派生的具体用法,在这里,我们先叙述的是封装这个属性的的相关内容。下图是关于 封装 这个特性所包含的一些内容。

封装

下图就是封装所具备的相关特性:

image-20210209204824118
那么上图所示的抽象出数据成员以及成员函数具体的含义是什么呢,正如前面教程所述,在前面的教程里,我们选用一个 Person类来作为例子进行讲解,其中这个类里我们有 name以及age,这个也就是我们抽象出来的数据,那抽象出来的成员函数也就是前面教程讲到的setName()setAge()函数,在设计这个类的时候,会把这个类的一些成员设置为私有的或者公有的,这也就是访问控制。具体的代码如下所示:

/* 为了代码简便,省略相关构造函数以及析构函数,为的是展示封装的特性*/
class Person {
private:
    char *name;
    int age;

public:

    Person()
    {
        cout << "Person" << endl;
        name = NULL;
    }

    ~Person()
    {
        cout << "~Person()" << endl;
        if (this->name)
        {
            delete this->name;
        }
    }

    void setName(char *name)
    
{
        if (this->name) {
            delete this->name;
        }
        this->name = new char[strlen(name)   1];
        strcpy(this->name, name);
    }

    int setAge(int a)
    
{
        if (a < 0 || a > 150)
        {
            age = 0;
            return -1;
        }
        age = a;
        return 0;
    }
};

继承

继承的含义就如其字面意思一样,用更加专业的话来说,就是从基类继承相关属性,而这个新的类就叫做派生类。下面这个示意图也表明了继承所带来的代码的简洁与方便。

image-20210209211013964
就如上述这张图所示,一个人肯定具有名字和年龄这两个属性,那作为一个学生来讲,他也必定具备名字和年龄这两个属性,那这个时候是要在 Student类里重新定义这些属性么?显然,因为引入了继承这个特性,只需要继承Person类,那么Student就具备 Person类的相关属性。在上述代码的基础上,我们增加如下所示的代码:

/* 注意是在上述代码的基础上 */

class Student : public Person
{

};

int main(int argc, char **argv)
{
    Student s;

    s.setName("zhangsan");
    s.setAge(16);
    s.printInfo();

    return 0;
}
上述代码中,Student类是继承自 Person类的,我们可以看到在上述所示的Student类中,并没有setName和 setAge的成员函数,但是在定义的 Student实例中,却能够适用 setName和 setAge的成员函数,这也就说明了 Student类已经继承了 Person类。

继承后的访问控制

private

一个派生类从一个基类继承而来,而继承的方式有多种,可以是私有继承,也可以是公有继承,同时也可以是保护继承。那么这个时候基类的各个数据成员的访问属性又是怎么样的呢,我们来看一下下面这张图,其展现了以各种方式继承自基类的派生类的数据成员的属性。

image-20210209223145289
从这个表可以清楚地知道基类的访问属性与派生类的访问属性的对应情况。同样的,我们用一个简单的例子来说明这个知识点:

class Father
{

private:
    int money;
public:
    void it_skill(void)
    
{
        cout << "The father's it skill" <<endl;
    }

    int getMoney(void)
    
{
        return money;
    }

    void setMoney(int money)
    
{
        this->money = money;
    }
};
这个是基类的数据成员以及成员函数,为了更好的说明继承后的数据的属性,我们定义一个 son类,代码如下所示:

class Son : public Father
{
private:
    int toy;
public:
    void play_game(void)
    
{
        cout << "play_game()" << endl;

        int m;

        //money -= 1; /* 错误的代码 */

        m = getMoney();
        m--;
        setMoney(m);
    }
};
上述定义了两个类,一个是 Father类,一个是 Son类,Son类继承于 Father类,这两个类用通俗的语言进行解释便是,父亲有自己的私房钱,儿子有自己的玩具,父亲有一项技能是 it,儿子呢比较喜欢玩游戏。因为是继承,所以儿子类具有父亲类的相关属性,但是,作为儿子是不能够直接去父亲兜里拿钱的,那会被揍,但是如果儿子需要钱,可以向父亲要。这对应的代码也就是上述中 money -= 1,但是这是错误的,不能直接从父亲的兜里拿钱,而剩余的三句代码的意思也就相当于是向父亲要钱。用专业的话来讲也就是:派生类不能够访问基类的私有成员,紧接着是主函数的代码:

int main(int argc, char **argv)
{
    Son s;

    s.it_skill();
    s.setMoney(10);
    cout << "The money is:" << s.getMoney() << endl;

    s.play_game();

    return 0;
}
代码输出的结果如下所示:

image-20210209232507917

protected

还是采用比较通俗的话来叙述这一知识点,儿子相对于父亲的关系自然是与其他人有所不同的,比如有一把父亲房间门的钥匙,对于儿子来说是可以拿到的,但是对于外人来说,这是不可访问的。那在程序中要如何实现这么一个功能呢?这里就要引入 protected了。代码如下所示:

class Father {
private:
    int money;

protected:
    int room_key;   /* 增添的 room_key */

public:
    void it_skill(void)
    
{
        cout<<"father's it skill"<<endl;
    }

    int getMoney(void)
    
{
        return money;
    }

    void setMoney(int money)
    
{
        this->money = money;
    }
};
我们可以看到在 Father类中,增添了一项就是 protected修饰的 room_key,紧接着我们来看Son类的代码:

class Son : public Father {
private:
    int toy;
public:
    void play_game(void)
    
{
        int m;

        cout<<"son paly game"<<endl;

        m = getMoney();
        m--;
        setMoney(m);

        /* 外人不能拿父亲的房间钥匙
         * 儿子可以
         */

        room_key = 1
    }
};
我们看到,这个时候,是可以在 Son类里面直接操作使用 protected修饰的 room_key的。在这里总结一下就是:派生类可以直接访问到基类用 protected 修饰的数据成员。接下来,我们继续看主函数的代码:

int main(int argc, char **argv)
{
    Son s;

    s.setMoney(10);
    cout << s.getMoney()<<endl;

    s.it_skill();
    s.play_game();  

    //s.room_key = 1;

    return 0;
}
通过上述代码可以看到 s.room_key = 1这条语句被注释了,这条语句是错误的,虽然基类使用了 protected修饰了 room_key,但是在主函数中,仍然是不能够直接访问 room_key的。

调整访问控制

依旧采用比较通俗的话来阐述,如果儿子从父亲那里继承了一些东西,那这个时候,继承得到的这些东西的处理权就全在儿子了。在程序里面也是同样的道理,我们在上述代码的基础上进行更改,Father类不变,改变 Son类。代码如下所示:

class Son : public Father {
private:
    int toy;
public:
    using Father::room_key;

    void play_game(void)
    
{
        int m;

        cout<<"son paly game"<<endl;

        m = getMoney();
        m--;
        setMoney(m);

        room_key = 1
    }
};
上述代码中,我们可以看到在 public的作用域内,我们使用 using Father::room_key将 room_key的属性更改为 public,做了这样的更改之后,我们就可以在主函数里直接访问 room_key了。代码如下所示:

int main(int argc, char **argv)
{
    Son s;

    s.setMoney(10);
    cout << s.getMoney()<<endl;

    s.it_skill();
    s.play_game();

    s.room_key = 1;

    return 0;
}
上述代码是可以运行的,也说明这种方式是可行的。但是如果想要将 money的属性更改为 public,也就是增加如下所示的代码:

class Son : public Father {
private:
    int toy;
public:
    using Father::room_key;
    using Father::money;

    void play_game(void)
    
{
        int m;

        cout<<"son paly game"<<endl;

        m = getMoney();
        m--;
        setMoney(m);

        room_key = 1
    }
};
那么编译将不会通过,错误信息如下所示:

image-20210210001456319
说明这种方法是不可行的,这是为什么呢?是因为对于 Son来说,money本身就是它不能访问到的数据,那么自然也就不能够对其属性进行更改了。换句更加专业的话来叙述也就是:在调整访问控制的时候,只有类本身能够访问到的数据才能调整它的访问控制,如果其本身对于这个类就是不能够访问的,那么也就无法对其进行更改

那上述可以说是提升访问控制,同样的,也可以降低访问控制,比如说上述的 it_skill,如果不想把这个属性继续继承下去或者说不让外部能够访问到它,那么也可以降低它的访问控制,降低的方法跟提升的方法是一样的,只需要在 private中加上一句代码就可以,加了的代码如下所示:

class Son : public Father
{
private:
    int toy;
    using Father::it_skill;
public:
    /* 省略 */
};
因此,只要对于派生类能够看到的数据成员或者成员函数,它都能够提高或者降低它的访问控制。

三种不同继承方式的差异

在上述的内容中,我们提到了派生类在继承基类的时候,存在不同的继承方式,不同的继承方式对数据成员的使用以及其成员函数的调用存在不同的影响,下面分别是三种不同的继承方式:public和 private以及protected,代码如下所示:

/* 以 public 方式继承 */
class Son_pub : public Father {
private:
    int toy;
public:

    void play_game(void)
    
{
        int m;

        cout<<"son play game"<<endl;

        m = getMoney();
        m--;
        setMoney(m);

        room_key = 1
    }
};

/* 以 private 方式继承 */
class Son_pri : private Father {
private:
    int toy;
public:

    void play_game(void)
    
{
        int m;

        cout<<"son play game"<<endl;
        m = getMoney();
        m--;
        setMoney(m);

        room_key = 1
    }
};

/* 以 protected 方式继承 */
class Son_pro : protected Father {
private:
    int toy;
public:

    void play_game(void)
    
{
        int m;

        cout<<"son play game"<<endl;
        m = getMoney();
        m--;
        setMoney(m);

        room_key = 1
    }
};
上述代码就是以三种不同方式从 Father类得到的 Son类,每一种继承方式存在什么不同呢,我们通过主函数来说明这个问题:

int main(int argc, char **argv)
{
    Son_pub s_pub;
    Son_pro s_pro;
    Son_pri s_pri;

    s_pub.play_game();
    s_pro.play_game();
    s_pri.play_game();

    s_pub.it_skill();
    //s_pro.it_skill();  // error
    //s_pri.it_skill();  // error

    return 0;
}
通过上述代码,并对照上述那种表,我们可以知道,无论是何种继承方式,派生类内部public的成员函数都是可以使用的,而对于从基类继承得到的成员函数,如果是以 protectedprivate方式来继承的话,那么是不能够在主函数进行调用的,因此上述代码中注释掉的两句后面表明了错误。

上述的代码所展示的是一层的继承,我们在继承得到的派生类 Son的基础上继续继承得到 Grandson,首先我们先在 Father类里新增加一个public的数据成员,增加的代码如下所示:

class Father
{

private:
    int money;
protected:
    int room_key;
public:
    int address;
    /*其余不改动,省略*/
};
增加了上述Father类的代码之后,我们来看 Grandson_pub类的代码:

class Grandson_pub : public Son_pub
{
public:
    void test(void)
    
{
        room_key = 1/* room_key is protected */
        address = 2;  /* address is public */
    }
};
上述代码中,Grandson_pub是以 public的方式从 Son_pub继承而来,room_key在 Father类是 protected,在 Son_pub类也是 protected,那么在这里也是 protected,而对于 address来说,它在 Father类里是 public,在 Son_pub里也是 public,在这里也是 public,所以在这里都能够访问到。

紧接着来看,Grandson_pro类的代码:

class Grandson_pro : public Son_pro
{
public:
    void test(void)
    
{
        room_key = 1;  /* room_key is protected */
        address = 2;   /* address is protected */
    }
};
上述中,Grandson_pro是以 public的方式从 Son_pro中继承得到的,以刚刚那种分析的思路我们能够分析得出 room_key当前是 protected以及 address是 protected,那么当前的数据成员在这也就是都能够访问的了。

继续来看Grandson_pri类的代码,代码如下所示:

class Grandson_pri : public Son_pri
{
public:
    void test(void)
    
{
        //room_key = 1; /* room_key is private */
        //address = 2;  /* address is private */
    }
};
上述中,Grandson_pri是以 public的方式从 Son_pri中继承得来,同样按照上述的分析方法,我们能够分析出 room_key和 address都是 private的,既然是 private的,那么也就不能够进行访问,因此上述代码中,我们将两句代码进行了注释。

小结

上述就是本次分享的关于封装以及继承的相关内容,主要是关于继承之后数据成员的访问控制,以及通过不同的方式进行继承时的数据成员的访问控制。

上述教程所涉及的代码可以通过百度云链接的方式获取到,下面是百度云链接:

链接:https://pan.baidu.com/s/18AGYqxkxsEcR4ZW6_Nhevg
提取码:dlst


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

2023年10月18日,中国在第三届“一带一路”国际合作高峰论坛期间发布《全球人工智能治理倡议》,围绕人工智能发展、安全、治理三方面系统阐述了人工智能治理中国方案。

关键字: 人工智能 大模型 代码

学好电子技术基础知识,如电路基础、模拟电路、数字电路和微机原理。这几门课程都是弱电类专业的必修课程,学会这些后能保证你看懂单片机电路、知道电路的设计思路和工作原理;

关键字: 单片机 编程 电路设计

单片机编程需要使用专门的软件工具,这些工具能够帮助程序员编写、调试和烧录程序到单片机中。以下是一些常用的单片机编程软件:

关键字: 单片机 编程 软件工具

我们看到这么多的安全问题,部分原因在于我们对待安全的方式:安全性通常被认为是事后考虑的问题,是在开发结束时才添加到设备上的东西。然而,复杂的系统,尤其是嵌入式系统,有一个很大的攻击面,这让攻击者有机可乘,能够在“盔甲”上...

关键字: 代码 嵌入式系统 软件漏洞

Java语言和C语言是两种不同的编程语言,它们在语法、特性和应用领域上有许多差别。下面将详细介绍Java语言和C语言之间的差异以及它们各自的技术特点。

关键字: Java语言 C语言 编程

嵌入式系统是一种专门设计用于特定应用领域的计算机系统,它通常由硬件和软件组成,并且被嵌入到其他设备或系统中,以实现特定的功能。在嵌入式系统的开发过程中,选择适合的编程语言是至关重要的。C语言是一种被广泛应用于嵌入式系统开...

关键字: 嵌入式 计算机 C语言

C语言是一种广泛应用于软件开发领域的编程语言。它是由贝尔实验室的Dennis Ritchie在20世纪70年代初创建的,旨在为UNIX操作系统的开发提供一种高级编程语言。C语言具有简洁、高效、可移植性强等特点,因此成为了...

关键字: C语言 操作系统 应用程序

嵌入式系统是现代生活中无处不在的一部分。它们包括了我们的家电、汽车、智能手机、医疗设备等等。这些系统的工作必须高效、可靠,因为它们往往控制着生活中的关键方面。而C语言作为一种广泛用于嵌入式系统开发的编程语言,其质量和稳定...

关键字: 嵌入式系统 C语言 编程

在嵌入式系统开发领域中,C语言是使用最广泛的编程语言之一。它具有高效、灵活和可移植的特点,成为嵌入式系统设计师的首选语言。本文将介绍C语言编程的基本概念、特点以及在嵌入式系统开发中的应用。

关键字: 嵌入式系统 C语言 编程

C语言编译器是一种用于将C语言源代码转换为可执行程序的软件工具。它的主要功能是将C语言代码翻译成机器语言,以便计算机能够理解和执行。C语言编译器通常包括预处理器、编译器、汇编器和链接器等多个组件,它们协同工作以完成编译过...

关键字: C语言 编译器 Microsoft Visual C++
关闭
关闭