<4> 面向对象到底有什么(上)?

我们很多时候在使用高级程序设计语言进行软件开发的时候,必然会遇到”OOP“,”OO“,”面向对象“等字眼。那么这些名词都是什么意思呢,在代码中是如何来体现的呢?我们高级程序设计语言和我们以前遇到的程序设计语言有什么区别呢,优劣势都是什么?我是觉得,了解了这些,就能理清我们现在的工作。(以C++为例)

1 . 低级程序设计语言(以C为例)

C语言相对于C++其实是低级程序设计语言,我们在使用C语言的时候,主要是利用C的简洁、允许直接访问物理地址,对硬件进行操作、表达方式灵活、数据类型丰富等特点。但是,一套成型的桌面软件应当是由界面、事件响应、后台运算等组成,逻辑操作纷繁复杂,C语言是一种逐过程的设计语言,面对任务,把任务进行分解成所需要的步骤,然后编写函数一步一步的实现即可。

比如需要完成一个象棋的游戏,需要按照一般游戏的玩法,构建程序的流程。初始化棋盘->初始化玩家1和玩家2->玩家1下棋->刷新棋盘->判断是否成功->玩家2下棋->刷新棋盘->判断是否成功->···->玩家1(或玩家2)成功->初始化棋盘。可以看出来,这是一个赢得过程,程序从头到尾都在Run下棋算法。如果针对玩家,还有一些统计数据、排行榜、历史信息等需要添加,或者针对整个局面需要”悔棋“,可能需要在整个程序都需要修改。这样看来,程序的可扩展性不强,程序的逻辑稍微有点混乱。

2 . 高级程序设计语言

而,高级程序设计语言的优势,在于对面向对象的支持。同样是一个象棋游戏,我们首先分析一下象棋游戏的元素都有什么。”象棋“、”玩家“、”棋盘“、”界面“、”统计系统“等等。我们可以明显的看出来,这些元素都是组成一个系统的部分,每一个元素都是一个实体,都有实实在在的存在意义。并且,每一个元素都有的特性。比如象棋,貌似可以被移动,还有多种不同的形态(车、马、卒···),可以走不同的路线,比如玩家,能下棋,能悔棋,能放弃,比如棋盘,有自己的形式。

当我们把这些实实在在的实体抽象出来之后,就成了一个比较通用的对象的集合,我们称之为类。因为具有象棋的这些特性的,我们都可以认为它是个”象棋“,都是一类东西。而功能的实现,只需要我们去拼凑对象来完成一个一个功能即可。并且我们需要添加新的功能,分析功能,找到涉及到的类,添加相应的功能接口,在相应的地方调用,即可完成。


2.1 封装

(以程序员的角度)从上边可以看出来一件事情,就是我们在完成某个功能的时候,仅仅需要在”用到的“地方”调用“相应的函数即可。也就是说,如果这个类不是我们自己的写的的话,我们可能根本不知道里边究竟写了哪些内容,即使是自己写的,也许时间久了,就忘记了函数是如何实现的,但是最最重要的,我们知道如何去用,就可以了,这个就是我们经常说的”面向对象的三大特性之一“的”封装性“。换言之,设计类的人员使用访问修饰符(private等)向外部使用人员屏蔽了内部的实现细节,使得我们能专注于逻辑代码的实现,以及后期比较容易debug。

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{

public:
	Person();
	~Person();

private:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::Person()
{
	//
}

Person::~Person()
{
	//
}
请看上边的例子,这是一个简单的“Person”类,里边有构造函数Person(),析构函数~Person(),还有三个“私有的”成员变量,已经三个“公有的”成员函数。

我们将这个类再稍微完善一下,在使用这个简单的类的时候,是这样的:

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

private:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::~Person(){
	//
}



int main() {

	// 声明一个Person类型的对象
	// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
	Person m_person(25, '男', "chengzhen");

	// 获得年龄
	m_person.get_age();

	// 获得性别
	m_person.get_gender();

	// 获得姓名
	m_person.get_name();

	std::cin.get();
}

我们为类的构造函数添加了形参,目的是在声明的时候,同时可以传入的参数直接在初值列中赋值给Person类的成员变量(这种给成员变量初始化的方式也是比较推崇的,可以看看Effective C++这本书)。然后我们就可以在需要的地方拿到年龄、性别、姓名了。但是我们发现在使用的时候,只是需要调用一下get_age()、get_gender()、get_name()等接口函数,乍一看,我是不知道这个函数究竟是怎么拿到这些成员变量的。但是假设我们这样写:

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::~Person(){
	//
}



int main() {

	// 声明一个Person类型的对象
	// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
	Person m_person(25, '男', "chengzhen");

	// 获得年龄
	m_person._age;
	m_person.get_age();

	// 获得性别
	m_person._gender;
	m_person.get_gender();

	// 获得姓名
	m_person._name;
	m_person.get_name();

	std::cin.get();
}

可以看见,我们在类的设计中,将“成员变量”的访问等级变成了“public”,然后在入口函数中,m_Person类可以直接调用_age,_gender,_name。也就是说,成员函数被完全暴露给了使用者,更有甚者,使用的人可以随意修改成员变量,那么“封装性”的意义就被削弱了。正确的做法是:如果成员变量有被修改的必要,那么可以在成员函数中,添加了可以间接修改的函数。如果没有被修改的可能,那么就只给读的权限。

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
	void set_age(const int &value);
	void set_gender(const char &value);
	void set_name(const std::string &value);
};


void Person::set_age(const int &value){
	this->_age = value;
}

void Person::set_gender(const char &value) {
	this->_gender = value;
}

void Person::set_name(const std::string &value) {
	this->_name = value;
}

Person::~Person()
{
	//
}



int main() {

	// 声明一个Person类型的对象
	// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
	Person m_person(25, '男', "chengzhen");

	// 获得年龄
	m_person.get_age();

	// 获得性别
	m_person.get_gender();

	// 获得姓名
	m_person.get_name();

	// 设置年龄
	m_person.set_age(30);

	// 设置性别
	m_person.set_gender('nv');

	// 设置姓名
	m_person.set_name("Charmain");

	std::cin.get();
}
但是,C++是支持这种行为的。友元(友元函数、友元成员函数、友元类):

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 类的封装
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
	friend void information(const Person &P);
};

void information(const Person &P){
	std::cout << "The age of Person is " << P._age << std::endl;
	std::cout << "The gender of Person is " << P._gender << std::endl;
	std::cout << "The name of Person is " << P._name << std::endl;
}

Person::~Person(){
	//
}



int main() {

	// 声明一个Person类型的对象
	// 请注意:对象的概念在这里开始出现,因为Person是一个集合,一个类型,那么类型的一个一个实际的例子,就是对象
	Person m_person(25, '男', "chengzhen");

	// 获得年龄
	m_person._age;
	m_person.get_age();

	// 获得性别
	m_person._gender;
	m_person.get_gender();

	// 获得姓名
	m_person._name;
	m_person.get_name();

	std::cin.get();
}

上边的代码片段中,我们添加了一个函数,在类中被修饰成为“friend”,意味着这个函数是个友元函数,并不是Person类的成员函数。我们在函数的实现中可以看见,infromation函数的前面并没有形如“Person::”的作用域操作符,也意味着并不是Person类的成员函数,而是一种类中的特殊函数。但是我们可以在函数体中,通过参数声明的对象,任意调用类的私有成员变量。乍一看,貌似这种行为打破了“封装性”。一句话,方便了使用,打破了封装,可谓功过相抵。

未完,待续。














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值