当前位置:首页 > 公众号精选 > 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


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

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

2024年4月11日,中国——意法半导体的ST25R100近距离通信(NFC)读取器芯片独步业界,集先进的技术功能、稳定可靠的通信连接和低廉的成本价格于一身,在大规模制造的消费电子和工控设备内,可以提高非接触式互动功能的...

关键字: 嵌入式 数据读取器 芯片

单片机是一种嵌入式系统,它是一块集成电路芯片,内部包含了处理器、存储器和输入输出接口等功能。

关键字: 单片机 编写程序 嵌入式

深圳2024年4月23日 /美通社/ -- 全球AI解决方案与工业级存储领导品牌宜鼎国际 (Innodisk)持续深化边缘AI布局,今(23)日发表全球首创"MIPI over Type-C"独家技术,让旗下嵌入式相机模...

关键字: AI 嵌入式 相机

为增进大家对嵌入式主板的认识,本文将对嵌入式主板以及嵌入式主板常见问题及其解决方法予以介绍。

关键字: 嵌入式 指数 主板

为增进大家对嵌入式系统的认识,本文将对嵌入式系统、嵌入式系统的特点予以介绍。

关键字: 嵌入式 指数 嵌入式系统

为增进大家对嵌入式的认识,本文将对嵌入式、嵌入式工作相关的内容予以介绍。

关键字: 嵌入式 指数 嵌入式技术

机器人操作系统(ROS)驱动程序基于ADI产品而开发,因此可直接在ROS生态系统中使用这些产品。本文将概述如何在应用、产品和系统(例如,自主导航、安全气泡地图和数据收集机器人)中使用和集成这些驱动程序;以及这样将如何有助...

关键字: 电机控制器 机器人 嵌入式

支持高达48V@5A的PD受电模式,达到目前USB PD最高标准。

关键字: 嵌入式 开发板

【2024年4月8日,德国慕尼黑讯】低碳化和数字化是当今时代人们面临的两大核心挑战,人类社会需要依靠创新和先进的技术,才能破除挑战、推动转型进程。在德国纽伦堡举办的2024国际嵌入式展(Embedded World 20...

关键字: 半导体 微控制器 嵌入式

TDK 株式会社(TSE:6762)进一步扩充 Micronas 嵌入式电机控制器系列 HVC 5x,完全集成电机控制器与 HVC-5222D 和 HVC-5422D,以驱动小型有刷(BDC)、无刷(BLDC)或步进电机...

关键字: 嵌入式 电机控制器 内存
关闭
关闭