面向对象程序设计
- 面向对象的观点:自然界是由一组彼此相关并能相互通信的实体(对象)所组成
- 面向对象的程序设计方法:使用面向对象的观点来描述现实问题,然后用计算机语言来模仿并处理该问题
- 消息:调用对象的操作
- 类:对象的特征
- 基本的程序设计手段
- 抽象:复杂度控制
- 过程抽象
- 数据抽象:使用者只需知道对数据所能实施的操作以及这些操作之间的关系,而不必知道数据的具体表示
- 封装:信息隐蔽
- 过程封装
- 数据封装:把数据和操作作为整体描述,数据的访问只能通过封装体的对外接口来完成
- 模块化:组织大型程序
- 过程式:模糊
- 对象式:对象类
- 软件复用:缩短开发周期
- 过程式:子程序库
- 对象式:类库
- 可维护性:延长软件寿命
- 过程式:以功能为中心,强调过程抽象,数据与操作分离
- 对象式:以数据为中心,强调数据抽象,操作依附数据
- 软件模型的自然度:缩小解题空间与问题空间之间的语义间隙
- 抽象:复杂度控制
- 面向对象程序设计基本内容
- 对象/类
- 继承:在定义一个新类(派生类、子类)时,可以利用已有类(基类、父类)的一些特征描述
- 多态:某一论域中的一个元素存在多种解释
- 一名多用:函数名重载,操作重载
- 类属性:类属函数,类属类型
- 绑定:确定对多态元素的某个使用是多态元素的那一种形式
- 静态绑定:编译时刻确定
- 动态绑定:运行时刻确定
- 面向对象语言
- 面向对象是一种程序设计思想,用任何语言都可以实现
- 采用面向对象语言会使得面向对象程序设计更加容易,语言也能提供更多的面向对象保障
类和对象
- 数据成员
- 类定义时,声明数据成员
- 如果未见到相应的类型定义或相应的类型未定义完,则该数据成员的类型只能是这些类型的指针或引用类型
- 成员函数
- 类定义放在头文件(.h)中,类外定义的成员函数则放在实现文件(.cpp)中
- 可以重载
- 成员对象
class class_name
{
public://访问不受限制
void do_nothing(){}//定义,建议编译器按内联函数处理
void declare();//声明
private://只能在本类和友元的代码中访问
int num_1, num_2;
protected://只能在奔雷,友元和派生类的代码中访问
};
void class_name::declare(){}//定义
-
类:程序实体,一般存在域静态的程序汇总
-
对象:
- 程序运行时创建
- 执行是通过对象间相互发送消息实现
- 接收到一条消息后,调用对象类中定义的某个成员函数处理
-
对象的创建和标识
- 直接方式:与普通变量定义相同,对象在进入相应变量的生存期时创建,通过变量名来标识和访问。相应变量的生存期结束时,对象消亡
- 间接方式:
- 使用 new 创建,动态对象,内存空间在程序堆区
- 使用 delete 撤销
- 使用指针标识
-
new 对比 malloc 的优点
-
自动分配
-
自动返回指定类型指针
-
可以重载
//动态对象 Data *p; p = new Date; deleta p; //动态对象数组 Data *p p = new A[100];//new 创建的动态对象数组只能用默认的构造函数进行初始化 delete []p;
-
-
对象的操作
- 发送消息:. ->
- 同类赋值:= 数据成员递归赋值
- 取地址:&
-
this 指针
-
隐藏形参:
class_name *const this;
-
类定义中说明的数据成员对该类的每个对象都有一个拷贝
-
类中的成员函数对该类的每个对象只有一个拷贝
//成员函数的实际形式 void g(A *const this, int i){ this->x=i; } //成员函数调用 a.g(1); //编译程序编译 g(&a,1)
-
-
预编译宏
#ifndef _HEADERNAME_H #define _HEADERNAME_H #endif #pragma once
-
Law of Demeter:良好的面向对象程序风格
- 降低模块之间的耦合度:仅与你的直接朋友交谈
- 一个类的成员函数除了能访问自身类结构的直接子结构(本类的数据成员)外,不能以任何方式依赖于任何其它类的结构
- 并且每个成员函数只应对某个有限类集合中的对象发送消息
- 降低模块之间的耦合度:仅与你的直接朋友交谈
构造函数与析构函数
构造函数
- 可重载
- 默认构造函数:不带参数的构造函数
- 对象创建后不能再调用构造函数
A a1; //also A a1=A(); can't write A a1();
A a2(1); //also A a2 = A(1); or A a2 = 1;
A a3("abcd"); //also A a3("abcd") or a3 = "abcd";
A a[4]; //调用 a[0]~a[3] 默认构造函数
A b[5]={A(),A(1),A("abcd"),2,"xyz"};
A *p1 = new A;
A *p2 = new A(2);
A *p3 = new A("xyz");
-
成员初始化表
- 成员初始化表中成员初始化次序由它们在类定义中的说明次序来决定
- 如果类中定义了构造函数,则一定要在定义的所有构造函数的成员初始化表中对常量数据成员或引用数据成员进行初始化
- 否则不会生成默认构造函数,无法创建对象
class A{ const int y; int x; int &z; public: A(): z(x),y(1);{ x = 0; } }
-
成员对象初始化
- 隐式默认构造函数会调用成员对象的默认构造函数对成员对象进行构造
- 创建包含成员对象的类的对象时,先执行成员对象类的构造函数,再执行本身类的构造函数
- 一个类若包含多个成员对象,这些对象的初始化次序按它们在类中的说明次序(而不是成员初始化表的次序)进行
- 析构函数的执行次序与构造函数的执行次序正好相反
- 可使用成员初始化表
拷贝构造函数
A(const A& a);
- 隐式拷贝构造函数
- 对于普通成员:它采用通常的初始化操作
- 对于成员对象:则递归调用成员对象类的拷贝构造函数来实现成员对象的初始化
- 调用
- 使用同类定义对象时
- 把对象作为值参数传给函数时
- 把对象作为函数返回值时
- 自定义拷贝构造函数
class A
{
int x,y
char *p
public:
A(char *str){
x = 0; y = 0;
p = new char[strlen(str)+1];
strcpy(p,str);
}
~A() { delete [] p; p=NULL; }
};
A a1(“abcd”);
A a2(a1);//a1和a2的成员指针p指向同一块内存区域
//应显示定义一个拷贝构造函数
A::A(const A&a)
{
x=a.x;
y=a.y;
p = new char[strlen(a.p)+1];
strcpy(p, a.p);
}
析构函数
class A
{
public:
A(char *s){
len = strlen(s);
str = new char[len+1];
strcpy(str, s);
}
~()A{
delete[] str;
str = NULL; //无必要
}
}
- 析构函数的调用
- 显示调用:
a.~A();
只归还资源,对象未消亡 - 变量销毁前自动调用
- 显示调用:
成员修饰符
对常量对象的访问:const
- If a const and/or volatile keyword is next to a type specifier, it applies to the type specifier. Otherwise the const and/or volatile keyword applies to the pointer asterisk on its immediate left.
- 成员函数声明为 const 则不允许修改类的数据成员
const int a = 1; //also int const a = 1;
const int* c; //also int const* c;
int *const c;
const int r[] = {1,2,3,4};
const Time noon(12, 0, 0);
void A::do_nonthing() const {}
同类对象间的数据共享:static
- 类的静态数据成员对该类的所有对象只有一个拷贝
- 静态成员函数只能访问类的静态成员
- 作用
- 扩展生存期
- 限制作用域
- 唯一性
提高对对象私有数据的访问效率:friend
- 友元无对称性和传递性
- 在类A中使用 friend,则可以访问A中私有变量
- 友元函数
- 友元类
- 友元类成员函数