学习C++(四)

  面向对象的基础是数据和算法的封装,也就是类的实现

从结构到类

  结构化程序设计
    功能分解并逐步求精。当一些任务十分复杂以至无法描述时,可以将它拆分为一系列较小的功能部件,直到这些自完备。
在这里插入图片描述
  结构化程序设计的不足:
    结构化编程将数据和过程相分离;
    结构化编程对代码重用支持不够。
      可重用思想就是创建一些已知属性的组件,然后插入到自己的程序中。
    对于庞大、复杂的程序难以开发和维护
  程序越来越难以掌控。
  如果缺少了帮助,越来越难以掌握所有系统函数
  程序难以复用以前的版本
  编程不支持团队开发。每一个人必须了解程序中的所有地方,不能独立处理系统的一个方面。
  业务模型难以转换到程序模型
  独立运行良好,但集成的时候却不行。

  面向对象程序设计概念
  出发点:
    更直接地描述客观世界中存在的事物(对象)以及它们之间的关系。
  特点:
    是高级语言。
    将客观事物看作具有属性和行为的对象。
    通过抽象找出同一类对象的共同属性和行为,形成类。
    通过类的继承与多态实现代码重用
  优点:
    使程序能够比较直接地反问题域的本来面目,软件开发人员能够利用人类认识事物所采用的一般思维方法来进行软件开发。

  类构成了实现C++面向对象程序设计的基础。类是C++封装的基本单元,它把数据和函数封装在一起。当类的成员声明为保护时,外部不能访问;声明为公共时,则在任何地方都可以访问。
  C的结构可把相关联的数据元素组成一个单独的统一体
例如,下面的代码是一个存款结构:

struct Savings
{
      unsigned accountNumber;//帐号
      float balance;//结算额
}

任何程序只要定义了这个结构都可以访问它, 安全性不好.
类不但能包含数据,保护数据,还可以包含函数, 比如

class Savings
{
  public:
    usigned deposit(unsigned amount)  //成员函数
    {
     Balance+=amount;
     Return balance;
     }
  private:
     unsigned accountNumber;     //数据成员
     float balance;
  };

  关键字class表示类,Savings是类名,一般首字符用大写字母表示,以示与对象名的区别。
  关键字public和protected(或private)表示存储控制。
  在类中说明的,要么是数据成员,要么是成员函数。它们或者说明为public的,或者说明为protected的,或者说明为private的。
  类(class)定义中默认情况下的成员是private的,而结构(struct)定义中默认情况下的成员是public的。
  虽然结构可以达成和类基本相同的效果,但作为和传统C结构的区别,一般工程上尽量使用类

软件方法的发展必然

结构化程序设计方法

  较早的软件开发,用结构化程序设计方法。程序的定律是:
在这里插入图片描述
    程序=(算法)+(数据结构)
  即算法是一个独立的整体,数据结构(包含数据类型与数据)也是一个独立的整体。
  两者分开设计,以算法(函数或过程)为主。

面向对象方法

  后来发展成(算法+数据结构)
在这里插入图片描述
  即算法与数据结构是一个整体,算法总是离不开数据结构,算法含有对数据结构的访问,算法只能适用于特定的数据结构。因此设计一个算法适合于访问多个数据结构是不明智的,而且数据结构由多个算法来对其进行同种操作也是多余的。
  面向对象程序设计的基础,在面向对象中,算法与数据结构被捆绑成一个类,
  从这样的角度看问题,就不用为如何实现通盘的程序功能而费尽心机了。现实世界本身就是一个对象的世界,任何对象都具有一定的属性与操作,也就总能用数据结构与算法两者合一地来描述。这时候,程序定律被再次另眼相看:
     对象:(算法+数据结构)
     程序=(对象+对象+……)
  也就是说,程序就是许多对象在计算机中相继表现自己。而对象又是一个个程序实体。人们不再孤立静止地去看待数据结构了,而把它看成是一个程序单位,一个程序分子,或者一个对象的象征。它本身又包含有算法与数据结构。
在这里插入图片描述
  对象本身又包含有算法与数据结构。 不管程序结构怎么复杂,程序总是由数据结构和算法构成的, 也就是说可以由对象组成. 可能是几个对象叠加, 其中有些对象又是由几个对象组成的.

