dream

一个菜鸟程序员的成长历程

0%

过去式

have - had - had

lose - lost 丢失

see - saw - seen

speak - spoke - spoken

take - took - taken

give - gave - given

drink- drank

meet-met

swim - swam - swum

过去式
am - was - been
is - was - been
are - were - been

do - did - done

buy - bought

bring - brought - brought

drive - drove - driven

try - tried

get - got

leave - left

find - found

hear - heard

make - made

send - sent

sweep - swept

tell - told

come - came

eat - ate - eaten

rise - rose - risen 上升

fly - flew - flown

catch - caught - caught

slip - slipped - slipped 滑到

fall - fell - fallen

hurt - hurt - hurt

drop - dropped

swallow - swallowed - swllowed 吞下

forget - forgot - forgotten

grow - grew - grown

will - would

can - could

may - might

lend - lent 借给

teach - taught

think - thought

spend - spent

write - wrote - written

throw - threw - thrown

creep - crept - crept: 爬行

leap - leapt - leapt: 跳

wind - wound - wound 蜿蜒

strike - struck: 撞击,碰撞

hold - held - held: 举办

lie - lay - lain: 躺着,位于
lay - laid - laid: 摆放

wind - wound - wound: 缠绕

forbid - forbade - forbidden 禁止
lead - led - led: 领导,领着,引导
hide - hid - hidden: 隐藏

ring - rang - rung : 响

seek - sought - sought: 寻找

过去分词

air - aired

go - gone

sell - sold

学堂在线C++程序设计第八章学习笔记

指针与数组

定义指向数组元素的指针
定义与赋值
例如: int a[10], *pa;
pa = &a[0] 或 pa = &a

*(pa +1)就是 a[1]

也可以 pa[1]

指针数组:数组的元素是指针类型
例如:Point *pa[2];
由pa[0],pa[1]两个指针构成

指针与函数

为什么需要指针做参数

  • 需要数据双向传递时(引用传递也可以达到此效果)
  • 需要传递一组数据,只传首地址运行效率比较高

指针类型的函数

若函数的返回类型是指针类型,该函数就是指针类型的函数

指针函数的定义形式:

1
2
3
存储类型 数据类型 *函数名() {

}
  • 不要将非静态局部地址用作函数的返回值
    • 因为非静态局部地址作用域仅限于函数内部,离开函数就被销毁
1
2
3
4
5
6
7
8
9
10
11
int main() {
int *function();
int *ptr = function();
*ptr = 5; //危险!!!
return 0;
}

int *function() {
int local = 0; //作用域只在函数内部有效
return &local;
} //函数结束,变量local被释放
  • 返回的指针要确保在主调函数中是有效合法的地址

在子函数中通过动态内存分配new操作取得的内存地址返回给主函数是合法有效的,但是内存分配释放不在同一级别,要注意不能忘记释放,避免内存泄露

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
int *function();
int *ptr = function();
*ptr = 5; //如果返回的是 new 分配的,就可以了
delete ptr; //但是这里需要释放,避免内存泄露
return 0;
}

int *function() {
int *local = new int(); //new 分配的不会离开函数就释放
return local;
}

指向函数的指针

函数指针的定义

  • 定义形式
    存储类型 数据类型 (*函数指针名)()
  • 含义
    函数指针指向的是程序代码存储区

用途:函数回调

  • 通过函数指针调用的函数
    • 例如将函数的指针作为参赛传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同方法
  • 调用者不关心谁是被调用者
    • 需要知道存在一个具有特定原型和限制条件的被调用函数。

对象指针

定义:
类名 *对象指针名;
例如:

1
2
3
Point a(5,10);
Point *ptr;
ptr = &a;

通过指针访问对象成员
对象指针名->成员名
例如:
ptr->getX() 相当于 (*ptr).getX()

this 指针

  • 隐含于类的每一个非静态成员函数中
  • 指出成员函数所操作的对象
    • 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含的使用了this指针。

动态内存分配

动态申请内存
new 类型名T (初始化参数列表)

  • 功能:
    在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋值
  • 结果值:
    成功:T类型的指针,指向新分配的内存,失败:抛出异常

释放内存
delete 指针P

  • 功能:
    释放指针P所指向的内存,p必须是new操作符的返回值

智能指针

C++11提供的

  • unique_ptr
    • 不允许多个指针共享资源,可以用标准库中的move函数转移指针
  • shared_ptr
    • 多个指针共享资源
  • weak_ptr
    • 可复制 shared_ptr ,但其构造或者释放对资源不产生影响

Vector

为什么需要Vector

  • 封装任何类型的动态数组,自动创建和删除
  • 数组下标越界检查

定义

  • vector<元素类型> 数组对象名(数组长度);
  • 例子:
    vector arr(5) 建立大小为5的int数组

vector对象的使用

  • 对数组元素的引用
    与普通数组具有相同形式:
    vector对象名[下标表达式]
    vector数组对象名不表示数组首地址
  • 获得数组长度
    size函数
    vector对象名.size()

对象复制和移动

浅层复制

  • 实现对象间数据元素的一一对应复制

深层复制

  • 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制

申请和释放动态数组

分配和释放动态数组

  • 分配:new 类型名T [数组长度]
    • 数组长度可以是任何整数类型表达式,在运行时计算
  • 释放:delete[] 数组名p
    • 释放指针p所指向的数组
    • p必须是用new分配得到的数组首地址

动态创建多维数组
new 类型名T[第1维长度][第2维长度]

  • 如果内存申请成功,new 运算返回一个指向新分配内存首地址的指针
  • 例如
    char (*fp)[3];
    fp = new char[2][3]

