面向对象入门——C++的类与对象

面向对象入门——C++的类与对象

作为一个转行菜鸡,刚接触面向对象时候,看了好几篇教程,好几个视频,都以上来就开始教你生成对象实体啊balabala,完全看不懂,看看百度给的定义:

对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是动作。对象则是数据和动作的结合体。对象不仅能够进行操作,同时还能够及时记录下操作结果。

好多人给出了一些例子来解释什么叫“面向对象”,但也都比较抽象,着实很难懂。正好最近我自己在学这部分内容,就结合我自己的理解,来尽量用小白(指我自己)也能看懂的语言和例子来说说什么是“面向对象”吧,可能有很多错误理解,大佬们多指正。


有一天,小白问我:“菠萝包菠萝包,看了那么多教程和解说也没明白,到底什么是“面向对象”啊?“类“又是什么?它是干什么用的?为什么要用它?”

我:“这个说起来就有点复杂了……还是先讲个故事吧。”

0. 什么是“类(Class)”

我,是我的世界的神,在我的世界里,需要通过代码,编写程序来创造世间万物。

有一天,我在另一个世界的“地球”上发现了这样一种毛茸茸的东西:

我想在我的世界里也整一个,但是在我的世界里,原本并没有这种毛茸茸的玩意儿,所以我得自己造一个。

我给它起名叫“猫”:

猫{
};

仿照“地球”的猫,有体重,身长,还有不同的毛色等等属性:

猫{
	体重;
    身长;
    毛色;
    ...
};

然后我观察,这种毛茸茸的玩意儿,除了吃饭睡觉,还会卖萌,掉毛。于是:

猫{
    体重;
    身长;
    毛色;
    ...
            
    吃饭() {	...	}
    睡觉() {	...	}
    卖萌() {	...	}
    掉毛() {	...	}
};

我给自己造的第一只“猫”,起名叫瓜皮

猫{
	...
};

猫 瓜皮;

这时候我的世界里,终于有了第一只“猫”。

作为我的世界的主宰,我可以想让它卖萌就卖萌,想让它掉毛就掉毛:

瓜皮.卖萌();
瓜皮.卖萌();
瓜皮.卖萌();
瓜皮.掉毛();
瓜皮.掉毛();

上面的例子里,我们在自己的世界里,创造了一个新的物种——"猫"。

我们定义了一个叫“猫”的类,然后又定义了“猫”这个类的成员变量(体重、身长、毛色、...),和成员函数(卖萌、掉毛、...),规定了“猫”这个物种的“属性”,和“能力”,这样在“我的世界”里,就有了"猫"这一物种。

但是这样,我们只是在自己的世界里,创造了“猫“这个“概念”,它就像一个“蛋”一样(虽然猫不是蛋生的XD),这时候我的世界里还并没有任何一只真正的“猫”。

于是我们又通过这句命令:

猫{
	...
};

猫 瓜皮;  // 生成实体

这才根据我们定义的物种里,生成了一只叫瓜皮实体,就像从“猫蛋(?)”里,孵化出了一只“猫”,然后给它取名叫“瓜皮”。

我:“所以这就是一个‘类’,通过‘类’,我们可以像创造生物一样的,先规定好这种‘生物’的各种属性,还有能力,造出一个‘蛋’,然再后通过它,生成具体的‘猫’。”

1. 为什么要用“类”

1.1. 封装(Encapsulation)

小白:“哇,这么一说,我有些明白‘类’是什么了,可是,我们为什么要用‘类’来产生一只‘猫’呢?用函数的方式也可以的吧?”

小白:“比如我们可以先这样——”

// 猫的能力——函数
猫吃饭() {	...	}
猫睡觉() {	...	}

// 具体的猫的信息——变量
猫_名字 = 瓜皮;
瓜皮_体重;
瓜皮_身长;
瓜皮_毛色;

我:“的确是这样没错,不过如果我想要不止一只猫呢?我们就需要定义很多的变量。又或者我们不想要“猫”,想要一只“狗”,我们就需要重写函数,这样代码就会变得越来越复杂——”

猫吃饭() {	...	}
猫睡觉() {	...	}
狗吃饭() {	...	}
狗睡觉() {	...	}

猫_1_名字 = 瓜皮;
瓜皮_体重;
瓜皮_身长;
瓜皮_毛色;

猫_2_名字 = 香蕉皮;
香蕉皮_体重;
香蕉皮_身长;
香蕉皮_毛色;

狗_1_名字 = 俊介;
俊介_体重;
俊介_身长;
俊介_毛色;

我:“这样就像在徒手一只一只地去创造每一只“猫”和“狗”,如果只有一两只猫猫狗狗还好说,如果是成百上千只’猫‘和’狗‘,甚至还有‘猪’、‘牛‘、’羊‘,就会很痛苦了——“

猫吃饭() {	...	}

猫睡觉() {	...	}

狗吃饭() {	...	}

狗睡觉() {	...	}

猪吃饭() {	...	}

猪睡觉() {	...	}

牛吃饭() {	...	}

牛睡觉() {	...	}

