面向对象程序设计C++学习笔记(2)

第三、四周学习笔记:

第二章 类和对象的特性

2.1.1 什么是面向对象程序设计
1、对象 客观世界中任何一个事物都可以看成一个对象。任何一个对象都应该具有属性(attribute)和行为(behavior)。对象应能根据外界给的消息进行相应的操作。
2、封装与信息隐蔽 把对象的内部实现与外界行为分隔开。
3、抽象 其作用是表示同一类事物的本质。类是对象的抽象,而对象是类的具体表现。
4、继承与重用 在原有类的基础上添加一些属性和行为,建立一个新类。
5、多态性 多态性是指由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应。多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。

2.1.2 面向对象程序设计的特点 面向的是一个个对象,程序设计者的任务包括两个方面:一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起;二是考虑怎样向有关对象发送消息,以完成所需的任务。

2.1.3 类和对象的作用 类是所有面向对象的语言的共同特征。程序中的一组数据和一组操作是相对应的,人们把相应的数据和操作放在一起,形成一个整体,与外界相分割。这就是面向对象程序设计中的对象。

在面向过程的结构化程序设计中 程序=算法+数据结构;面向对象程序设计中:对象=算法+数据结构 程序=对象s+消息
2.1.4 面向对象的软件开发
1.面向对象分析(object oriented analysis,OOA)
2.面向对象设计(object oriented design,OOD)
3.面向对象编程(object oriented programing,OOP)
4.面向对象测试(object oriented test,OOT)
5.面向对象维护(object oriented soft maintenance,OOSM)

2.2类的声明和对象的定义

1、声明类的类型
class 类名
{ private:
私有的数据和成员函数;
public:
公有的数据和成员函数;
};//还有一
种protected受保护的成员,不能被类外访问,但可以被其派生类的成员函数访问。
注意:如果在类的定义中没有指定是private还是public,系统默认为private。

2、定义对象的方法
a、先声明类类型,然后在定义对象。
class 类名 对象名;或者类名 对象名;
b、在声明类的同时定义对象。
c、不出现类名,直接定义对象。

3、类和结构体类型的异同
可以将class声明的类类型改成struct,但是如果对其成员不做private和public声明,用struct声明的类系统默认为public,而class声明的类其成员为private。

2.3 类的成员函数
将需要被外界调用的成员函数指定为public,它们是类的对外接口。
既可以在类里面定义成员函数,也可以在类体中只对成员函数做声明,在类外定义。
class Student
{public:
void display();//声明
private:
int num;
}
void Student::display()
{cout<<“num:”<<num<<endl;
}
Student stud1;
**注意:如果在作用域运算符“::”**的前面没有类名,或者函数名前面既无类名又没有作用域运算符,则表示该display函数是全局函数,即一般函数,不是成员函数。
**a、内置成员函数(inline函数)**当成员函数规模很小时
在程序调用内置成员函数时,并不是真正地执行调用过程(如保留返回地址等处理),而是把函数代码嵌入程序的调入点。
C++要求一般的内置函数要用关键字inline声明,但对类内定义的成员函数,可以省略inline。inline void Student::display(){ }
b、成员函数的存储方式
同一类的不同对象中的数据成员的值一般是不相同的,而不同对象的函数的代码是相同的,不论调用哪一个对象的函数的代码,其实调用的都是同样内容的代码。
(1)不论成员函数在类外定义还是类内定义,都不占用对象的存储空间;
(2)不论是否用inline声明,成员函数的代码段都不占用对象的存储空间,inline函数只影响程序的执行效率而与成员函数是否占用对象的存储空间无关,它们不属于同一范畴。
(3)虽然成员函数并没有放在对象的存储空间中,但从逻辑的角度,成员函数是和数据一起封装在一个对象中的,只允许本对象中成员的函数访问同一对象的私有数据。
2.4 对象成员的引用
访问对象中的成员有三种方式:
(1)通过对象名和成员运算符访问,“.”。
(2)通过指向对象的指针访问,“->”。
(3)通过对象的引用访问。

第三章 怎样使用类和对象

