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