将动态数组封装成类

  • 更加简洁,便于管理
  • 可以在访问数组元素前检查下标是否越界

移动构造

C++11中提供了新的构造方法

C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。在某些情况下,我们没有必要复制对象 – 只需要移动它们

移动构造

  • C++11引入移动语义:
    • 源 对象资源的控制权全部交给目标对象
  • 移动构造函数

移动构造函数
class_name(class_name &&)

字符串

C风格字符串

字符串常量

  • 例如:”program”
  • 各字符连续,顺序存放,每个字符占一个字节,以’\0’结尾,相当于一个隐含创建的字符常量数组
  • “program” 出现在表达式中,表示这个char数组的首地址
  • 首地址可以赋给char常量指针
  • const char *STRING1 = “program”

用字符数组存储字符串

  • 例如
    char str[8] = {‘p’,’r’,’o’,’g’,’r’,’a’,’m’,’\0’}
    char str[8] = “program”
  • 缺点
    • 执行连接,拷贝,比较等操作,都需要显式调用库函数,很麻烦
    • 当字符串长度不确定时,需要用new 动态创建字符数组,最后要用delete释放,很繁琐
    • 字符串实际长度大于为他分配的空间时,会产生数组下标越界的错误

string类

构造函数

  • string() 默认的,建立长度为0的字符串
    例如 string s1;
  • string(const char *s); //用指针s所指向的字符串常量初始化string对象
    例如 string s2 = “abc”;
  • string(const string& rhs); //复制构造函数
    例如 string s3 = s2;

学堂在线C++程序设计第七章学习笔记

数组的定义与初始化

数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量称为该数组的元素

数组的定义

类型说明符 数组名[常量表达式] [常量表达式]…
数组名的构成方法与一般变量名相同

例如:

  • int a[10]
  • int a[5][3]

数组的使用

  • 数组必须先定义,后使用
  • 可以逐个引用数组元素
  • 例如:
    a[0] = a[5] + a[7]

数组的存储和初始化

一位数组的存储

数组元素在内存中顺次存放,它们的地址是连续的。元素间物理地址上的相邻,对应着逻辑次序上的相邻

数组名字是数组首元素的内存地址

数组名字是一个常量,不能被赋值

一维数组初始化

  • 列出全部值
    • static int a[5] = {0,1,2,3,4}
  • 可以只给一部分元素初始化
    • static int a[5] = {0,1,2}
  • 列出全部值时,可以不指定长度
    • static int a[] = {0,1,2,3,4}

二维数组的存储

  • 按行存放

二维数组的初始化

  • 列出全部值,按照顺序初始化
    • static int a[3][2] = {0,1,2,3,4,5}
  • 分行列出二维数组元素的初值
    • static int a[3][2] = { {0,1},{2,3},{4,5} }
  • 可以只对部分元素初始化
    • static int a[3][2] = { {0},{1,2},{3} }
  • 列出全部初始值,第一维下标可以省略
    • static int a[][2] = {0,1,2,3,4,5}
    • static int a[][2] = { {0,1},{2,3},{4,5} }
  • 如果不做任何初始化,局部作用域非静态数组中会存在垃圾数据,static 数组中的数据默认初始化为0
  • 如果只对部分元素初始化,剩下的将自动被初始化为0

对象数组

对象数组的定义和访问

  • 定义对象数组
    • 类名 数组名[元素个数]
  • 访问对象数组
    • 通过下标访问 数组名[下标].成员名

对象数组初始化

  • 数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象
  • 通过初始化列表赋值
    • 例如:Point a[2] = {Point{1,2}, Point{3,4}}
  • 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)

数组元素的构造和析构

  • 构造数组时,元素所属的类未声明构造函数,则采用默认构造函数
  • 各元素对象的初值要求为相同的值时,可以声明具有默认形参值得构造函数
  • 各元素对象的初值要求为不同的值时,需要声明带形参的构造函数
  • 当数组中每一个对象被删除时,系统需要调用一次析构函数

基于范围的for循环

C++11 提供的

自动遍历整个容器

1
2
3
4
5
6
7
8
9
int main(){
int array[3] = {1,2,3}
int *p;
for (p = array;p < array + sizeof(array) / sizeof(int); ++p) {
*p += 2;
std::cout << *p << std::endl;
}
return 0;
}

如果用基于范围的

1
2
3
4
5
6
7
8
int main(){
int array[3] = {1,2,3};
for(int & e : array) {
e+=2;
std::cout <<e <<std::endl;
}
return 0;
}

指针的定义和运算

内存空间的访问形式:

  • 通过变量名访问
  • 通过地址访问

指针的概念:

  • 指针:内存地址,用于间接访问内存单元
  • 指针变量:用于存放地址的变量
1
2
static int i;
static int* ptr = &i; //指向int变量的指针

指针运算符 *:表示该变量内存里存储的是地址,并操作存储地址对应的内存空间

地址运算符 &: 表示该变量的地址

指针的初始化和赋值

指针变量的初始化

  • 语法形式
    • 存储类型 数据类型 *指针名 = 初始地址
    • 例如:int *pa = &a
  • 注意事项
    • 用变量地址作为初始值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致
    • 可以用一个已有合法值的指针去初始化另一个指针变量
    • 不要用一个内部非静态变量去初始化 static 指针

