c/c++学习_封装(类和对象)

什么是封装?

       封装(Encapsulation)是将数据和处理数据的程序(procedure)组合起来,仅对外公开接口(interface),达到信息隐藏(information hiding)的功能。封装的优点是能减少耦合(Coupling)。C++、Java、C# 等语言定义对象都是在语法中明确地使用类型(Class)来做到封装。

       封装可以隐藏实现细节,使得代码模块化;封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。在面向对象编程上可理解为:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

        封装的三个条件:

1.有一个清楚的边界。

2.有确定的接口,用来接收用户发送的信息。

3.受保护的内部实现。

       数据封装:

       c++仰仗强有力的类型检查能力,正确无误的识别类型、对象、成员、变量与操作,匹配参数传递中的实体类型、过滤公有、私有的访问权限,并用可见性规则挑剔不合格常规默认名字,使得编程的数据外在安全性得到保障。

       c++通过类机制对类的数据访问权限的规定,拒绝或屏蔽来自类外部直接访问类内操作的数据。这种屏蔽是类对象内部数据的有选择屏蔽,从而使数据仅在类对象内部的专门操作(成员函数)来操控,以保证数据的内在安全性。这种内在的安全性正是语言设计者在编程层面对数据经受各种目的的访问所造成的内容不确定性的应对技术,即类机制的数据封装技术。

       利用类的数据封装,提高了对象的访问的安全性。用户不能直接操作对象中的数据分量,只能以对象的身份去操作(调用成员函数),去影响对象的值,从而职责分明:如果对象执行了某一功能的操作,却没有达到该功能的所声称的效果,则找类算账;如果对象执行了操作,达到了该操作所声称的功能,还不理想,那一定是应用错误,由编程者自己负责。职责明确使类定义和其实现比数据的过程化处理更容易重用和维护了。

       批注:数据封装对对象来说,带来了统一的分布格局和严格的访问限制。由于对象数据往往不是独立的--同类对象之间需要在类内共享数据,异类对象之间需要数据往来,于是数据的操作经常是以一个局外人的身份往来于诸多不同的对象之间,使得屏蔽技术所主导的数据封装在编程之中又带来了访问不便。于是,在数据屏蔽的基础上,类的静态成员和友元便作为完善数据封装技术的语言成分粉墨登场了。

       下面具体阐述一下类和对象

       1.对象

  • 任何一个对象都应当具有这两个要素,一是属性(attribute);二是行为(behavior),即能根据外界给的信息进行相 应的操作。对象是由一组属性和一组行为构成的。(PS:使用面向对象的程序设计方法设计一个复杂的软件系统时,首要的问题是确定该系统是由哪些对象组成的,并且设计这些对象
  • 对象的概念:是一个封装体,封装了数据结构以及可以施加在这些数据结构的操作。
  • 在C++中,每个对象都是由数据和函数(即操作代码)这两部分组成的。
  • 我们可以对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,也就是说从外界是看不到的、甚至是不可知的。(使用对象的人完全可以不必知道对象内部的具体细节,只需了解其外部功能即可自如地操作对象。 )
  • 把对象的内部实现和外部行为分隔开来。
  • 因此人们设想把相关的数据和操作放在一起,形成一个整体,与外界相对分隔。这就是面向对象的程序设计中的对象。 
  • 面向对象的程序组成:
                                    对象=算法+数据结构
                                    程序=(对象+对象+对象+......)+消息  (消息的作用就是对对象的控制)

  • 程序设计者的任务包括两个方面:一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起;二是考虑怎样向有关对象发送消息,以完成所需的任务。各个对象的操作完成了,整体任务也就完成了。(程序设计的关键是设计好每一个对象以及确定向这些对象发出的命令,使各对象完成相应的操作。 )
  • 在c++中对象的类型称为类,类代表了某一批对象的共性和特征。类是对象的抽象,而对象是类的具体实例。
  • 对象的定义格式:
  •           <存储类型>类名 对象1,对象2 《,......》;
  • 在建立对象时,只为对象分配用于保存的数据成员的内存空间,而成员函数的代码为该类的每一个对象所共享。
  • 对象的使用:一个对象的成员就是该对象的类所定义的成员,有成员数据和成员函数,引用时同结构体变量类似,用“.”运算符。(PS:用成员选择运算符“.”只能访问对象的公有成员,而不能访问对象的私有成员或保护成员。若要访问对象的私有的数据成员,只能通过对象的公有成员函数来获取。 )
  • 同类型的对象之间可以整体赋值,这种赋值与对象的成员的访问权限无关。
  • 对象可以作函数的入口参数(实参、形参),也可以作函数的出口参数。这与一般变量作为函数的参数是完全相同的。
  • 一个类的对象,可作为另一个类的成员。
  • 类的成员函数,是对类中的数据的操作,同时作为外部数据成员的接口,代表了对象的行为。
        2.类

  • 类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的运算封装在一起的集合体。
class 类名
{
    private:
        成员数据;
        成员函数;
    public:
        成员数据;
        成员函数;
    protected:
        成员数据;
        成员函数;
};
        用private限定的成员为私有成员,私有成员限定在该类的内部使用。(只允许该类中的成员函数使用私有的成员数据,对于私有的成员函数,只能被该类内的成员函数调用;类就相当于私有成员的作用域。 )

        用public限定的成员为公有成员,公有成员的数据或函数,不受类的控制,可以在类内类外自由使用。对类而言是透明的。

        用protected限定的成员为保护成员,只允许在类内以及该类的派生类中使用保护的数据或函数。该类及该类的派生类是保护成员的作用域。

 私有成员公有成员保护成员
类内函数可以调用可以调用可以调用
类外函数不可调用可以调用不可调用
              (如果未加说明,类中默认的访问权限是private私有的,这个要和结构体类型进行区分,结构体中成员缺省的存取权限是公有的)

  • 定义类的注意事项:
                                 类具有封装性,并且类只是定义了一种结构(样板),所以类中的任何成员数据均不能使用关键字extern,auto或register限定其存储类型。

                                 在定义类时,只是定义了一种导出的数据类型,并不为类分配存储空间,所以,在定义类中的数据成员时,不能对其初始化。

  • 定义类(内联函数,也可称为内嵌函数):我们在类中直接定义函数体,这时成员函数在编译时是作为内联函数来实现的;我们也可以在类体内说明函数,类体外定义类的内联成员函数,在类外定义时,在成员函数的定义前面加上关键字inline。(PS:内联函数:类内定义的函数和inline限制的类外定义的函数)
//在c++中,函数调用需要建立栈环境,进行参数复制,保护调用现场,返回时,还要进行返回值复制,恢复调用现场。这些工作都是与完成特定任务的操作无关的额外
//开销。程序效率由于该项工作而受到影响,所以,流行的CPU都已经将函数调用的额外工作硬件化了,以次来减少运行开销。尽管如此,调用工作还是有一些微小的
//开销,如果频繁调用很少语句的小函数,则这些开销对性能的影响还不好说。
#include "stdafx.h"
#include "iostream"
using namespace std;
bool isnumber (char ch) 
{
	return ch>='0' && ch<='9' ? 1:0 ;
}
int _tmain(int argc, _TCHAR* argv[])
{
	char c;
	while(cin>>c && c!='\n')
		if(isnumber(c))
			cout<<"hello world.\n";
		else
			cout<<"good morning.\n";
	return 0;
}


//程序不断到输入设备中读取数据,频繁调用isnumber函数,isnumber是个小函数,所以函数调用的开销相对来说占的比重就大了。下面对程序进行更改:
#include "stdafx.h"
#include "iostream"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
	char c;
	while(cin>>c && c!='\n')
		if(c>='0' && c<='9' ? 1 : 0)
			cout<<"hello world.\n";
		else
			cout<<"good morning.\n";
	return 0;
}
//该程序在if语句中用表达式替换了函数调用。在程序运行上,因为失去了大量的函数调用开销,提高了执行效率。