我:“毕竟挨个定义函数和变量,并不是创造生物的核心,但是却不得不写,代码变得越来越不好阅读。于是我们希望能把这些函数进行归类——”

动物 {
    检测物种;
    吃饭;
    睡觉;
    ...
}

我:“你看,这样,是不是就很像之前我们提到的‘类’的概念了呢?这样我们就把一大堆重复度很高,功能类似的函数‘封装’到了一起,使得代码可以重复利用,与其一个一个造,为什么不先创造一个‘蛋’,然后根据不同需要,从中来产生不同的动物呢?“

我:“比如我们想要创造很多只猫,那么我们可以先创造一个‘猫’的‘模板’,然后再从这个‘模板’,批量生成‘猫’,这样我们就不再需要单独的给每个变量都取名字,只要给生成的‘猫’取名字,它自然会有‘猫’模板的所有‘属性’,和‘能力’,这样做的好处就是,代码变得‘模块化’,每当我们需要创造一只‘猫’的时候,我们不用重复的思考需要设置哪些‘变量’,要使用哪些‘函数’,只需要将最初的‘模块’设计好,之后便可以直接使用了。

猫 {
    体重;
    毛色;
    ...
    吃饭(){};
    睡觉(){};
    ...
};
猫 瓜皮;
猫 香蕉皮;
瓜皮.吃饭();
香蕉皮.睡觉()
打印("瓜皮.体重");
打印("香蕉皮.毛色");

我:“这样代码就变得简洁多了,并且逻辑也更清晰。

小白:“噢!那我明白了,不就是懒得复制粘贴,图省事儿嘛,的确这样方便多了。”

我:“emm... 你也可以这么理解。”

1.2. 继承

从这部分开始需要举比较多的具体例子,用C++完成,其他语言也都差不多。

这下小白明白了“类”的作用,以及使用“类”来进行编程有什么好处。

小白也有自己的小世界,于是他迫不及待的打算在自己的世界里,创造一只会“喵喵”叫的猫:

#include<iostream>
using namespace std;
// 通过类来制作一个“猫”的模板
class Cat{
    /* ....... 
       省略部分 
       ....... */
public:
    void Bark(){cout << "喵喵喵~" << endl;}   
};

int main(){
    // 成一只猫,并它叫一声
    Cat cat;
    cat.Bark();
    return 0;
}

输出:

喵喵喵~

但是很快就出现了问题,小白看到了这样一种动物:

长的挺像猫的,但是明显又不是猫,叫声也完全不一样,小白只好又写了一个新的类,起名叫“老虎”。

class Cat{
    /* ....... 
       省略部分 
       ....... */
public:
    void Bark(){cout << "喵喵喵~" << endl;}   
};

class Tiger{
    /* ....... 
       省略部分 
       ....... */
public:
    void Bark(){cout << "老虎:吼!" << endl;}   
};

这样算是实现功能了,直到小白又看到了“狮子”、“美洲豹”、“猞猁”、......。

不过小白发现,这些动物和之前的“猫”,除了叫声、体型等不同,其他地方都很相似。甚至很大部分就是从“猫”的代码里直接复制来的。

小白:“这些动物的代码,大部分都几乎相同,每次都要复制粘贴,又麻烦又复杂,有没有什么办法,把它们都整理到一起呀?”

我:“这时候就可以用到类的‘继承’了。”

小白:“继承?那又是什么?”

我:“就像你爸妈生你一样,你的基因并不是随机生成的,而是由你爸妈的基因来决定的,这样你就有着和他们类似的五官。像‘老虎’、‘狮子’什么的,都和‘猫’很像,我们可以从‘猫’那里,继承‘猫’的‘属性’、‘能力’,然后只修改不同的地方就可以了,比如这样——”

class Cat{
    /* ....... 
       省略部分 
       ....... */
public:
    void Bark(){cout << "喵喵喵~" << endl;}   
};

class Tiger:public Cat{
};

我:“这里我们从‘猫’类继承,产生了一个‘老虎’类,这样,‘老虎’类就有了‘猫’类所有的能力,你试试看。”

小白:"那是不是我们就可以这样——"

#include<iostream>
using namespace std;
// 通过类来制作一个“猫”的模板
class Cat{
    /* ....... 
       省略部分 
       ....... */
public:
    void Bark(){cout << "猫:喵喵喵~" << endl;}   
};
// 从“猫”类继承,制作一个“老虎”的模板
class Tiger:public Cat{
};

int main(){
    // 成一只老虎,并让它叫一声
    Tiger tiger;
    tiger.Bark();
    return 0;
}

输出:

喵喵喵~

小白:“哎呀!这怎么还是喵喵喵?这老虎不是和猫一模一样了?”

我:“没错,因为我们只是单纯的从‘猫’类继承,生成了‘老虎’类,而没有做任何改动,因此这时候的‘老虎’,行为是和‘猫’一样的。这时候我们就需要‘多态’这个概念啦!”

posted @ 2020-08-12 12:36  菠萝包包包  阅读(342)  评论(0)    收藏  举报