指针变量的赋值运算

  • 语法形式 指针名 = 地址
  • 注意:
    • 地址 中存放的数据类型与指针类型必须相符
    • 向指针变量赋值必须是地址常量或变量,不能是普通整数
  • 例如:
    • 通过地址运算 & 求得已定义的变量和对象的起始地址
    • 动态内存分配成功时返回的地址
  • 例外:整数0可以赋值给指针,表示空指针
  • 允许定义或声明指向 void 类型的指针。该指针可以被赋予任何类型对象的地址
    • void *general;

指向常量的指针

  • const 指针
  • 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象
  • 例如:
    1
    2
    3
    4
    5
    int a;
    const int *p = &a;
    int b;
    p = &b
    *p = 2; //编译出错

指针类型的常量

  • 声明指针常量,则指针本身的值不能被改变
  • 例如:
    1
    2
    3
    int a;
    int * const p2 = *a;
    p2 = &b; //错误

指针的运算

指针的算术运算

  • 指针与整数的加减运算
    • 指针p 加上或减去 n:指针当前指向位置的前方或后方第n个数据的起始位置
  • 指针++,–运算
    • 指向下一个或前一个完整数据的起始位置
  • 运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置
  • 当指针指向连续存储的同类型数据时,指针与整数的加减运算和自增自减才有意义

指针的关系运算

  • 指向相同类型数据的指针之间可以进行各种关系运算
  • 指向不同数据类型的指针,以及指针与一般整形变量之间的关系运算是无意义的
  • 指针可以和零之间进行等于或不等于的关系运算,判断空指针

数据库系统原理第五节

数据库设计

数据库设计概述

数据库的生命周期

  • 数据库分析与设计阶段
    • 需求分析
    • 概念设计
    • 逻辑设计
    • 物理设计
  • 数据库实现与操作阶段
    • 实现
    • 操作与监督
    • 修改与调整

数据库设计的目标

  • 满足应用功能需求
    • 存,取,删,改
  • 良好的数据库性能
    • 高效率存取和空间的节省
    • 共享性,完整性,一致性,安全保密性

数据库设计的内容

  • 静态的 数据库结构设计
    • 数据库概念结构设计
    • 逻辑结构设计
    • 物理结构设计
  • 动态的 数据库行为设计
    • 功能设计
    • 事务设计
    • 程序设计

数据库设计的方法

  • 直观设计法
    • 最原始的数据库设计方法
  • 规范设计法
    • 新奥尔良设计法:需求分析,概念结构设计,逻辑结构设计,物理结构设计
    • 基于E-R模型
    • 基于第三范式
  • 计算机辅助设计法
    • 辅助软件工程工具

数据库设计的过程

  • 需求分析
  • 结构设计,行为设计
  • 数据库实施
    • 加载数据库数据
    • 调试运行应用程序
  • 数据库运行与维护

数据库设计的基本步骤

需求分析

数据库设计的起点
目标:是了解与分析用户的信息及应用处理的要求,并将结果按一定格式整理而形成需求分析报告

该分析报告是后续概念设计,逻辑设计,物理设计,数据库建立与维护的依据

  • 确定数据范围
    • 有效的利用计算机设备及数据库系统的潜在能力
    • 提高数据库的应变能力
    • 避免应用过程中对数据库做太多或太大的修改
    • 延长数据库的生命周期
  • 分析数据应用过程
    • 用到那些数据
    • 数据使用的顺序
    • 对数据作何处理和处理的策略以及结果
    • 应用过程分析的结果是数据库结构设计的重要依据
  • 收集与分析数据
    • 数据收集与分析的任务是了解并分析数据的组成各式及操作特征,每个数据元素的语义及关系等,并将它们收集起来整理归档
    • 静态结构 —不施加应用操作于其上时数据的原数始状况
    • 动态结构 —将应用操作施加于数据之上后数据的状况
    • 数据约束 —使用数据时的特殊要求
      • 数据的安全保密性
      • 数据的完整性
      • 响应时间
      • 数据恢复
  • 编写需求分析报告
    • 数据库的应用功能目标
    • 标明不同用户视图范围
    • 应用处理过程需求说明,包括:数据流程图;任务分类表;数据操作特征表;操作过程说明书。
    • 数据字典,是数据库系统中存储三级结构定义的数据库,通常指的是数据库系统中各类数据详细描述的集合。功能是存储和检索各种数据描述,即元数据。包括数据分类表,数据元素表和各类原始资料
    • 数据量
    • 数据约束

概念结构设计 重点

概念模型 – E-R图

  • 实体分析法 (自顶向下)
  • 属性综合法 (自底向上)

概念模型

  • 独立于任何软件与硬件
  • 主要目标:最大限度的满足应用需求

逻辑结构设计

目标:
将概念模型转换为等价的,并为特定DBMS所支持数据模型的结构

步骤:

  1. 模型转换 是将概念模型等价的转换为特定DBMS支持的关系模型,网状模型或层次模型表示
  2. 子模式设计的目标是抽取或导出模式的子集,以构造不同用户使用的局部数据逻辑结构
  3. 编制应用程序设计说明的目的是为可实际运行的应用程序设计提供依据与指导,并作为设计评价的基础
  4. 设计评价的任务是分析并检验模式及子模式的正确性与合理性

物理设计

具体任务主要是确定数据库在存储设备上的存储结构存取方法
因DBMS的不同还可能包括建立索引聚集,以及物理块大小缓冲区个数和大小数据压缩的选择

数据库实施

  • 加载数据
  • 应用程序设计
  • 数据库试运行

数据库运行与维护

保证数据库的正常运行

最困难:数据库重组与重构

关系数据库设计方法