#include "stdafx.h"
#include "iostream"
using namespace std;
inline bool isnumber(char); //内联声明
int _tmain(int argc, _TCHAR* argv[])
{
	char c;
	while(cin>>c && c!='\n')
		if(isnumber(c))
			cout<<"hello world.\n";
		else
			cout<<"good morning.\n";
	return 0;
}
bool isnumber(char ch)
{
	return ch>='0' && ch<='9' ? 1 : 0;
}
//由于isnumber函数比相应的表达式可读性好,所以若程序中多处出现isnumber,而又讲其替换为复杂的实现语句的话,就会降低程序的可读性。
//我们既要用函数用来体现其结构化和可读性,又要使效率尽可能地高。解决办法就是将这种小函数声明为内联。


        内联函数体应该尽可能小,且要结构简单,这样嵌入的代码才不会影响调用函数的主体结构。所以在内联函数中,不能含有复杂的结构控制语句,如switch和while。如果内联函数有这些语句,则编译器将无视内联声明,只是视同普通函数那样产生调用代码。当然递归函数属于结构复杂的函数,也不能用来做内联函数。(PS:正因为函数调用有开销,而内联函数调用几乎没有调用开销,所以编程时就应尽可能内联函数调用。)

        3.类类型

  •  可以定义类类型的指针,类类型的引用,对象数组,指向类类型的指针数组和指向一维或多维数组的指针变量
        类体的区域称为类作用域。类的成员函数与成员数据,其作用域都是属于类的作用域,仅在该类的范围内有效,故不能在主函数中直接通过函数名和成员名来调用函数。
        类类型的作用域:在函数定义之外定义的类,其类名的作用域为 文件作用域 ;而在函数体内定义的类,其类名的作用域为 块作用域
        对象的作用域与前面介绍的变量作用域完全相同 , 全局对象、局部对象、局部静态对象等。
  • 类的嵌套(类是允许嵌套的)
        在定义一个类时, 在其类体中又包含了一个类的完整定义,称为类的嵌套 。
  • 类的对象如何引用私有数据成员:
        1.通过公有函数为私有成员赋值。

        2.利用指针访问私有数据成员。

        3.利用函数访问私有数据成员。

        4.利用引用访问私有数据成员。

