面向对象入门——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;
}
输出:
喵喵喵~
小白:“哎呀!这怎么还是喵喵喵?这老虎不是和猫一模一样了?”
我:“没错,因为我们只是单纯的从‘猫’类继承,生成了‘老虎’类,而没有做任何改动,因此这时候的‘老虎’,行为是和‘猫’一样的。这时候我们就需要‘多态’这个概念啦!”

浙公网安备 33010602011771号