概念模型的表示方法

  • 矩形:实体
  • 椭圆:属性
  • 菱形:联系

数据库系统原理第六节

数据库设计

关系数据库设计方法

逻辑结构设计方法

  • 将E-R图转换为关系模型
    • 一个实体转换为一个关系模式,实体的属性作为关系的属性,实体的码作为关系的码
    • 一个一对一联系可以转换为一个独立的关系模式,也可以与任意一端对应的关系模式合并
    • 一个一对多联系可以转换为一个独立的关系模式,也可以与N端对应的关系模式合并
    • 一个多对多联系可以转换为一个独立的关系模式,与该联系相连的各实体的码以及联系本身的属性均转换为关系的属性
    • 三个或以上实体间的一个多元联系可以转换为一个关系模式
    • 具有相同码的关系模式可合并
  • 对关系数据模型进行优化
    • 数据库逻辑设计的结果,不是唯一
    • 确定各属性间的函数依赖关系
    • 对于各个关系模式之间的数据依赖进行极小化处理,消除冗余的联系
    • 判断每个关系模式的范式,根据实际需要确定最合适的范式
    • 按照需求分析得到的处理要求,分析浙西模式对于这样的应用环境是否合适,确定是否要对某些模式进行合并或分解
    • 对关系模式进行必要的分解,提高数据操作的效率和存储空间的利用率
  • 设计面向用户的外模式

设计用户子模式

  • 可以通过视图机制在设计用户视图时,重新定义某些属性的别名,使其更符合用户的习惯,以方便使用
  • 可以对不同级别的用户定义不同的视图,以保证系统的安全性。
  • 简化用户对系统的使用

物理设计方法

建立索引

  • 逻辑连接
  • 静态建立索引
  • 动态建立索引
    建立聚集
  • 聚集是将相关数据集中存放的物理存储技术
  • 数据聚集结构的一种有效方式是块结构方式
  • 数据聚集可在一个或多个关系上建立

SQL与关系数据库基本操作

SQL概述

什么是SQL

结构化查询语言是专门用来与数据库通信的语言,它可以帮助用户操作关系数据库

SQL的特点

SQL不是某个特定数据库供应商专有的语言
SQL简单易学
SQL强大,灵活,可以进行非常复杂和高级的数据库操作

SQL的组成

数据查询
数据定义 DDL

  • CREATE 创建数据库或数据库对象
  • ALTER 对数据库或数据库对象进行修改
  • DROP 删除数据库或数据库对象
    数据操纵 DML
  • SELECT 从表或视图中检索数据
  • INSERT 将数据插入到表或视图中
  • UPDATE 修改表或视图中的数据
  • DELETE 从表或视图中删除数据
    数据控制 DCL
  • GRANT 用于授予权限
  • REVOKE 用于收回权限

MySql预备知识

MySQL使用基础

关系数据库管理系统 RDBMS

优点:体积小,速度快,开放源代码,遵循GPL

MySQL扩展语言要素

常量:也称字面值或标量值

  • 字符串常量
  • 数值常量
  • 十六进制常量
  • 时间日期常量
  • 位字段值
  • 布尔值
  • NULL值

变量

  • 用户变量
    用户变量前常添加@
  • 系统变量
    大多数系统变量前添加两个@@

运算符

  • 算术运算符
  • 位运算符
  • 比较运算符
  • 逻辑运算符
    • NOT
    • AND
    • OR
    • XOR

表达式:是常量,变量,列名,复杂计算,运算符合函数的组合

  • 字符型表达式
  • 数值型表达式
  • 日期型表达式

内置函数

  • 数学函数
    • ABS
  • 聚合函数
    • COUNT
  • 字符串函数
  • 日期和时间函数
  • 加密函数

数据定义

数据库模式定义

创建数据库
使用 CREATE DATABASECREATE SCHEMA语句

1
2
3
CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
[DEFAULT] CHARACTER SET[=]charset_name
| [DEFAULT] COLLATE[=]collation_name

查看数据库

1
2
SHOW {DATABASES | SCHEMAS}
[LIKE pattern | WHERE expr]

选择数据库

1
use db_name

修改数据库

1
2
3
ALTER DATABASE db_name
DEFAULT CHARACTER SET gb2312
DEFAULT COLLATE gb2312_chinese_ci;

删除数据库

1
DROP {DATABASE|SCHEMA} [IF EXISTS] db_name;

学堂在线C++程序设计第六章学习笔记

变量和对象定义在不同的位置

  • 其作用域,可见性,生存期都不同

友元:

  • 对一些类外的函数,其他的类,给预授权,使之可以访问类的私有成员

标识符的作用域与可见性

作用域分类:

  • 函数原型作用域
  • 局部作用域
  • 类作用域
  • 文件作用域
  • 命名空间作用域

函数原型作用域

  • 函数原型中的参数

类的作用域

  • 类的成员具有类作用域,其范围包括类体和成员函数体
  • 在类作用域以外访问类的成员
    • 静态成员:通过类名,或者该类的对象名,对象引用访问
    • 非静态成员:通过类名,或该类的对象名,对象引用,对象指针访问

文件作用域

  • 不在前述各个作用域中出现的声明,就具有文件作用域
  • 其作用域开始于声明点,结束于文件尾

可见性

  • 可见性是从对标识符的引用角度来谈论的
  • 可见性表示从内层作用域向外层作用域“看”时能看见什么
  • 如果标识在某处可见,就可以在该处引用此标识符
  • 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见
  • 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层标识符在内层不可见

对象的生存期

