简述
本节简单介绍一下什么是面向对象的程序设计思路。
首先对其意义做介绍,也就是为什么要面向对象。抽象意义上一个对象
就是用来描述、操作一个具体事物
的控制实体
。而具体事物是显然不断在变化升级的,因此控制实体也需要不断升级以适配事物的新发展。
而通过面向对象的程序设计,可以极大提高控制实体
的代码复用程度,减少在更新换代中需要写的代码量。
比如说一个游戏的主角可以转职,转职前只能移动、跳跃和使用近战攻击,
转职后可以远程了,使用面向对象的程序设计思路,
以主角作为一个对象,转职前移动跳跃的代码完全可以不必写
(连复制都不必),
可以直接复用,而只需要添加一个远程攻击的代码即可。
上面所述是面向对象程序设计最主要的优势,即使得对于某事物的更新迭代变得更容易。当然还有很多其他优势,为了减少理解量暂时不提。
类
面向对象程序的主要代码工作就是描述一类对象
,也就是写类
,相当于画蓝图。而写完后的一个程序的运行就是根据类
创建各种各样的对象
完成不同的任务。
对象是类的一个实例,类是创建对象的模板
描述一类对象
的主要工作在于:
1.定义这类对象需要的各种数据,称为成员变量。
2.定义这类对象需要对各种数据进行的操作,称为成员函数。
这里用一个例子简单展示一下,这里使用C++语言。
面向对象的语言就是提供实现面向对象程序设计思想的语言基础,C++、Java、Python都是经典的面向对象语言
#include<iostream>
using namespace std;
class Calc//一个类,这个类用来处理a+b问题
{
public:
int a, b;//两个成员变量
Calc(){//一个对象的构造函数,这里无参,函数内容就是初始化a、b为0
a=b=0;
};
Calc(int a,int b){//重载一个指定了a、b参数的构造方法。
this->a = a;//这里this是表示指向自己这个对象的地址
this->b = b;
}
~Calc();
int getPlusAnwser(){//一个普通成员函数,返回计算结果
return a + b;
}
};
int main(){
Calc c1;//默认无参构造
c1.a = 100;//手动设置参数
c1.b = 200;
Calc c2(10, 20);//实例化一个类,即创建对象,采用有参构造直接初始化a、b参数
cout<<c1.getPlusAnwser()<<endl;
cout<<c2.getPlusAnwser()<<endl;//通过对象调用成员函数,使用的参数就是本对象内部的参数
return 0;
}
得出来的结果是
300
30
更具体的类编写规则可以到网上找:
https://www.runoob.com/cplusplus/cpp-classes-objects.html
封装
刚刚我们演示了一个最简单的类的编写与对象的创建,并完成了a+b问题
我们介绍进阶一些的面向对象概念,主要是面向对象程序设计中最重要的三个特性:封装、继承和多态。
首先是封装
,主要有两层概念:
1. 数据的储存和数据的操作封装在一起,构成一个统一独立实体
上面的例子已经能体现这一点了。
2. 数据被隐藏/保护在抽象数据类型(自定义类)的内部
只保留一些对外接口与外部发生联系,安全性更高,也避免错误的使用。
这主要是通过限制访问权限等操作实现
下面用一个简单的例子说明一下第二点的优势:
#include<iostream>
using namespace std;
class Student
{
private:
int score;
public:
Student(){score = 0;};
Student(int score){
if(score<0 || score >100){//score必然在[0,100]区间
this->score = 0;
}else{
this->score = score;
}
}
~Student(){};
void setScore(int score){
if(score<0 || score >100){
this->score = 0;
}else{
this->score = score;
}
}
int getScore(){
return this->score;
}
};
int main(){
Student s;
s.setScore(101);
cout<<s.getScore()<<endl;
s.setScore(99);
cout<<s.getScore()<<endl;
return 0;
}
注意这里将Student类的score成员对象设定为private
,说明只有对象自己能够访问此成员,
这样在main函数里面就无法使用s.score = 101
直接赋值
只能通过调用s.setScore(101)
,通知对象帮忙设定指定值
而此时就能在成员函数里判断要设定的这个值是否有问题等,防止错误的赋值
上例结果如下:
0
99
继承
继承是实现代码复用的最主要特性
子类继承父类,会继承父类的非private
成员变量与方法。
也举个例子:
比如如果不采用继承,描述两个类Student
和Teacher
#include<iostream>
#include<cstring>
using namespace std;
class Student
{
private:
string name;
int score;
public:
Student(string name, int score){
this->name = name;
this->score = score;
}
~Student(){};//析构函数,当销毁对象时调用
int getScore(){//一个普通成员函数,返回计算结果
return this->score;
}
string getName(){
return this->name;
}
};
class Teacher{
private:
string name;
int salary;
public:
Teacher(string name, int salary){
this->name = name;
this->salary = salary;
}
~Teacher(){};
string getName(){
return this->name;
}
int getSalary(){
return this->salary;
}
};
int main(){
Student s("CakeCN", 60);
Teacher t("Opela", 2000);
cout<<s.getName()<<" "<<s.getScore()<<endl;
cout<<t.getName()<<" "<<t.getSalary()<<endl;
return 0;
}
运行结果如下:
CakeCN 60
Opela 2000
通过继承实现,这里注意到Student
和Teacher
都有同样的Name
属性,抽象出一个Person
类作为两者的父类,这样就不用写两遍输出Name
的代码了。
当然因为重复度不高,所以代码量区别不大,不过如果共性的属性age
、gendar
、height
很多,这种复用就很优越。
#include<iostream>
#include<cstring>
using namespace std;
class Person{
protected:
string name;
public:
Person(string name){
this->name = name;
}
string getName(){
return this->name;
}
};
class Student : public Person //用public继承,不改变父类的成员访问权限
{
private:
int score;
public:
Student(string name, int score):Person(name){
this->score = score;
}
~Student(){};
int getScore(){
return this->score;
}
};
class Teacher : public Person
{
private:
int salary;
public:
Teacher(string name, int salary):Person(name){
this->salary = salary;
}
~Teacher(){};
int getSalary(){
return this->salary;
}
};
int main(){
Student s("CakeCN", 60);
Teacher t("Opela", 2000);
cout<<s.getName()<<" "<<s.getScore()<<endl;
cout<<t.getName()<<" "<<t.getSalary()<<endl;
return 0;
}
多态
多态是面向对象的核心特性,它尽管没有继承
那么好的直观理解,但与之相当重要。
相对于继承
的提供了一种容易描述事物的发展性
的能力,多态则是描述事物在发展中发生变化
而需要对其的多样性
给予的支持能力。
下面举个例子,比如来了一个人,但是事先不知道这个人是学生还是老师,又需要能够得出这个人的信息,就可以使用下面两种多态能力:父类指针指向子类对象
以及重写父类的同名函数
#include<iostream>
#include<cstring>
using namespace std;
class Person{
protected:
string name;
public:
Person(string name){
this->name = name;
}
string getName(){
return this->name;
}
//虚函数,说明子类需要重写这个函数,各自输出不同的信息
virtual void info(){}
};
class Student : public Person
{
private:
int score;
public:
Student(string name, int score):Person(name){
this->score = score;
}
~Student(){};
int getScore(){
return this->score;
}
void info(){
cout<<"im Student "<<name<<" score is:"<<score<<endl;
}
};
class Teacher : public Person
{
private:
int salary;
public:
Teacher(string name, int salary):Person(name){
this->salary = salary;
}
~Teacher(){};
int getSalary(){
return this->salary;
}
void info(){
cout<<"im Teacher "<<name<<" salary is:"<<salary<<endl;
}
};
int main(){
Person* p;
p = new Student("CakeCN", 60);
p->info();
p = new Teacher("Opela", 2000);
p->info();
return 0;
}
运行结果是
im Student CakeCN score is:60
im Teacher Opela salary is:2000
多态还有其它的体现点,不过上面的例子是比较经典的两种多态体现(重载广义上来说也可算是一种多态体现,但是并没有太大面向对象的特质)。
更重要的优势
比复制黏贴更强的代码复用,意味着你抄代码变得更容易了——事实上现在写各种需求基本上都是先去git找有没有相似的需求然后自己改。
也就是拿别人写好的库,直接有什么自己想添加的内容继承添加即可,既可以防止对别人的库的破坏,又利于自己实现自定义的需求。
以及更抽象的对面向对象的认识,就需要更多代码的训练培养了。