【Day25】:从struct进化成class的物件导向技巧(上)

本篇与STM32相关性不大
会有这个章节其实是有原因的...有一天我请学长帮我看一下程序的时候,请我让马达开始旋转,结果意外的看到我打的语法是

motor.rotate();

他立马就说:「你写C++喔?太好了吧」
为什麽学长一看到这一行就知道我写C++呢?以及这样写哪里好呢?这就要提到物件导向对於大程序的种种好处了!

物件导向OOP(Object-oriented programming)

物件导向是一种写程序的概念,主要的精神就是将日常生活中的物件(object)中的概念,应用於程序设计。以一种更生活化、可读性更高的设计观念来进行。用这种方式会让程序更容易扩充、修改与维护。

类别

类别的概念衍伸於C的结构,是使用者定义的抽象型态,可以打包许多资料型别成为一个新的型别。例如我们可以设计一个车子的类别,底下有两个变数分别为l1、l2储存车子轮子间的距离(长与宽),另外还有x、y来储存现在现在移动的距离、还有dir记住现在车子移动的方向...。利用这种方式来描述这个物件,是不是更直观啊。

class Car {
	double l1, l2;
	double x;
	double y;
	int dir;
};

但是到目前为止,这些功能用C的struct都可以完成,接下来要加一些C语言不具备的功能。
C++更人性化的一个设计就是他具有「成员函式」。既然他是一个物件,那不同物件就应该要有操纵物件的「方法」,这个方法就称为成员函式,更白话一点就是可以在类别内宣告函式。
例如:

class Car {
	double l1, l2;
	double x;
	double y;
	int dir;
    //member function 成员函式
	void move(double vx, double vy, double w0){
        /*
        函式内容
        */
    }
	void stop(){
        /*
        函式内容
        */
    }
};

我们的car物件具有两种使用方法,分别是让他以某一个速度移动,以及让车子停下来。宣告的方法与一般函式的宣告没什麽不一样。而在实际写程序时,为了让整个看起来更乾净,通常会将成员函式的内容拉到其他地方来写,只在类别内做宣告,如下:

class Car {
	double l1, l2;
	double x;
	double y;
	int dir;
    //member function 成员函式
	void move(double vx, double vy, double w0);
	void stop();
};

void Car::move(double vx, double vy, double w0){
    /*
    函式内容
    */
}
void Car::stop(){
    /*
    函式内容
    */
}

这样整个Class的内容就像一个手册,可以看到Car这个物件的使用方法。在程序外面宣告时需要在函式名称的开头加上
" 物件名称:: " 就可以表示要定义哪一个物件的函式。
假设我们已经成功实做出这两个函式,我们只要用以下的方法就可以对car这个物件使用函式了。

int main(){
    Car car;
    car.stop();
}

先宣告一个Car型别的物件(习惯上自己定义的型别开头会大写),使用的方法就与C语言的struct相同,变数与成员函式都可以用「.」这个方式存取或使用。

在程序设计时,我们可以先宣告一个物件,接着先列出这个物件要有哪些使用方法,例如上面车子的物件,就可以有好几种使用的方法,可以让车子走,可以让车子旋转,可以让车子停下来...,列出来後,我们再来分别实作出这些函式,例如要让车子往前,我们可能就要在函式内让四个轮子的马达以相同的速度旋转。只要你顺利完成这个函式,以後就不要在动这个部份了,以後我们的工作就是「使用」这些函式。

封装

接下来就要讲到物件导向的三大重要概念之一-封装
封装的意思就是将某些变数封装在类别中,将资料隐藏起来,不让外界任意的使用,这麽做有个很大的好处,那就是我们不会在其他地方任意的更改到这个值,假设有很多人共同撰写一个程序,如果有人不小心动到你的变数,那麽你可能会毫无发觉,但程序怎麽样跑都是错误的。因此将资料隐藏起来是一个很重要的概念。
C++有三种存取级别,分别为private、protected、public,写在程序码後,就可以限制底下变数的存取权限,如下:

class Car{
    private:
        /****/
    protected:
        /****/
    public:
        /****/
}

public代表此一区块属於公共成员,也就是在程序码的任何位置都可以取用这个变数,在C语言当中的struct就可以想像成C++的class,只是里面的所有变数都是public的,没有任何的限制。再来private代表此一区块属於私有成员,具有最高的保护层级,也就是此区块内的成员只能被物件的成员函式存取。protected代表此一区块是属於保护成员,只能让继承此类别的新类别能使用,是专为继承关系订做的一种存取模式。目前我们可以先不管它。
这样的存取限制是在编译过程中就会抓出来的错误,不是在程序执行中才限制,因此如果该变数是private的存取权,你在其他类别当中使用,就会出现error。
在写程序时,虽然资料隐藏对大程序来说很重要的,但是使用上会有一些麻烦,如果只是一个人写,你也很清楚变数在哪里被更动的话,可以先将所有的变数、成员函式以public的方式宣告。(c++中,如果类别内没有加上存取权限,预设都是private)。

资料来源

  1. 吴灿铭(2019)。C++程序设计与运算思维事务。新北市:博硕。

<<:  【Day24】:TIM-输入捕获

>>:  【Day26】:从struct进化成class的物件导向技巧(下)

Day 9: 人工智慧在音乐领域的应用 (有趣的AI演算法三)

昨天我们聊到了快速简单且能达到一定成效的爬山法。 但也聊到了爬山法的缺点在於如果初始位置没有座落在好...

C# 小白需要看小抄 01 - 环境设定篇(使用VS Code on MacOS)

资料统整自StackOverFlow及这位大大 感谢详细图文步骤让小白无痛完成环境设定! 想看文字版...

[DAY9]k8s必学的设定档-yaml (下)

Deployment 在新版的 Kubernetes 官方推荐使用 Deployment 来取代 R...

第21天 - 来试着做一个简易购物系统(5),统计购物车价格

延续昨天的构想,今天来试试看能否成功。 昨天的构想好像有点错误,因为购物车只会有1台,且纪录是暂时的...

DAY9 Kotlin 基础 Repeat语法

上一篇我们为挚友用kotlin写出了生日祝福: fun main(){ var age = 38 p...