静态生存期

  • 这种生存期与程序的运行期相同
  • 在文件作用域中声明的对象具有这种生存期
  • 在函数内部声明静态生存期对象,要冠以关键字static

动态生存期

  • 开始于程序执行到声明点时,结束于命名该标识符的作用域结束处
  • 块作用域中声明的,没有用static修饰的对象是动态生存期的对象(也称局部生存期对象)

静态数据成员

  • 用关键词static修饰
  • 为该类的所有对象共享,静态数据成员具有静态生存期
  • 必须在类外定义和初始化,用(::)来指明所属的类
1
2
3
4
5
class Point{
public:
static int count; //声明
}
int Point::count = 0; //初始化

静态函数成员

静态函数成员主要用来处理静态数据成员

静态函数成员要处理非静态数据成员,需要用对象调用

类的友元

  • 友元是C++提供的一种破坏数据封装和数据隐藏的机制
  • 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息
  • 可以声明友元函数和友元类
  • 为了确保数据的完整性,及数据封装与隐藏的原则,建议慎用友元

友元函数

  • 友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private和protected成员
  • 作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择
  • 访问对象中的成员必须通过对象名

友元类

  • 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员
  • 声明语法:将友元类名在另一个类中使用friend修饰说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A{
friend class B;
public:
void display() {
cout << X << endl;
}
private:
int X;
}

class B{
public:
void set(int i);
void display();
private:
A a;
}

void B::set(int i) {
a.X = i;
}

void display() {
a.display();
}

类的友元关系是单向的:

  • 声明B类是A类的友元 不等于 A类是B类的友元

共享数据的保护

常类型

  • 常对象:必须进行初始化,不能被更新。
    const 类名 对象名
  • 常成员:
    用 const 进行修饰的类成员:常数据成员和常函数成员
  • 常引用:被引用的对象不能被更新
    const 类型说明符 &引用名;
  • 常数组:数组元素不能被更新
    类型说明符 const 数组名[大小]
  • 常指针:指向常量的指针

常成员:用const修饰的对象成员

  • 常成员函数
    • 使用const 关键字说明的函数
    • 常成员函数不更新对象的数据成员
    • 常成员函数说明格式:
      类型说明符 函数名(参数表)const;
      这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字
    • const 关键字可以被用于参与对重载函数的区分
    • 通过常对象只能调用它的常成员函数
  • 常数据成员
    • 使用 const 说明的数据成员

常引用

  • 在友元函数中用常引用做参数,既能获得较高的执行效率,又能保证实参的安全性

多文件结构和预编译处理命令

C++程序的一般组织结构

  • 一个工程可以划分为多个源文件
    • 类声明文件(.h文件)
    • 类实现文件(.cpp文件)
    • 类的使用文件(main()所在的.cpp文件)
  • 利用工程来组合各个文件

文件1 Point.h

1
2
3
4
5
6
7
8
9
10
class Point{
//类的定义
public:
Point(int x =0,int y = 0):x(x),y(y){}
Point(const Point &p);
static void showCount();
private:
int x,y;
static int count;
}

文件2 Point.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Point.h"
#include <iostream>
using namespace std;

int Point::cout = 0;

Point::Point(const Point &p):x(p.x),y(p.y){
count++;
}

void Point::showCount() {
cout << "Object count =" << count << endl;
}

文件3 主函数 main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Point.h"
#include <iostream>
using namespace std;

int main() {
Point a(4,5);
cout << "Point A:" < a.getX() << "," << a.getY();
Point::showCount();
Point b(a);
cout << "Point B:" << b.getX() << "," << b.getY();
Point::showCount();
return 0;
}

外部变量

  • 除了在定义它的源文件中可以使用外,还能被其他文件使用
  • 文件作用域中定义的变量,默认情况下都是外部变量
  • 在其它文件中如果需要使用,需要用extern关键字声明

外部函数

  • 在所有类之外声明的函数(也就是非成员函数),都是具有文件作用域的
  • 这样的函数都可以在不同的编译单元中被调用
  • 只要在调用之前进行引用性声明(即声明函数原型)即可

将变量和函数限制在编译单元内

  • 在匿名命名空间中定义的变量和函数,都不会暴露给其它的编译单元
1
2
3
4
5
6
7
namespace {
//匿名的命名空间
int n;
void f() {
n++;
}
}

标准C++库

  • 标准C++类库是一个极为灵活并可扩展的可重用软件模块的集合

标准C++类与组件在逻辑上分为6种类型:

  • 输入/输出类
  • 容器类与抽象数据类型
  • 存储管理类
  • 算法
  • 错误处理
  • 运行环境支持

编译预处理

  • #include 包含指令
    • 将一个源文件嵌入到当前源文件中该点处
    • #include<文件名>
      • 按标准方式搜索,文件位于C++系统目录的include子目录下
    • #include “文件名”
      • 首先在当前目录中搜索,若没有,再按标准方式搜索
  • #define 宏定义指令
    • 定义符号常量,很多情况下已被const定义语句取代
    • 定义带参数宏,已被内联函数取代
  • #under
    • 删除由 #define 定义的宏,使之不再起作用

条件编译指令 #if 和 #endif
#if 常量表达式
//当 常量表达式 非零时编译
程序正文
#endif

条件编译指令 #else
#if 常量表达式
//当 常量表达式 非零时编译
程序正文1
#else
//当 常量表达式 为零时编译
程序正文2
#endif

条件编译指令 #elif
#if 常量表达式1
//当 常量表达式1 非零时编译
程序正文1
#elif 常量表达式2
//当 常量表达式2 非零时编译
程序正文2
#else
//其他情况下编译
程序正文3
#endif