定义成员函数

  在类中定义成员函数
    类中定义的成员函数一般规模都比较小,语句只有1-5句,而且特别的switch语句不允许用。它们一般为内联函数,即使没有明确用inline标示。
    在C++中,类定义通常在头文件中,因此这些成员函数定义也伴随着进入头文件。
    我们知道函数声明一般在头文件,而函数定义不能在头文件,因为它们将被编译多次。如果是内联函数,包含在头文件中是允许的,因为内联函数在源程序中原地扩展,由于在类中定义的成员被默认为内联函数,所以就避免了不能被包含在头文件中的问题。
  将类拿来编制应用程序时,只需类的外部接口(头文件)。这和我们使用标准库函数的道理是一样的,即只需包含某函数声明的头文件。因为类定义中,全部包含了类成员函数的声明
  在类之后定义成员函数
    C++运行在其他地方定义成员函数
    将类定义和其他成员函数定义分开,是目前开发程序的通常做法
    我们把类定义(头文件)看成是类的外部接口,类的成员函数定义看成是类的内部实现。
  类成员函数的重载
    类的成员函数可以像普通函数一样重载
    但不同类即使有相同的函数名也不算重载

class Student
{
public:
	float grade(){
		//...
	}
	float grade(float newGPA){//类成员函数的重载
		//...
	}
}

调用成员函数

  一个对象要表现其行为,就要调用它的成员函数。调用成员函数的形式类似于访问一个结构对象的分量,先指明对象,再指明分量。它必须指定对象和成员名,否则无意义

  用指针来调用成员函数:

//Tdate为类
void someFunc(Tdate* ps)
{
	ps->Print();//ps是对象的指针
}

  用引用传递来访问成员函数
    用对象的引用来调用成员函数,看上去和使用对象自身的简单情况一样

void someFunc(Tdate& refs)
{
	refs.Print();//refs是对象的别名
}

  

成员权限

  保护成员
  类的成员可以声明为被保护的(protected),这时,除了友元和子类的成员函数之外,从类的外部就不能对它们访问,在类中设置保护屏障,不让外部访问,主要是由面向对象程序的目标决定的:
    相对于外部函数(普通函数,其他类的成员函数)而言,保护类的内部数据不被’肆意‘侵犯。使类对它本身内部实现的维护负责。 因为只有类自己才能访问该类的保护数据,所以一切对保护数据的维护只有靠类自己了。如果其他的类或函数能够自由访问该类的保护成员,那么,还让该类对它自己的内部实现负责是不公平的。如果某人打开了电视机外壳,改动了内部电路,使电视机遭损,要求厂家退换,厂家概不负责。但是,在使用外部按钮时,突然电视机发生故障,那厂家要负全面的责任,因为电视机是他们造的,内部电路的实现只有他们知道。限制类与外部世界的接口。把一个类分作两部分,一部分是公共的,另一部分是保护。保护成员对于使用者来说是不可见的,也是不需了解的。了解和使用一个具有有限接口(公共成员)的类是很容易的。电视机的按钮就寥寥几个,人们很容易学会使用,无须知道电视机的内部实现,照样可以将电视机用得好好的。

  私有成员
类的成员可以声明为私有的(protected),这时,从类的外部(指在普通函数或其它类的成员函数中)就不能对它们访问,连他的派生类也不能访问.

  公共成员
  也可以把成员声明为公共的,公共成员在任何地方都可以被访问。

封装

  类的封装的概念首先是,数据与算法(操作)结合,构成一个不可分割的整体(对象)。
  其次是,在这个整体生—些成员是保护它们被有效地屏蔽,以防外界的干扰和误操作。
  另一些成员是公共的,它们作为接口提供给外界使用。而对该对象的描述即是创建用户定义的类型,对c++来说,就是类的实现机制。

  类的作用域
    一个类的所有成员位于这个类的作用域内,一个类的任何成员都能访问同一类的任一其它成员。C++认为一个类的全部成员都是一个整体的相关部分
类作用域是类定义和相应类成员定义范里。在该范围内,一个类的成员函数对同一类的数据成员具有无限制的访问权。
    对类作用域外的一个类的数据成员和成员函数的访问受到程序员的控制。
  这种思想是要把一个类的数据结构和功能封装起来,从而使得在类的成员函数之外对类的数据进行访问是有限的。

  共享库的构成
    一个商业性类库包括一个类定义和成员函数定义。类定义以头文件的方式提供,成员函数定义则以一定的计算机硬件或操作系统为背景而编译实现的代码方式提供。
  C++程序结构
    一个c++应用程序是一个程序工程。一个c++程序工程文件中,应该组合下面这些程序
    文件:
    main.cpp // 包含主函数的程序文件
     class.cpp’s // 用户自定义类库的内部实现程序
    function.cpp’s // 用户自定义函数库的实现程序
    其中class.cpp’s表示多个类成员函数定义的源文件,function.cpp’s表示多个函数定义的源文件
    其中包含主函数的源文件应该是下面的形式:

