文章目录
二、面向对象的基本概念
1、结构化程序设计
- 在结构化程序设计中,采用自顶向下、逐步求精及模块化的思想,将复杂的大问题层层分解为许多简单的小问题。
- 在编写程序时,使用3种基本控制结构来构造程序。可以说,程序基本上都含有顺序、选择、循环3种基本控制结构,这3种结构到目前为止仍是主要的控制结构。程序以控制结构为单位,只有一个入口和一个出口,基于控制结构可以从前往后地顺序阅读程序,程序的静态描述与执行时的控制流程容易对应,所以可以独立地理解各个部分。结构化程序设计主要强调的是程序的易读性。
2、面向对象程序设计的概念和特点
- 所谓面向对象的程序设计方法,就是使分析、设计和实现一个系统的方法尽可能地接近人们认识一个系统的方法。通常包括3个方面:面向对象的分析、面向对象的设计和面向对象的程序设计。
- 面向对象技术把问题看成是相互作用的事物的集合,也就是对象的集合。对象具有两个特性:一是状态;二是行为。
- 状态是指对象本身的信息,也称为属性;
- 行为是对对象的操作。通过对事物的抽象找出同一类对象的共同属性(静态特征)和行为(动态特征),从而得到类的概念。对象是类的一个具象,类是对象的一个抽象。
- 面向对象的程序设计有“抽象” “封装” “继承”和“多态”4个基本特点。
- 抽象:对象是系统中用来描述客观事物的一个实体,如各位员工是员工类的一个个对象。对象的特点包括两个方面:属性和操作。属性指的是描述对象静态特征的数据项,如员工的姓名、职位、薪水等,可以用变量来表示;操作指的是描述对象动态特征(即行为)的函数序列,也称为方法或服务,如员工可以请假、加班,员工还可以获得提拔、加薪等。
- 封装:在C++中,通过用户定义的类来支持数据封装和信息隐藏。
- 继承:在C++现有类的基础上可以声明新的类,将一个已有类中的数据和函数保留,并加上自己特殊的数据和函数,从而构成一个新类,这就是继承和复用的思想。原来的类是基类,也称为父类或超类。新类是派生类,也称为子类。
- 多态:多态是指不同种类的对象都具有名称相同的行为,而具体行为的实现方式却有所不同。在一个类或多个类中,可以让多个方法使用同一个名字,从而具有多态性。这是通过函数重载及运算符重载实现的多态。
- C++中使用对象名、属性和操作三要素来描述对象。
3、类的初步知识
C++的基本数据类型 | 名称 |
---|---|
bool | 布尔型 |
char | 字符型 |
int | 整型 |
float | 浮点型 |
double | 双精度浮点型 |
(1)类的定义
- 类中的成员按功能划分,包括成员变量和成员函数;按访问权限划分,包括公有成员、私有成员和保护成员。
- 在C++中还可以定义不是任何类的成员的函数,这样的函数可称为“全局函数”。
- 成员函数既可以在类体内定义,也可以在类体外定义。如果成员函数定义在类体内部,则默认是内联函数。也可以在类体内部声明函数,并加上inline关键字,然后在类体外给出函数定义,这样的成员函数也是内联函数。
名称 | 描述 | 代表 |
---|---|---|
成员变量 | 是类中的一类成员,个数不限,也称为数据成员。成员变量的声明方式与普通变量的声明相同。 | 代表对象的“属性”。 |
成员函数 | 是类中的另一类成员,个数不限,其声明方式与普通函数的声明相同。 | 代表对该类对象所含数据进行操作的方法。 |
- 标识符命名规则:字母、数字和下划线的组合,大小写敏感,但不能以数字开头,也不能和系统中使用的关键字完全相同。
- 类是具有唯一标识符的实体,就是说类名不能重复。类定义以“;”结束,大括号中的部分称为类体。
- 定义类时系统并不为类分配存储空间,而只是把类看作是一种模板或样板。或者说,类可以看作是用户自定义的一种数据类型。在C++98标准下,类中声明的任何成员不能使用auto、extern和register关键字进行修饰。
- 如果成员函数定义在类体外,则类体内必须要有函数原型,类体外函数定义的前面必须用“类名::”来限定,格式如下:
返回值类型 类名::成员函数名(参数列表)
{
成员函数的函数体
}
- 类名是成员函数所属类的名字,符号::是类作用域运算符,表明它后面的成员函数是属于类名标识的这个类的。返回值类型就是这个成员函数返回值的类型。
- 类C中不能定义类C的成员变量,但可以定义类C的指针和引用。
(2)类的定义示例
#include <iostream>
#include <string>
using namespace std;
class myDate
{
public:
//构造函数
myDate();
myDate(int,int,int);
//类内声明成员函数
void setDate(int,int,int); //设置日期
void setDate(myDate); //设置日期
void setYear(int); //设置年
myDate getDate(); //获取日期
int getMonth(); //获取月
//注:const修饰为常函数,在常函数不允许出现赋值操作。
void printDate()const; //打印日期
private:
int year,month,day;
};
//在类体外定义成员函数
myDate::myDate()
{
year=1998,month=7,day=3;
}
myDate::myDate(int year,int month,int day)
{
this->year=year;this->month=month;this->day=day;
}
void myDate::setDate(int year,int month,int day)
{
this->year=year,this->month=month,this->day=day;
return;
}
void myDate::setDate(myDate oneDate)
{
year=oneDate.year,month=oneDate.month,day=oneDate.day;
return;
}
myDate myDate::getDate()
{
return *this;
}
void myDate::setYear(int y)
{
year=y;
return;
}
int myDate::getMonth()
{
return month;
}
void myDate::printDate() const
{
cout<<year<<"/"<<month<<"/"<<day<<endl;
return;
}
class Student
{
public:
//在Student类中声明成员函数
void setStudent(string,myDate); //设置学生信息
void setName(string name); //设置姓名
void setBirthday(myDate); //设置生日
void printStudent() const; //打印信息
string getName(); //获取姓名
myDate getBirthday(); //获取生日
private:
string name;
//封闭类:类中含有其他类中的成员
myDate birthday;
};
//在类外定义成员函数
void Student::setStudent(string name,myDate birthday)
{
this->name=name;
this->birthday.setDate(birthday);
}
void Student::setName(string name)
{
this->name=name;
}
void Student::setBirthday(myDate cintMyDate)
{
this->birthday.setDate(cintMyDate);
}
string Student::getName()
{
return name;
}
myDate Student::getBirthday()
{
return birthday;
}
void Student::printStudent() const
{
cout<<"姓名:"<<name<<"\t 生日:";
birthday.printDate();
cout<<endl;
}
int main()
{
Student myStudent;
int year,month,day;
string name;
// ""中的 '\'为转义符
cout<<"请输入学生的姓名和生日,生日以\"年月日\"的次序输入:"<<endl;
cin>>name>>year>>month>>day;
myStudent.setStudent(name,myDate(year,month,day));
myStudent.printStudent();
return 0;
};
4、类的示例程序剖析
(1)程序结构
- 一个完整的C++程序包括以下几部分:
- —个主函数,可以调用其他函数,但不能被调用,也称为主程序。
- 用户定义的任意多个的类及全局函数。
- 全局说明。在所有函数和类定义之外的变量说明及函数原型。
- 注释。
- 头文件。
- 对于比较大的程序,根据主函数和各用户定义的类及全局函数的功能及相互关系,可以把类及全局函数划分为几个程序文件,包括.cpp文件和.h文件。.cpp文件是源程序文件,.h文件是头文件。
- 从逻辑关系上看,典型的C++程序的结构包括类的定义、类中成员函数的实现及主函数main。
(2)成员变量与成员函数的定义
实现成员函数时要指明类的名称,在类体外定义的一般格式如下:
返回值类型 类名::函数成员名(参数表)
{
函数体
}
- 成员函数并非每个对象各自存有一份。成员函数和普通函数一样,在内存中只有一份,它可以作用于不同的对象,为类中各对象共享。
通常,因为函数体代码较长,所以在类体内仅给出成员函数的原型,然后在
类体外给出对应的函数体。如果函数体定义在类体内,则系统将其视为内联函数。类中定义的成员函数允许重载。
(3)创建类对象的基本形式
方法一的基本格式如下:
类名 对象名;
或是
类名 对象名(参数);
或是
类名 对象名=类名(参数);
可以扩展为多个对象,如下所示:
类名 对象名1, 对象名2, …;
或是
类名 对象名1(参数1), 对象名2(参数2), …;
方法二的基本格式如下:
类名 *对象指针名 = new 类名;
或是
类名 *对象指针名 = new 类名();
或是
类名 *对象指针名 = new 类名(参数);
用new创建对象时返回的是一个对象指针,这个指针指向本类刚创建的这个对象。C++分配给指针的仅仅是存储指针值的空间,而对象所占用的空间分配在堆上。使用new创建的对象,必须用delete来撤销。
与基本数据类型一样,还可以声明对象的引用、对象的指针及对象的数组。
声明对象引用,即变量别名的基本格式如下:
类名 &对象引用名 = 对象;
声明对象指针,即指向对象的指针的基本格式如下:
类名 *对象指针名 = 对象的地址;
声明对象数组的格式如下:
类名 对象数组名[数组大小];
同类型的对象之间可以相互赋值。对象和对象指针都可以用作函数参数。函数的返回值可以是对象或指向对象的指针。
//例如,定义了类C后,可以有如下的声明:
C a1,b1; //定义了C类的对象a1和b1
C *p = &a1; //定义了指向对象a1的C类类型的指针p
C &R = b1; //定义了C类类型对象b1的引用R
C A[3]; //定义了C类类型对象的数组A,含3个元素
5、访问对象的成员
定义了类和对象后,就可以访问对象的成员。通过对象访问成员变量的一般格式如下:
对象名.成员变量名
调用成员函数的一般格式如下:
对象名.成员函数名(参数表)
(1)使用对象访问成员变量与调用成员函数
使用点操作符,即“对象.成员名”。
(2)使用指针访问对象的成员
除了“对象名.成员名”的格式外,还可以使用指针或引用的方式来访问类成员。如果是通过指针访问成员变量,则点运算符.换为箭头运算符->,即使用“指针->成员名”的方式来访问对象的成员。
int main( )
{
Student myStudent(“张三”,myDate(2002,6,7));
Student *refStudent = &myStudent;
refStudent ->printStudent();
}
(3)使用引用访问对象的成员
访问成员时仍使用点操作符,即“引用名.成员名”。
6、类成员的可访问范围
(1)类成员的可访问范围
访问范围说明符 | 含义 | 作用 |
---|---|---|
public | 公有的 | 使用它修饰的类的成员可以在程序的任何地方被访问。 |
private | 私有的 | 使用它修饰的类的成员仅能在本类内被访问 |
protected | 保护的 | 它的作用介于public和private之间,使用它修饰的类的成员能在本类内及子类中访问。 |
- 私有类型的成员在类外是不能访问的,通过公有函数访问的效率比直接访问的效率要低。为了权衡这两方面的因素,C++提供了友元访问方式。只有在类内和在友元函数内才可以访问私有成员。
- 类中定义的成员默认访问属性为private。
(2)成员的访问
(3)隐藏的作用
- 设置私有成员的机制叫作“隐藏”。“隐藏”的一个目的就是强制对私有成员变量的访问一定要通过公有成员函数进行。
- 这样做的好处是:如果以后修改了成员变量的类型等属性,只需要更改成员函数即可;否则,所有直接访问成员变量的语句都需要修改。
7、标识符的作用域与可见性
- 标识符是组成程序的最小成分之一。类名、函数名、变量名、常量名和枚举类型的取值等都是标识符。这些标识符有各自的作用域和可见性。
- 标识符的作用域是指标识符的有效范围,即它在程序中的存在区域。标识符的可见性是指在程序的哪个区域里可以使用。对于同一个标识符来说,这两个区域可能是不完全重合的。
- C++中标识符的作用域有:函数原型作用域、局部作用域(块作用域)、类作用域和命名空间作用域。
(1)函数原型作用域
- 在声明函数原型时形参的作用范围就是函数原型作用域,这是C++程序中最小的作用域。例如,有如下的函数声明: double area(double radius)
- 标识符radius的作用范围就在函数area形参列表的左右括号之间,在程序的其他地方不能引用这个标识符。因为函数声明中形参仅在形参列表中有效,所以,函数声明中往往不写形参名,而仅写形参的类型。作用域。
(2)局部作用域(块作用域)
(3)类作用域
- 类可以被看成是一组有名字的成员的集合,类X的成员m具有类作用域,对m的访问方式有如下3种:
- ①如果在类X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说,m在这样的函数中起作用。
- ②在类外,可以通过表达式x.m或者X::m来访问,其中x是类X的对象。这正是程序中访问对象成员的最基本方法。当然,这样的访问不能违反m的访问修饰符的限定。
- ③在类外,可以通过ptr->m这样的表达式来访问,其中ptr为指向类X的一个对象的指针。当然,这样的访问不能违反m的访问修饰符的限定。
(4)命名空间作用域
定义命名空间的一般形式如下:
namespace 命名空间名
{
命名空间内的各种声明(函数声明、类声明、…)
}
在命名空间内部可以直接引用当前命名空间中声明的标识符,如果需要引用其他命名空间的标识符,需要使用下面的方式: 命名空间名::标识符名
例如,定义一个命名空间如下:
namespace SomeNs
{
class SomeClass{……};
someFunc(int a){ }
};
如果需要引用类名SomeClass或者函数名someFunc(),需要使用下面的方式:
SomeNs::SomeClass obj1; //声明一个SomeNS::SomeClass型的对象obj1
SomeNs::someFunc(5); //调用someFunc()函数
在标识符前面总要加上这样的命名空间限定会显得过于冗长,为了解决这一问题,C++又提供了using语句,using语句有两种形式:
using 命名空间名::标识符名;
using namespace 命名空间名;
作用域的隐藏规则
具有命名空间作用域的变量也称为全局变量。
对于在不同的作用域声明的标识符,可见性的一般原则如下:
- 标识符要声明在前,引用在后。
- 在同一个作用域中,不能声明同名的标识符。在没有互相包含关系的不同作用域中声明的同名标识符,互不影响。
- 如果存在两个或多个具有包含关系的作用域,外层声明了一个标识符,而内层没有再次声明同名标识符,那么外层标识符在内层仍然可见。
- 如果在内层声明了同名标识符,则外层标识符在内层不可见,这时称内层标识符隐藏了外层同名标识符,这种机制称为隐藏规则。