条件编译指令
#ifdef 标识符
程序段1
#else
程序段2
#endif

  • 如果 标识符 经 #define 定义过,且未经undef删除,则编译程序段1
  • 否则编译程序段2

条件编译指令
#ifndef 标识符
程序段1
#else
程序段2
#endif

  • 如果标识符未被定义过,则编译程序段1
  • 否则编译程序段2

软件工程第二章

编程过程与规范

编程是软件工程师的基本能力

编写优雅的代码是每一个程序员的不懈追求

编程是一门艺术,他能够展示结构之美,构造之美,表达之美

软件变成工作

软件编程是一个复杂而迭代的过程,它不仅仅是编写代码,还应该包括代码审查,单元测试,代码优化,集成调试等一系列工作。

系统模型到源代码

  • 编写代码
  • 代码审查
  • 单元测试
  • 代码优化
  • 系统构建
  • 集成调试

高质量软件开发之道

  • 规范的编码
  • 高质量的设计
  • 有效的测试

软件编程规范

是与特定语言相关的描写如何编写代码的规则集合

目的

  • 提高编码质量,避免不必要的程序错误
  • 增强程序代码的可读性,可重用性和可移植性

学会只编写够用的注释,过犹不及,重视质量而不是数量

  • 好的注释解释为什么,而不是怎么样
  • 不要在注释中重复描述代码
  • 当自己在编写密密麻麻的注释来解释代码时,需要停下来看是否存在更大的问题
  • 想一想在注释中写什么,不要不动脑筋就输入
  • 写完注释之后要在代码得上下文中回顾一下,它们是否包含正确的信息
  • 当修改代码时,维护代码周围的所有注释

命名规范
好的名字一目了然,不需要读者去猜,甚至不需要注释

编写自文档化的代码

  • 唯一能完整并正确的描述代码的文档是代码本身
  • 编写可以阅读的代码,其本身简单易懂

良好的编程实践

看:阅读优秀的代码,学习别人的代码
问:发现问题,提出问题
练:亲自动手编写代码,实践,实践,再实践

高质量的设计

  • 模块化设计:将一个大的程序按功能分拆成一系列小模块
  • 面向抽象编程
  • 错误与异常处理

基于易变与稳定:认识和识别变与不变的部分,并将之科学地分离开
易变:业务逻辑
稳定:通用功能

基于单一职责:类或者函数应该只做一件事,并且做好这件事
单一职责:只有一个引起变化的原因

在模块化设计的基础上,先设计出各个模块的骨架,或者说对各个模块进行抽象,定义它们之间的接口

代码静态检查

代码审查是一种用来确认方案设计和代码实现的质量保证机制,它通过阅读代码来检查源代码与编码规范的符合性以及代码得质量。

代码审查的作用:

  • 检查设计的合理性
  • 互为Backup
  • 分享知识,设计,技术
  • 增加代码可读性
  • 处理代码中的“地雷区”

检查编码规范
面向对象设计
性能方面
资源释放处理
程序流程
线程安全
数据库处理
通讯方面
异常处理
方法
安全方面
其他

代码性能分析

优化师对代码进行等价变换,使得变换后的代码运行结果与变换前的代码运行结果相同,但执行速度加快或存储开销减少

  • 代码性能优化师一门复杂的学问
  • 根据80/20原则,实现程序的重构,优化,扩展以及文档相关的事情通常需要消耗80%的工作量

满足正确性,可靠性,健壮性,可读性等质量因素的前提下,设法提高程序的效率
以提高程序的全局效率为主,提高局部效率为辅
在优化程序效率时,应先找出限制效率的“瓶颈”
先优化数据结构和算法,再优化执行代码

从一开始就要考虑程序性能,不要期待在开发结束后在做一些快速调整
正确的代码要比速度快的代码重要,任何优化都不能破坏代码得正确性

  • 证明需要进行优化
  • 找出优化关键部门
  • 测试代码
  • 优化代码
  • 评测优化后的代码

认真选择测试数据
永远不要在没有执行前后性能评估的情况下尝试对代码进行优化

性能优化的关键是如何发现问题,寻找解决问题的方法
有效的测试是不可缺少的,通过测试找出真正的瓶颈,并分析优化结果
要避免不必要的优化,避免不成熟的优化,不成熟的优化师错误的来源

改进算法,选择合适的数据结构

  • 良好的算法对性能起到关键作用,因此性能改进的首要点是对算法改进

循环优化的基本原则:尽量减少循环过程中的计算量,在多重循环的时候,尽量将内层的计算提到上一层

结对编程

结对编程是由两名程序员在同一台电脑上结对编写解决同一问题的代码

驾驶员:负责用键盘编写程序
领航员:起到领航,提醒的作用

数据库原理第三节

关系数据模型

关系数据结构

码或键:
属性(属性组)的值都能用来唯一标识该关系的元组,则称这些属性为该关系的码或键

超码或超键:
在码中去除某个属性,他仍然是这个关系的码

候选码或候选键:
在码中不能从中移去任何一个属性,否则它就不再是这个关系的码或键。候选码或候选键是这个关系的最小超码或超键

主属性或码属性:

主码或主键:
在若干个候选码中指定一个唯一标识关系的元组(行)

全码或全键:
一个关系模式的所有属性集合是这个关系的主码或主键,这样的主码或主键称为全码或全键

外码或外键:
某个属性不是这个关系的主码或候选码,而是另一个关系的主码

