二、面向对象的基本概念

结构化程序设计

在结构化程序设计中,采用自顶向下逐步求精模块化的思想,将复杂的大问题层层分解为许多简单的小问题。

在编写程序时,使用3种基本控制结构来构造程序。可以说,程序基本上都含有顺序选择循环3种基本控制结构,这3种控制结构到目前为止仍然是重要的控制结构。程序以控制结构为单位,只有一个入口和一个出口,基于控制结构可以从前往后的顺序阅读程序,程序的静态描述与执行时的控制流程容易对应,所以可以独立的理解各个部分。结构化程序设计主要强调的是程序的易读性。

面向对象程序设计的概念和特点

概念

  • 所谓面向对象的程序设计方法,就是使分析、设计和实现一个系统的方法尽可能地接近人们认识一个系统的方法。通常包括3个方面:
    • 面向对象的分析
    • 面向对象的设计
    • 面向对象的程序设计
  • 面向对象技术把问题看成是相互作用的事物的集合,也就是对象的集合。对象具有两个特性:
    1. 状态:指对象本身的信息,也称为属性。
    2. 行为:对对象的操作。
  • 通过对事物的抽象,找出同一类对象的共同属性(静态特征)行为(动态特征),从而得到类的概念。
  • 对象是类的一个具象,类是对象的一个抽象。

特点

  • C++中使用对象名属性操作三要素来描述对象。
  • 面向对象的程序设计有抽象封装继承多态4个基本特点。
    • 抽象:对象是系统中用来描述客观事物的一个实体。对象的特点包括两个方面:属性和操作
      • 属性是指描述对象静态特征的数据项,可以用变量来表示
      • 操作是指描述对象动态特征的函数序列,也称为方法或服务。
    • 封装:在C++中,通过用户定义的来支持数据封装和信息隐藏。
    • 继承:在C++现有类的基础上可以声明新的类,将一个已有类中的数据和函数保留,并加上自己特殊的数据和函数,从而构成一个新类,这就是继承和复用的思想。原来的类是基类,也称为父类或超类。新类是派生类,也称为子类。
    • 多态:指不同种类的对象都具有名称相同的行为,而具体行为的实现方式却有所不同。在一个类或多个类中,可以让多个方法使用同一个名字,从而具有多态性。这是通过函数重载及运算符重载实现的多态。

类的初步知识

类的定义

  • 类中的成员
    按功能划分,包括成员变量成员函数
    按访问权限划分,包括公有成员私有成员保护成员
  • 在C++中还可以定义不是任何类的成员的函数,这样的函数可称为全局函数
  • 成员函数既可以在类体内定义,也可以在类体外定义。如果成员函数定义在类体内部,则默认是内联函数。也可以在类体内部声明函数,并加上inline关键字,然后在类体外给出函数定义,这样的成员函数也是内联函数。
名称 描述 代表
成员变量 是类中的一类成员,
个数不限,
也称为数据成员。
成员变量的声明方式与普通变量的声明相同。
代表对象的属性
成员函数 是类中的另一类成员,
个数不限,
其声明方式与普通函数的声明相同。
代表对该类对象所含数据
进行操作的方法。
  • 标识符的命名规则字母、数字和下划线****的组合,大小写敏感,但不能以数字开头,也不能和系统中使用的关键字完全相同。

  • 是具有唯一标识符的实体,就是说类名不能重复类定义以结束,大括号中的部分称为类体。

  • 定义类时系统并不为类分配存储空间,而只是把类看作是一种模板或样板。或者说,类可以看作是用户自定义的一种数据类型。在C++98标准下,类中声明的任何成员不能使用autoexternregister关键字进行修饰。

  • 如果成员函数定义在类体外,则类体内必须要有函数原型,类体外函数定义的前面必须用类名::来限定,格式如下:

    返回值类型 类名::成员函数名(参数列表) {
        成员函数的函数体
    }
    
  • 类名是成员函数所属类的名字,符号::类作用域运算符表明它后面的成员函数是属于类名标识的这个类的。返回值类型就是这个成员函数返回值的类型。

  • 类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 受保护的 介于publicprivate之间,
使用它修饰的类的成员
能在本类内及子类中被访问

隐藏的作用

设置私有成员的机制叫做隐藏隐藏的一个目的就是强制对私有成员变量的访问一定要通过公有成员函数进行。这样做的好处是:如果以后修改了成员变量的类型等属性,只需要更改成员函数即可;否则,所有直接访问成员变量的语句都需要修改。

标识符的作用域与可见性

  • 标识符是组成程序的最小成分之一。类名、函数名、变量名、常量名和枚举类型的取值等都是标识符。这些标识符有各自的作用域和可见性。标识符的作用域是指标识符的有效范围,即它在程序中的存在区域。标识符的可见性是指在程序的哪个区域里可以使用。对于同一个标识符来说,这两个区域可能是不完全重合的。
  • C++中标识符的作用域有:函数原型作用域、局部作用域(块作用域)、类作用域和命名空间作用域。

函数原型作用域

  • 在声明函数原型时形参的作用范围就是函数原型作用域,这是C++程序中最小的作用域。例如,有如下的函数声明:

    double area(double radius);
    
  • 标识符radius的作用范围就在函数area形参表的左右括号之间,在程序的其他地方不能引用这个标识符。因为函数声明中形参仅在形参列表中有效,所以,函数声明中往往不写形参名,而仅写形参的类型。

局部作用域

image.png

类作用域

类可以被看成是一组有名字的成员的集合,类X的成员m具有类作用域,对m的访问方式有如下3中:

  1. 如果在类X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说,m在这样的函数中起作用。
  2. 在类外,可以通过表达式x.m或者X::m来访问,其中x是类X的对象。这正是程序中访问对象成员的最基本方法。当然,这样的访问不能违反m的访问修饰符的限定。
  3. 在类外,可以通过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 命名空间名;

作用域隐藏规则

具有命名空间作用域的变量也称为全局变量

对于在不同的作用域声明的标识符,可见性的一般原则如下:

  • 标识符要声明在前,引用在后。
  • 在同一个作用域中,不能声明同名的标识符。在没有互相包含关系的不同作用域中声明的同名标识符,互不影响。
  • 如果存在两个或多个具有包含关系的作用域,外层声明了一个标识符,而内层没有再次声明同名标识符,那么外层标识符在内层仍然可见。如果在内层声明了同名标识符,则外层标识符在内层不可见,这时称内层标识符隐藏了外层同名标识符,这种机制称为隐藏规则。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335