#include<标准类库头文件>'s
#include<标准函数头文件>'s
#include<自定义类库头文件>'s
#include<自定义函数头文件>'s
函数原型's
全局数据定义's
void main()
{
	//...
}
函数定义's

  在这里,包含标准类库头文件,即称为类库重用。自定义类库头文件称为类库设计。
而在main()函数开始之后的面向对象程序设计,则显得相对自然和简捷。往往是先
定义若干对象,然后调用其成员函数,由成员函数来完成程序员所规定的操作
(即让对象表现自己)。

构造函数

  构造函数和析构函数的实现使C++的类机制得以充分的展示,使得对象能够轻松地被创建和撤销。
    构造函数创建类对象,初始化其成员。
    析构函数撤销类对象。
    构造函数和析构函数是类的特殊成员函数。

类和对象

  人类是一个类,你是人,我是人,都是人类的实例(instance),或称对象(object)。
  人类只有一个,人类的实例可以有无数个。
   对象可以被创建和销毁,但类是无所不在的。
    例如,桌子是一个类,人们不断打造各种尺寸和风格(属性)的桌子(桌子的实例),打造桌子,又不断毁坏桌子,年复一年,旧的去了,新的又来,但桌子的概念没变,它是一个抽象的概念。应该称它为桌子类,以区别于打造的具体桌子
  定义对象
  属于不同类的对象在不同的时刻、不同的地方分别被建立。
  全局对象在主函数开始执行前首先被建立,局部对象在程序执行遇到它们的对象定义时才被建立。
  与定义变量类似,定义对象时,C++为其分配空间。
    例如,下面的代码定义了两个类,创建了类的全局对象、局部对象、静态对象和堆对象:

class Desk//Desk类
{
public:
	int weight,height,width,length;
};
class Stool//另一个类:Stool类
{
public:
	int weight,height,width,length;
};
Desk da;//全局对象
Stool sa;
void fun()
{
	static Stool ss;//静态局部变量
	Desk da;//局部变量
}

  对象的初始化
    建立对象,须有一个有意义的初始值
    C++建立和初始化对象的过程专门有该类的构造函数完成。
    构造函数给对象分配空间和初始化
    如果一个类没有专门定义构造函数,那么C++就仅仅创建对象而不做任何初始化
  对象的撤销
    析构函数完成对象的撤销
    析构函数也是类的成员函数
    析构函数的作用是善后处理

  根据变量定义,全局变量和静态变量在定义(分配空间)时,将清0,局部变量在定义时,分配的内存空间内容保持原样,故为随机数。
  对象定义时,情况不一样。对象的意义表达了现实世界的实体,因此,一旦建立对象,须有一个有意义的初始值。c++建立和初始化对象的过程专门由该类的构造函数来完成。
  这个构造函数很特殊,只要对象建立,它马上被调用,给对象分配空间和初始化。
  例如一旦打造了一张桌子,桌子就应有长、宽、高和重量。因此,在桌子对象建立时,构造函数的任务是赋予一组值给该桌子对象。
  如果一个类设有专门定义构造函数,那么C++就仅仅创建对象而不做任何初始化。 C++另有一种析构函数,它也是类的成员函数,当对象撤消时,就会马上被调用,其作用是善后处理。
  例如,一张桌子要扔掉,须将桌子里面的东西拿出来,这些东西可能有用,不能随桌子一起扔。
  类似这些事就由析构函数来完成。

构造函数

  基于对象的多样化,我们要为类提供构造函数
  类的封装性,就体现在一部分数据是不能让外界访问的。所以直接在非成员函数中访问类对象的保护或私有数据是不允许的。
  类对象的初始化任务,自然就落在了类的成员函数身上,因为它们可以访问保护和私有数据成员。我们要求建立对象的同时,自动调用构造函数,省去人为调用的麻烦。也就是说,类对象的定义 Student ss;明确表达了为对象分配空间和初始化的意向。这些工作由构造函数来完成。
  类对象的定义“Student ss”涉及到一个类名和一个对象名。类只有一个名字,但对象名可以任意多,每个对象创建时,都要调用类的构造函数。
  类的唯一性和对象的多样性,使我们马上想到用类名而不是对象名来作为构造函数名是比较合适的。
  类名 == 构造函数名

class Desk
{
protected:
	int weight,height,width,length;
public:
	Desk()//构造函数定义
	{
		weight = 10;
		height = 11;
		width = 12;
		length = 13;
	}
};

  放在外部定义的构造函数,其函数名前要加上“类名::”,这和别的成员函数定义方法一样
  构造函数另一个特殊之处是它没有返回类型,函数体中也不允许返回值,但可以有无值返回语句“return;”。
  因为构造函数专门用于创建对象和为其初始化,所以它不能随意被调用。没有返回类型,正显得它与众不同
  如果建立一个对象数组Desk dd[5]; //对象数组dd,构造函数会被调用5次
  一个类定义中,类的数据成员可能为另一个类的对象