参照关系被参照关系:
参照关系称为从关系,被参照关系称为主关系,它们是指以外码相关联的两个关系

域:
表示属性的取值范围

数据类型:
每个列都有相应的数据类型,它用于限制(或容许)该列中存储的数据

关系模式:
关系模式是,关系是,即关系模式是对关系的描述。

关系模式是静态的,稳定的

关系是动态的,随时间不断变化的。

关系数据库:
所有关系的集合,构成一个关系数据库
以关系模型作为数据的逻辑模型,并采用关系作为数据组织方式的一类数据库,其数据库操作建立在关系代数的基础上

关系数据库对关系的限定/要求:

  • 每一个属性都是不可分解的
  • 每一个关系仅仅有一种关系模式
  • 每一个关系模式中的属性必须命名,属性名不同
  • 同一个关系中不允许出现候选码或候选键值完全相同的元组
  • 在关系中元组的顺序是无关紧要的,可以任意交换
  • 在关系中属性的顺序是无关紧要的,可以任意交换

基本的关系操作

查询Query:

  • 选择
  • 投影
  • 笛卡尔积
    从上面的基本的组合:
  • 连接

特点:集合操作方式

关系数据语言的分类

关系代数语言

关系演算语言

  • 元组关系演算
  • 域关系演算

SQL结构化查询语言

共同特点:具有完备的表达能力,是非过程化的集合操作语言,功能强,能够独立使用也可以嵌入高级语言中使用。

关系代数的运算符

任何一种操作都包含三大要素

  • 操作对象
  • 操作符
  • 操作结果

传统的集合运算

并,差,交,笛卡尔积

专门的关系运算

选择,投影,连接,除

选择 select:a
a id = 1 (user) //获取id = 1的user表的数据
相当于这个SQL:select * from user where id = 1
a id = 1 ^ name = ‘abc’ (user)
SQL: select * from user where id = 1 and name = ‘abc’

投影 projection:π
projection 关系名(属性名1,。。。) 选出要展示的列
π id,name (id = 1 ^ name = ‘abc’ (user))
SQL: select id,name from (select * from user where id = 1 and name = ‘abc’)

并集 union:union 需要两个表的属性值一样才可以,假设两个表都是id,name字段那么可以,如果一个表有三个字段就不行
t1 union t2, 就是把两个表合并
SQL: (select * from t1) union all (select * from t2) //和union不完全一样

交集 intersection:和union要求一样,选的是交集
t1 intersect t2

差集 difference: 和union要求一样,取得是第一个表出现过第二表没出现的数据
t1 except t2

笛卡尔积 product: 把两个表所有的组合列出来
t1 * t2
SQL: select * from t1 cross join t2

连接 join: 取某个字段值相同的数据,和sql的join差不多
t1 join t2
SQL: select * from t1 natural join t2

数据库原理第四节

关系数据模型

关系的完整性约束

数据库的数据完整性是指数据库中数据的正确性相容性一致性

分类:

  • 实体完整性约束:主码的组成不能为空,主属性不能是空值NULL
  • 参照完整性约束:要么外码等于主码某个元组的主码值,要么为空值
  • 用户定义完整性约束
  • 域完整性约束

执行插入操作检查:

  • 检查实体完整性约束
  • 插入外码表的时候检查参照完整性约束
  • 检查用户定义完整性约束

执行删除操作:
一般只需要对被参照关系检查参照完整性约束

关系数据库的规范化理论

关系模式中可能存在的冗余和异常问题

  • 数据冗余
  • 更新异常
  • 插入异常
  • 删除异常

函数依赖与关键字

设R为任一给定关系,如果对于R中属性X的每一个值,R中的属性Y只有唯一值与之对应,则称X函数决定Y或称Y函数依赖于X,记作X->Y.其中X称为决定因素

分类:

  • 完全函数依赖
  • 部分函数依赖
  • 传递函数依赖

完全函数依赖:
设R为任一给定关系,X,Y为其属性集,若X->Y,且对X中的任何真子集X’,都有X’不依赖Y,则称Y完全函数依赖于X

部分函数依赖:
设R为任一给定关系,X,Y为其属性集,若X->Y,且X中存在一个真子集X’,都有X’->Y,则称Y部分依赖于X

传递函数依赖:
设R为任一给定关系,X,Y,Z为其不同属性子集,若X->Y,Y不决定X,Y->Z,则有X->Z,称为Z传递函数依赖于X。

关键字的定义:
设R为任一给定关系,U为其所含的全部属性集合,X为U的子集,若有完全函数依赖X->U,则X为R的一个候选关键字。

范式与关系规范化过程

一个低一级范式的关系模式通过模式分解可以转换为若干个高一级范式的关系模式的集合,这种过程就叫规范化

第一范式1NF:
设R为任一给定关系,如果R中每个列与行的交点处的取值都是不可再分的基本元素,则R为第一范式

第二范式2NF:
设R为任一给定关系,若R为1NF
且其所有非主属性都完全函数依赖于候选关键字,则R为第二范式。

第三范式3NF:
设R为任一给定关系,若R为2NF
且其每一个非主属性都不传递函数依赖于候选关键字,则R为第三范式。

第三范式的改进形式BCNF:
设R为任意给定关系,X,Y为其属性集,F为其函数依赖集,若R为3NF
且其F中所有函数依赖X->Y(Y不属于X)中的X比包含候选关键字,则R为BCNF

有部分函数依赖就是1NF,没有就是2NF,没有传递函数依赖就是3NF