3.1 利用构造函数对类对象进行初始化
构造函数是一种特殊的成员函数,不需要用户来调度它,而是在建立对象时自动执行。
构造函数与类同名,无返回值类型,形式:构造函数名(类型1 形参1,类型2 形参2,…);~~~~定义对象时的一般格式:类名 对象名(实参1,实参2,…);
需要说明:
(1)构造函数没有返回值,它的作用只是对对象进行初始化。
(2)构造函数不需用户调用,也不能被用户调用
(3)可以用一个类对象初始化另一个类对象。
Time t1; Time t2=t1;//把t1
的数据拷贝到t2,而不调用构造函数。
(4)构造函数函数体中不仅可以对数据成员赋初值,而且可以包含其他语句,如cout语句,但是不提倡,以保持程序的清晰。
(5)如果用户自己没有定义构造函数,则C++系统会自动生成一个空的构造函数。
用参数初始化表对数据成员初始化
一般形式:类名::构造函数名([参数表])[:成员初始化表] {[函数构造体]}
如Box::Box(int h,int w,int len):height(h),width(w),length(len){ }
构造函数可以重载;
构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值;(在声明构造函数时指定默认值,声明时形参名可以省略,即Box(int = 10,int = 10,int = 10);

3.2利用析构函数进行清理工作
程序何时会调用析构函数:
(1)如果在一个函数中定义了一个对象(假设是自动全局对象),当函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
(2)静态(static)局部对象在函数调用结束时对象并不释放,不调用析构函数,只在main函数结束或调用exit函数结束程序时,才析构。
(3)如果定义了一个全局的对象,则在程序的流程离开其作用域时,调用该全局的对象的析构函数。
(4)如果用new运算符动态地建立了一个对象,当用delete运算符释放对象时,先调用该对象的析构函数。
析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。
析构函数不返回任何值,没有函数类型和参数,不能被重载。一个类可以有多个构造函数,但只有一个析构函数。
形式:~Box() { }

3.3 调用构造函数和析构函数的顺序
先构造的后析构,后构造的先析构。
(1)如果在全局范围定义对象,那么其构造函数在本文件模块中的所以函数(包括main函数)执行之前调用。但如果一个程序包含多个文件,而在不同的文件中都定义了全局对象,那么执行顺序是不确定的。
(2)如果是局部自动对象,那么在每次建立对象时都要调用,调用结束时先析构。
(3)如果是静态局部对象,那么只在第一次调用此函数定义对象时调用构造函数一次,main函数结束或调用exit函数结束程序时,才调用析构函数。

3.4 对象数组
对象数组的每一个元素都是同类的对象。
建立数组时,同样要调用构造函数:如果构造函数只有一个参数,在定义的同时可以提供实参,如果构造函数有多个,则不能在定义时直接提供所有实参,可以这样定义:Student Stud[2]={Student(1001,18,87),Student(1002,19,79)}

3.5 对象指针
(1)指向对象的指针
一个对象存储空间的起始地址就是对象的指针。可以定义一个指针变量,用来存放对象的地址,这就是指向对象的指针变量。
定义:类名 **对象指针名;
(2)指向对象成员的指针
存放对象成员地址的指针变量。
指向对象数据成员的指针:数据类型名 *指针变量名;
指向对象成员函数的指针:
定义指向普通函数的指针变量方法:类型名(*指针变量名)(参数表列);如void (*p)();
定义指向对象成员函数的指针变量方法:数据类型名(类名::*指针变量名)(参数表列);如void(Time::*p2);
使指针变量指向一个公用成员函数的一般形式:指针变量名=& 类名::成员函数名;
(3)指向当前对象的this指针
在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this,它是指向本类对象的指针,它的值是当前调用的成员函数所在对象的起始地址。
所谓调用对象a的成员函数f,实际上是在调用成员函数f时使this指针指向对象a,从而访问对象a的成员。

3.6 共用数据的保护
(1)常对象
类名 const 对象名[(实参表)]; 或者 const 类名 对象名[(实参表)];
常对象必须要有初值,在该对象的生命周期中,该对象的所有数据成员的值都不能被修改。说明:
a、如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数,不能调用其普通成员函数。
b、常成员函数可以访问常对象中的 数据成员,但是不允许修改其值。
如果一定要修改常对象中某个数据成员的值,对该数据成员声明为mutable,如mutable int count;
(2)常对象成员
a、常数据成员 只能通过构造函数的参数初始化表对常数据成员进行初始化,任何其他函数都不能对常数据成员赋值。
b、常成员函数 如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们。如void get_time() const; 声明常成员函数的一般格式为 类型名 函数名(参数表) const(const是函数类型的一部分,在声明和定义函数时都要有const关键字,调用时不必加。
(3)指向对象的常指针
类名 * const 指针变量名;(指向对象的常指针变量的值不能改变,即始终指向同一个对象,但可以改变该对象的值)
(4)指向常对象的指针变量
const 类型名 *指针变量名;
指向常变量的指针变量:
a、如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的指针变量去指向它。
b、指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量。由此可知,指向常变量(对象)的指针变量可以指向一个非const变量(对象),通过指针变量访问该变量(对象)时,不能改变该变量(对象)的值,但是指针变量本身的值是可以改变的,即可以指向另一个变量(对象)。
c、如果函数的形参是指向普通变量的指针变量,实参只能是指向普通变量的指针。而不能用指向const变量的指针。
指向常对象的指针变量:
a、b同上,另:指向常对象的指针最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改。
(5)对象的常引用
在C++面向对象程序设计中,经常用常指针和常引用作函数参数。这样既能保证数据安全,使数据不能被随意修改,在调用函数时又不必建立实参的拷贝。
(6)const型数据小结
const 型数据的小结

3.7 对象的动态建立和释放
即用new运算符动态地分配内存,用delete运算符释放这些内存空间。
如果已经定义了一个Box类,可以这样动态地建立一个对象:*new Box;*执行此语句时,系统开辟一段内存空间,空间中存放一个Box类对象,同时调用其构造函数,但是此时用户无法访问这个对象,因为它既没有对象名,用户也不知道它的地址,称为无名对象,确实存在但是没有名字。
用new运算符动态的分配内存以后,将返回一个指向新对象的指针,即所分配的内存空间的起始地址。(如果只定义一个指向对象的指针,系统是不会给这个指针分配内存空间的,也不会调用对象的构造函数。)
C++允许在执行new时,对新建立的对象进行初始化。如:Box * pt=new Box(12,15,18);也可以先定义指针变量,再用new建立新对象。
在执行new运算时,如果内存量不足,无法开辟所需的内存空间,目前大多数C++编译系统都使new返回一个0指针值(NULL)。

3.8 对象的赋值和复制
(1)对象的赋值对象名1=对象名2;(同一个类)
说明:
a、对象的赋值只是对其中的数据成员的赋值,而不对成员函数赋值。
b、类的数据成员不能包括动态分配的数据,否则赋值时可能出现严重后果。
(2)对象的复制类名 对象2(对象1);
在建立对象时调用一个特殊的构造函数——复制构造函数(copy constructor)。
//The copy constructor definition
Box::Box(const Box& b)
{height=b.hright;
width=b.width;
length=b.length;
}
复制构造函数也是构造函数,但是它只有一个参数,这个参数是本类的对象(只能是本类),而且采用对象的引用的形式(一般约定)。
C++还提供另一种方便用户的复制形式:类名 对象名1=对象名2; 还可以在一个语句中对多个对象复制,如Box box2=box1,box3=box2;
对比普通构造函数和复制构造函数:
a、形式上:
类名(参数表列); //普通构造函数的声明
类名(类名 &对象名); //复制构造函数的声明
b、在建立对象时实参类型不同,系统会根据实参的类型决定调用哪个构造函数。
c、调用情况:
普通构造函数在程序中建立对象时被调用。
复制构造函数在用已有对象复制一个新对象时被调用。

3.9 静态成员
(1)静态数据成员:以关键字static开头。为各对象所共有,所有对象都可以引用它,在内存中只占一份空间(即使有多个对象)。而且只要在类中指定了静态数据成员,即使不定义对象,也为静态数据成员分配空间,它可以被引用。
a、静态数据成员是在程序编译时被分配空间的,到程序结束时才释放空间,不随对象的建立或撤销而分配空间或释放。
b、静态数据成员可以初始化,但只能在类体外进行初始化。其一般形式:数据类型 类名::静态数据成员名=初值; (在类体中声明静态数据成员时加static,不必在初始化语句中加static)。
c、在类外可以通过对象名引用公用的静态数据成员,也可以通过类名引用静态数据成员。(::运算符引用)
(2)静态成员函数:static int volume();
和静态数据成员一样,静态成员函数是类的一部分而不是对象的一部分。如果要在类外调用公用的静态成员函数,可以用类名和域运算符“::”,也可以通过对象调用。
静态成员函数与非静态成员函数的根本区别是:静态成员函数没有this指针由此决定了静态成员函数不能访问本类中的非静态成员公用的成员函数可以引用类中的静态数据成员

3.10 友元
友元可以访问与其有好友关系的类中的私有成员。
(1)友元函数:用friend声明。(只在声明时加friend)
friend函数不仅可以是一般函数(非成员函数),而且可以是另一个类中的成员函数。
在调用友元函数中访问有关类的私有数据时注意:
被声明为友元函数的成员函数的实参是声明其为友元的类的对象,且引用私有数据时必须加上对象名。
(2)友元类:friend 类名;(友元关系是单向的而不是双向的且不能传递)。

3.11 类模板
声明类模板时要在开头增加一行:template <class 类型参数名1,…>
类型参数名是可以任意取的,只要是合法的标识符即可。
怎样使用:
类模板名 <实际类型名> 对象名(参数表);
如果在类模板外定义成员函数:
函数类型 类模板名 <虚拟类型参数>::成员函数名(函数形参表){…}

课本:《C++面向对象程序设计(第2版)》 谭浩强 编著

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值