//下面的代码中表示在组合音响中包含各个套件类对象
class Recorder{
	//...
};
class Cdplayer{
	//...
};
class Amplifier{
	//...
};
class Tuner{
	//...
};
class HiFi{
/*如果一个类对象是另一个类的数据成员,
则在那个类的对象创建所调用的构造函数中,
对该成员(对象)自动调用其构造函数*/
protected:
	Recorder re;
	Cdpyayer cd;
	Amplifier am;
	Tuner tu;
public:
	//...
};

析构函数

  析构函数回收对象的资源
  一个类可能在构造函数里分配资源,这些资源需要在对象不复存在以前被释放。
例如,如果构造函数打开了一个文件,文件就需要被关闭。或者,如果构造函数从堆中分配了内存,这块内存在对象消失之前必须被释放。析构函数允许类自动完成这些清理工作,不必调用其他成员函救。
  析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载。只是在类对象生命期结束的时候.由系统自动调用。构造函数不同于析构函数,却可以有参数,可以重载。作为一个类,可能有许多对象,每当对象生命期结束时,都要调用析构函数,每个对象一次。
  这跟构造函数形成了鲜明的对立,所以析构函数名,就在构造函数名前加上一个逻辑非运算行“~”,表示“逆构造函数”。

//析构函数的例子
class XYZ
{
public:
	XYZ()
	{
		name = new char[20];//分配堆空间
	}	
	~XYZ()
	{
		delete name;//释放堆空间
	}
}

  XYZ类的构造函数中分配了一段堆内存给作为指针的name数据成员。
  一旦对象创建,该对象就在对象空间之外拥有了一段堆内存资源。
  对应地,当对象在撤消的时候,首先必须归还这~堆内存资源。
    当你进入图书馆阅览室借书阅览时, 你就成了一个阅览室的阅览人(对象),借什么书是由一进去就完成的(构造)。 当你要撤离阅览室(撤消对象)时,你必须先归还图书(析构)才能顺当地离去。
  析构函数以调用构造函数相反的顺序被调用。

默认构造函数和带参构造函数

默认构造函数

  (1)C++规定,每个类必须有一个构造函数,没有构造函数,就不能创建任何对象。
  (2)若未提供一个类的构造函数(一个都未提供)。则c++提供一个默认的构造函数,该默认构造函数是个无参构造函数,它仅负责创建对象。而不做任何初始化工作。
  (3)只要一个类定义了一个构造函数(不一定是无参构造函数),C++就不再提供默认的构造函数。也就是说,如果为类定义了一个带参数的构造函数,还想要无参构造函数,则必须自己定义
  (4)与变量定义类似,在用默认构造函数创建对象时。如果创建的是全局对象或静态对象,则对象的位模式全为O,否则,对象值是随机的。

带参构造函数

  应该运行构造函数带参,否则,往往程序员只能先将对象构造成千篇一律的对象值,甚至是一个随机对象,然后再调用一个初始化成员函数将数据存到该对象中去。
  带参的构造函数使初始化一步到位。

重载构造函数

  构造函数可以被重载,C++根据声明中的参数选择合适的构造函数

//重载构造函数例子
class Tdate{
protected:
	int month;
	int day;
	int year;
public:
	Tdate();
	Tdate(int month);
	Tdate(int month, int day);
	Tdate(int month, int day, int year);
};

构造对象过程

  在一个大程序中,各种作用域的对象很多,有些对象包含在别的对象里面,有些对象早在主函数开始运行之前就已经建立。创建对象的唯一途径是调用构造函数。构造函数是一段程序,所以构造对象的先后顺序不同,直接影响程序执行的先后顺序,导致不同的运行结果。
  C++给构造对象的顺序做了专门的规定
    1.局部和静态对象,以声明的顺序构造
    2.静态对象只被构造一次
    3.所有全局对象都在主函数main()之前被构造
    4.全局对象构造时无特殊顺序
    5.成员以其在类中声明的顺序构造

阶段总结

  因为对象不能像变量那样随便初始化,所以需要构造函数
  构造函数的名字与类名相同,不能有返回值
  构造函数可以带参数,默认提供无参的构造函数
  可以提供多个重载的构造函数,也可以默认参数
  用:类初始化嵌套类
  析构函数为了撤销对象、回收资源

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值