作者: @Ai酱
Attention: 1. 本文中的“面向对象”一词属于计算机用语范畴,非人文社科范畴。 2. 虽然本文用c++
实现,但是思想在其他语言一样适用。
对于面向对象很多初学者非常不理解:“为啥要这么麻烦的定义类和各种访问限制?”。 这当然与老师的教法很有关系。 我一直认为:“不理解一个东西为何而产生,那么就不能说真的懂这个东西” 所以,在本文我想聊一聊,面向对象思想有何而来,以及运算符重载又是为了什么。
面向对象是什么?
经常听到一句话叫做:一切皆对象。 下面我们来实践下。
面向过程
假设有一天,老板让你实现存储一个平面坐标(x,y)。然后你会怎么做呢? 拍拍脑袋觉得好简单,不就两行代码吗?于是10秒钟解决。给如下代码给老板看。
// 存储坐标(1,1)
int x=1;
int y=1;
然后,老板说:“小明啊,我刚刚想了想,我们要存储100个点行不行?”。卧槽,怎么不早说。100个点我得复制粘贴100次啊,而且还得改变量名。 想想下面这种代码就头痛。
// 存储坐标(1,1)
int x=1;
int y=1;
// 存储坐标(1,1)
int x1=1;
int y1=1;
// 存储坐标(1,1)
int x2=1;
int y2=1;
...//省略n行代码,n>=50
// 存储坐标(1,1)
int x99=1;
int y99=1;
然后,你翻了翻书。发现结构体这个好东西。于是这这么存储100个坐标点。
struct Point{
int x;
int y;
}
Point a[100];
心想:“我简直是天才啊!”,5行代码解决。 然后,->-你老板看了后,想了想。不行啊,我想对坐标进行加法操作。两个坐标都不能相加要它有何用。 然后你就写了一个函数解决这个问题。
struct Point{
int x;
int y;
};
/**
* 返回a和b对应坐标值相加后的坐标
*/
Point add(Point a, Point b){
Point result = Point();
result.x =a.x+b.x;
result.y =a.y+b.y;
return result;
};
...
// 别人调用你代码这样写
Point c = add(a,b);
然后你兴高采烈的告诉老板,你写完了。老板说:“这么快写完了,不错不错”。
运算符重载
心中大舒一口气,想道:“这家伙终于没咋的了”。 然后,程序员B跟你说,你这不直观啊。整数可以a+b
这么写,按道理坐标也应该要这么写啊。 你微笑道:“好的,我再改改”。脑海里想到四个词:“万马奔腾”。 然后,百度了下。发现咦用类好像还真可以这么写。然后复制粘贴,写成了下面这行代码。于是你顿悟,为啥要用运算符重载了,因为这样别人用你的代码更直观.
class Point{
public:
int x;
int y;
Point(){
x=0,y=0;
}
Point(int _x,int _y){
x=_x,y=_y;
}
Point operator+(Point b){
Point result = Point(x+b.x,y+b.y);
return result;
}
// 哈哈,同事们这是我留给你们的新特性可以打印坐标
print(){
cout<<"(",x,",",y,")"<<endl;
}
};
...
// 别人调用你代码可以这样写,确实变直观了
Point c = a + b;
继承
有一天,老板决定开新业务。说小明啊,我们想做3D的坐标。可以帮我实现下3维坐标存储和加法么? 你想了想,吸取上次的教训。这次一定得考虑怎么利用原有的代码。不然老是这么不停重写不是办法,头发都得掉光了。 然后去知乎搜下怎么重复利用代码,发现有人告诉你继承
可以重复利用代码。 然后,你顿悟。原先是两个维度,我加个维度确实可以继承啊。我就只用写新增那个维度的代码了。 然后改成了这样。
class Point{
public:
int x;
int y;
Point(){
x=0,y=0;
}
Point(int _x,int _y){
x=_x,y=_y;
}
Point operator+(Point b){
Point result = Point(x+b.x,y+b.y);
return result;
}
// 哈哈,同事们这是我留给你们的新特性可以打印坐标
print(){
cout<<"(",x,",",y,")"<<endl;
}
};
// 以下是三维坐标类
class Point_3D : public Point{
public:
//哈哈,x,y在父类已经有了,子类直接继承相当于自己已经定义了x,y. 子类只用管新增的那个维度。
int z;// 新增的一个维度
Point_3D(){
Point();// 重复利用父类的构造函数
z = 0;
}
Point_3D(int _x, int _y, int _z){
z = _z;
Point(_x,_y);
}
// 加法也要改一改
Point_3D operator+(Point_3D b){
//利用父类的运算符重载实现x,y这两个维度的加法
Point_3D result = *this + b;
// 处理新增维度
result.z = z+b;
return result;
}
// 打印的也要改一改
print(){
cout<<"(",x,",",y,",",z,")"<<endl;
}
};
多态
慢慢好像找到感觉了。好像写的有那么点艺术感。 有一天老板说,你给我用一个数组把二维坐标和三维坐标都存在一起。并且还有一次性将他们坐标都打印。 你仰天长啸:“哈哈,开森。终于懂了什么是多态了,多态原来这个用的啊”
Point a[3] = {Point(1,2), Point_3D(1,0,1),Point(0,1)};
// 小B,看我写的代码叼不叼,明明里面有不同类,我同样的代码就正确打印了
// 这就是多态,运行的时候父类会调用父类的print,子类会调用子类的print
for (int i=0;i < 3;i++)
a[i].print();
总结
本文介绍了一些面向对象的思想的出发点,分别是:
运算符重载(为了让别人更直观的用你的代码)
继承(为了少写代码,保留更多的头发)
多态(为了一行代码父类和子类产生不同的效果)
如果你觉得这篇文章对你有用,你的赞是我分享的动力
推荐阅读:
- pytorch神经网络实践(1): 安装与初次使用pytorch搭建神经网络实践手写数字识别教程
- 如何理解那个把嫦娥送上天的卡尔曼滤波算法Kalman filter?
- 啤酒与尿布?机器学习之Apriori算法挖掘商品之间的关联性Python实现
- 概率统计与机器学习神经网络的联系?
欢迎关注我的知乎专栏适合初学者的机器学习神经网络理论到实践。