1NF->2NF
找到候选关键字,看其余的属性是否完全函数依赖候选关键字
是的,与候选关键字一同抄下来形成一个表格
不是的,抄下来,形成第二个表格,并且将候选关键字里能够唯一决定表格2的属性组抄在第一列

2NF->3NF
找到表格中的传递函数依赖关系的三个属性组,设为X,Y,Z
将这三个属性组拆成两个表格
第一个表格为X,Y
第二个表格为Y,Z

3NF->BCNF
列出表格中的所有函数依赖关系
每个关系拆出一个表格

学堂在线C++程序设计第五章学习笔记

类与对象

对象:现实中对象的模拟,具有属性和行为。
类:同一类对象的共同属性和行为

定义对象时:通过构造函数初始化
删除对象时:通过析构函数释放资源

面向对象程序的基本特点

抽象:对同一类对象的共同属性和行为进行概括,形成类

封装:将抽象出的数据,代码封装在一起,形成类

继承:在已有类的基础上,进行扩展形成新的类

多态:同一名称,不同的功能实现方式

类和对象

设计类就是设计类型

  • 此类型的“合法值”是什么
  • 此类型应该有什么样的函数和操作符
  • 新类型的对象该如何被创建和销毁
  • 如何进行对象的初始化和赋值
  • 对象作为函数的参数如何以值传递
  • 谁将使用此类型的对象成员

类定义的语法形式

1
2
3
4
5
6
7
8
9
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
}

类的成员函数

  • 在类中声明函数原型
  • 可以在类外给出函数体实现,并在函数名前使用类名加以限定
  • 也可以直接在类中给出函数体,形成内联成员函数
  • 允许声明重载函数和带默认参数值得函数

构造函数

类中的特殊函数
用于描述初始化算法

作用:

  • 在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态

形式:

  • 函数名与类名相同
  • 不能定义返回值类型,也不能有return语句
  • 可以有形式参数,也可以没有形式参数
  • 可以是内联函数
  • 可以重载
  • 可以带默认参数值

调用时机

  • 在对象创建时被自动调用
  • 例如:
    Clock myClock(0,0,0)

默认构造函数

  • 调用时可以不需要实参的构造函数
    • 参数表为空的构造函数
    • 全部参数都有默认值的构造函数

隐含生成的构造函数

  • 如果程序中未定义构造函数,编译器将自动生成一个默认构造函数
  • 参数列表为空,不为数据成员设置初始值
  • 如果类内定义了成员的初始值,则使用内类定义的初始值
  • 如果没有定义类内的初始值,则以默认方式初始化
  • 基本类型的数据默认初始化的值是不确定的

复制构造函数

  • 复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。
1
2
3
4
5
6
7
8
class 类名{
public:
类名(const 类名 &对象名); //复制构造函数
}

类名::类(const 类名 &对象名) {
函数体
}

复制构造函数被调用的三种情况

  • 定义一个对象时,以本类另一个对象作为初始值,发生复制构造
  • 如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造
  • 如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造

隐含的复制改造函数

  • 如果没有为类声明拷贝初始化构造函数,则编译器自己生成一个隐含的复制构造函数
  • 这个构造函数执行的功能是:用初始值对象的每个数据成员,初始化将要建立的对象的对应数据成员

析构函数

  • 完成对象被删除前的一些清理工作。
  • 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。
  • 如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数体为空。
  • 析构函数的原型:~类名()
  • 析构函数没有参数,没有返回类型

类的组合

  • 类中的成员是另一个类的对象
  • 可以在已有抽象的基础上实现更复杂的抽象

类组合的构造函数设计

  • 原则:不仅要对本类中的基本类型成员数据初始化,也要对对象成员初始化
  • 声明形式:
1
2
3
4
5
类名::类名(对象成员所需的形参,本类成员的形参):
对象1(参数),对象2(参数),...
{
//函数体
}

构造组合类对象时的初始化次序

  • 首先对构造函数初始化列表中列出的成员(包括基本类型和对象成员)进行初始化,初始化次序是成员在类体中定义的次序
    • 成员对象构造函数调用顺序:按对象成员的定义顺序,先声明者先构造
    • 初始化列表中未出现的成员对象,调用默认构造函数(即无形参)的初始化
  • 处理完初始化列表后,再执行构造函数的函数体

结构体

结构体是一种特殊形态的类
与类的唯一区别

  • 类的缺省访问权限是private
  • 结构体的缺省访问权限是public

什么时候用结构体

  • 定义主要用来保存数据,而没有什么操作的类型
  • 人们习惯将结构体的数据成员设为公有,因此这时用结构体更方便

定义

1
2
3
4
5
6
7
struct 结构体名称{
共有成员
protected:
保护型成员
private:
私有成员
}

结构体中可以有数据成员和函数成员

结构体的初始化
如果:

  • 一个结构体的全部数据成员都是公共成员
  • 没有用户定义的构造函数
  • 没有基类和虚函数

这个结构体的变量可以用下面的语法形式初始化
类型名 变量名 = {成员数据1初始值,…}

联合体

定义

1
2
3
4
5
6
7
union 联合体名称{
共有成员
protected:
保护型成员
private:
私有成员
}

特点:

  • 成员共用同一组内存单元
  • 任何两个成员不会同时有效

枚举类

定义
enum class 枚举类型名:底层类型{枚举值列表}
例子:
enum class Type{General,Light}
enum class Type:char{General,Light}

枚举类的优势

  • 强作用域:作用域限制在枚举类中
  • 转换限制:枚举类对象不可以与整型隐式转换
  • 可以指定底层类型