//1.通过公有函数为私有函数赋值
#include "stdafx.h"
#include "iostream"
using namespace std;
class Test
{
private:
	int x,y;
public:
	void Setxy (int a, int b)
	{
		x=a;
		y=b;
	}
	void Printxy ()
	{
		cout<<"x="<<x<<",y="<<y<<endl;
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	Test t1,t2;
	t1.Setxy (6,8);
	t1.Printxy ();
	return 0;
}

//2.利用指针访问私有数据成员
#include "stdafx.h"
#include "iostream"
using namespace std;
class Test
{
private:
	int x,y;
public:
	void Setxy (int a, int b)
	{
		x=a;
		y=b;
	}
	void Getxy (int *px, int *py)
	{
		*px=x;
		*py=y;
	}
	void Printxy ()
	{
		cout<<"x="<<x<<",y="<<y<<endl;
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	Test t1;
	t1.Setxy (6,8);
	int a,b;
	t1.Getxy(&a,&b);
	t1.Printxy ();
	return 0;
}

//3.利用函数访问私有数据成员
#include "stdafx.h"
#include "iostream"
using namespace std;
class Test
{
private:
	int x,y;
public:
	void Setxy (int a, int b)
	{
		x=a;
		y=b;
	}
	int Getx ()
	{
		return x;
	}
	int Gety ()
	{
		return y;
	}
	void Printxy ()
	{
		cout<<"x="<<x<<",y="<<y<<endl;
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	Test t1;
	t1.Setxy (6,8);
	int a,b;
	t1.Getx();
	t1.Gety();
	t1.Printxy ();
	return 0;
}

//4.利用引用访问私有数据成员
#include "stdafx.h"
#include "iostream"
using namespace std;
class Test
{
private:
	int x,y;
public:
	void Setxy (int a, int b)
	{
		x=a;
		y=b;
	}
	void Getxy (int &px, int &py)
	{
		px=x;
		py=y;
	}
	void Printxy ()
	{
		cout<<"x="<<x<<",y="<<y<<endl;
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	Test t1;
	t1.Setxy (6,8);
	int a,b;
	t1.Getxy(a,b);
	t1.Printxy ();
	return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值