dream

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

0%

数据库系统原理第十四节

数据库安全和保护

事务与并发控制

所谓事务是用户定义的一个数据操作序列,这些操作可作为一个完整的工作单元,要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务中的操作一般是对数据的更新操作,包括增删改

begin transaction开始
commit 或 rollback结束

事务的特征

  • A 原子性
  • C 一致性
  • I 隔离性
  • D 持续性
并发操作问题
  • 丢失更新
  • 不可重复读
  • 读‘脏’数据
封锁

封锁 是最常用的并发控制技术

基本思想:需要时,事务通过向系统请求对它所希望的数据对象加锁,以确保它不被非预期改变

一个实质上就是允许或阻止一个事务对一个数据对象的存取特权

基本的封锁类型

  • 排它锁,用于写操作
  • 共享锁,用于读操作

封锁的粒度
粒度越细,并发性就越大,但软件复杂性和系统开销也就越大

封锁的级别又称为一致性级别或隔离度

  • 0级封锁:不重写其他非0级封锁事务的未提交的更新数据
  • 1级封锁:不允许重写未提交的更新数据。防止了丢失更新的发生
  • 2级封锁:既不重写也不读未提交的更新数据(防止了读脏数据)
  • 3级封锁:不读未提交的更新数据,不写任何(包括读操作)未提交数据

活锁:先来先服务
死锁:预防

  • 一次性锁请求
  • 锁请求排序
  • 序列化处理
  • 资源剥夺

可串行性
一组事务的一个调度就是它们的基本操作的一种排序

在数据库系统中,可串行性就是并发执行的正确性准则

两段封锁法

  • 发展或加锁阶段
  • 收缩或释放锁阶段

备份与恢复

备份

1
2
3
4
5
select * into outfile 'file_name' export_options  
dumpfile 'file_name'
fields terminated by ',' 逗号分隔
optionally enclosed by "" 字符串用双引号
lines terminated by '?' 问号分隔每行数据

dumpfile 导出的备份文件里面所有的数据都彼此紧挨着

导入

1
2
3
4
load data infile 'file_name' into table table_name
fields terminated by ',' 逗号分隔
optionally enclosed by "" 字符串用双引号
lines terminated by '?' 问号分隔每行数据

数据库生命周期

  • 需求分析
  • 系统功能与数据库的设计
  • 系统功能与数据库的实现
  • 测试与维护

php mysql应用

连接数据库非持久连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$con = mysql_connect("localhost:3306","root","123456");

持久连接 mysql_pconnect

选择数据库 mysql_select_db("test", $con)

执行 sql mysql_query("set names 'gbk'")

$res = mysql_query($sql, $con);

$array = mysql_fetch_array($res, MYSQL_NUM);

mysql_close($con) or die("关闭失败");

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

泛型程序设计和标准模板库

泛型及STL

泛型程序设计

  • 编写不依赖于具体数据类型的程序
  • 将算法从特定的数据结构中抽象出来成为通用的
  • C++的模板为泛型程序设计奠定了关键的基础

术语:概念

  • 可以比较大小的数据类型记作Comparable
  • 具有公有的复制函数并可以用 = 赋值的记作Assignable
  • 将 可以比较大小 并且 具有公有复制函数可以用 = 赋值的记作Sortable

对于两个不同的概念A,B,如果A所需求的所有功能也是B所需求的,那么B是A的子概念,比如,Sortable是 Comparable 的子概念,也是 Assignable的子概念

术语:模型

  • 符合一个概念的数据类型称为该概念的模型
  • 比如 int 型是 Comparable的模型
  • 静态数组不是 Assignable的模型

STL

  • 标准模板库STL定义了一套概念体系,为泛型程序设计提供了逻辑基础
  • STL中的各个类模板,函数模板的参数都是用这个体系中的概念来规定的
  • 使用STL模板时,类型参数既可以是C++标准库中已有的类型,也可以是自定义的类型–只要这些类型是所要求的概念的模型

STL基本组件

  • 容器(container):容纳,包含一组元素的对象
    • 顺序容器:
      array,vector,双端队列,单链表,列表
    • 有序容器:
      集合,多重集合,map,多重映射
    • 无序关联容器
      无序集合,无序多重集合,无序映射,无序多重映射
  • 迭代器(iterator)
    • 提供了顺序访问容器中每个元素的方法
    • 可以使用 ++ 运算符来获得指向下一个元素的迭代器
    • 可以使用 * 运算符访问迭代器指向的元素,如果元素类型是类或结构体,还可以使用 -> 运算符直接访问成员
    • 有些迭代器还支持通过 – 运算符获得指向上一个元素的迭代器
    • 迭代器是泛化的指针:指针也具有同样的特性,因此指针本身就是一种迭代器
    • 使用独立于STL容器的迭代器,需要包含头文件
  • 函数对象(function object)
  • 算法(algorithms)

迭代器算法容器的桥梁

  • 将迭代器作为算法的参数,通过迭代器来访问容器而不是把容器直接作为算法参数
  • 函数对象作为算法参数而不是将函数所执行的运算作为算法的一部分。
  • 使用STL中提供的或自定义的迭代器和函数对象,配合STL的算法,可以组合出各种各样的功能

容器适配器

  • 队列

迭代器

迭代器是算法和容器的桥梁

  • 迭代器用作访问容器中的元素
  • 算法不直接操作容器中的数据,而是通过迭代器间接操作

算法和容器独立

  • 增加新的算法,无需影响容器的实现
  • 增加新的容器,原有的算法也可以适用

输入流迭代器

  • istream_iterator
  • 以输入流 如 cin 为参数构造
  • 可用 *(p++) 获得下一个输入的元素

输出流迭代器

  • ostream_iterator
  • 构造时需要提供输出流 如 cout
  • 可用 (*p++) = x 将 x 输出到输出流

二者都属于适配器

  • 适配器是用来为已有对象提供新的接口的对象
  • 输入流适配器和输出流适配器为流对象提供了迭代器的接口

容器的基本功能和分类

  • 容器类是容纳,包含一组元素或元素集合的对象
  • 基于容器中元素的组织方式分类:顺序容器,关联容器
  • 按照与容器所关联的迭代器类型分类:可逆容器,随机访问容器

顺序容器

顺序容器的基本功能
  • STL中的顺序容器
    • 向量
    • 双端队列(deque)
    • 列表(list)
    • 单向链表(forward_list)
    • 数组(array)
  • 元素线性排列,可以随时在指定位置插入元素和删除元素
  • 必须符合 Assignable 这一概念(具有共有的复制构造函数并可以用 ‘=’ 赋值)
  • array 对象的大小固定,forward_list有特殊的添加和删除操作

顺序容器的接口(不包含单向链表和数组)

  • 构造函数
  • 赋值函数:assign
  • 插入函数: insert, push_front(只对list和deque),push_back, emplace, emplace_front
  • 删除函数:erase, clear, pop_front, pop_back, emplace_back
  • 首尾元素的直接访问:front, back
  • 改变大小:resize

顺序容器的特征

向量

  • 特点
    • 一个可以扩展的动态数组
    • 随机访问,在尾部插入或删除元素块
    • 在中间或头部插入或删除元素慢
  • 容量
    • 容量:实际分配空间的大小
    • s.capacity: 返回当前容量
    • s.reserve(n): 容量小于n,则对s进行扩展,使其容量至少为n

双端队列

  • 特点
    • 在两端插入或删除元素快
    • 在中间插入或删除元素慢
    • 随机访问较快,但比向量慢

列表

  • 特点
    • 在任意位置插入和删除元素都很快
    • 不支持随机访问
  • 接合操作(splice)
    • s.splice(p, s2, q1, q2): 将s2中[q1,q2)移动到s1中p所指向元素之前

顺序容器的插入迭代器与适配器

顺序容器的插入迭代器

  • 用于向容器头部,尾部或中间指定位置插入元素的迭代器
  • 包括前插迭代器(front_inserter),后插迭代器(back_inserter)和任意位置插入迭代器(inserter)
  • 例子:
    list s;
    back_iserter iter(s);
    *(iter++) = 5; //通过iter把5插入s末尾

顺序容器的适配器

  • 以顺序容器为基础构建一些常用数据结构,是对顺序容器的封装
    • 队列
    • 优先级队列:最 的先弹出

栈和队列模板

  • 栈模板
    template <class T, class Sequence = deque> class stack;
  • 队列模板
    template <class T,class FrontInsertionSequence = deque> class queue;
  • 栈可以用任何一种顺序容器作为基础容器,而队列只允许用前插顺序容器(双端队列或列表)

栈和队列共同支持的操作

  • 比较
  • 返回元素个数
  • empty
  • push
  • pop
  • 不支持迭代器,因为不允许对任意元素访问

栈和队列不同的操作

  • 栈的操作
    s.top 返回栈顶元素的引用
  • 队列操作
    s.front 获得队头元素的引用
    s.back 获得队尾元素的引用

优先级队列

  • 优先级队列也像栈和队列一样支持元素的压入和弹出,但元素弹出顺序和元素大小有关,每次弹出最大的
  • template <class T, class Sequence = Vector> class priority_queue
  • 优先级队列的基础容器必须是支持随机访问的顺序容器
  • 支持栈和队列的size,empty, push, pop几个函数
  • 优先级队列并不支持比较操作
  • 与栈类似,优先级队列提供一个 top 函数,可以获得下一个即将被弹出元素的引用

关联容器

分类和基本功能

关联容器的特点

  • 每个关联容器都有一个key
  • 可以根据key高效查找元素

接口

  • 插入insert
  • 删除 erase
  • 查找 find
  • 定界 lower_bound,upper_bound,equal_bound
  • 计数 count

集合

集合用来存储一组无重复的数据。用于集合的元素本身是有序的,可以高效查找元素,也可以方便的得到指定大小范围的元素在容器中所处的区间

映射

映射和集合的主要区别

  • 集合的元素类型是key本身

  • 映射的元素类型是key,value二元组

  • 在集合中按照key查找一个元素时,一般只是确定这个元素是否存在。

  • 映射中按照key查找时,除了能确定是否存在外,还可以得到相应的附加数据

多重集合和多重映射

  • 多重集合允许有重复元素的集合,多重映射允许一个key对应多个value
  • 多重集合与集合,多重映射与映射的用法差不多

函数对象

函数对象

  • 一个行为类似函数的对象
  • 可以没有参数,也可以带有若干参数
  • 其功能是获取值,或者改变操作状态
  • 普通函数就是函数对象
  • 重载了 “()” 运算符的类的实例是函数对象

函数适配器

  • 绑定适配器
    • bind1st, bind2nd: 将n元函数对象的指定参数绑定为一个常数,得到n-1元函数对象
  • 组合适配器
    • not1,not2: 将指定谓词的结果取反
  • 函数指针适配器
    • ptr_fun: 将一般函数指针转换为函数对象,使之能够作为其他函数适配器的输入
    • 在进行参数绑定或其他转换的时候,通常需要函数对象的类型信息
  • 成员函数适配器:ptr_fun,ptr_fun_ref
    • 对成员函数指针使用,把n元成员函数适配为n + 1元函数对象,该函数对象的第一个参数为调用该成员函数时的目的对象
    • 也就是 object->method() 变成 method(object)
    • 将 object->method(arg1) 变成 method(object, arg1)

STL算法

本身是一种函数模板

  • 通过迭代器获得输入数据
  • 通过函数对象对数据进行处理
  • 通过迭代器将结果输出

STL算法是通用的,独立于具体的数据类型,容器类型

分类

  • 不可变序列算法
    • 不直接修改所操作的容器内容的算法
    • 用于查找指定元素,比较是否相等,对元素进行计数等
  • 可变序列算法
    • 可修改所操作的容器对象的算法
    • 包括复制,删除,替换,洗牌及生成一个序列的算法
  • 排序和搜索算法
    • 对序列排序
    • 搜索
    • 堆算法
  • 数值算法
    • 求和,求差,积等

数据库系统原理第十一节

数据库编程

存储过程体

使用 declare 声明局部变量

1
declare cid int(10) 0;
  • 只能值begin end中声明
  • 必须在存储过程开头声明
  • 不同于用户变量
  • 作用范围仅限于声明他的begin end语句块

局部变量和用户变量的区别

  • 局部变量前面没有@符号,并且他只能被声明的begin end语句块中使用
  • 用户变量在声明时前面有@符号,同时已声明的用户变量存在于整个会话之中

使用 set 为局部变量赋值

1
set cid = 910;

select id into cid table

流程控制语句

条件判断
if 条件 then
表达式1
else
表达式2
end if

循环语句

  • while
  • repeat
  • loop

iterate语句
用于表示退出当前循环

declare cursor创建游标

1
declare cursor_name cursor for select_statement

open 打开游标

1
open cursor_name

使用 fetch into 读取游标数据
读取已打开的游标

1
fetch cursor_name into var_name

使用 close 关闭游标

1
close cursor_name

存储函数

由SQL语句和过程式语句组成

使用 create function 创建

1
2
3
create function sp_name(参数)
returns type
routine_body //主体

给定id号返回性别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use test;
delimiter $$
create function fn_search(cid int)
returns char(20)
deterministic
begin
declare sex char(20)
select cust_sex into sex from customers where id = cid;
if sex is null then
return(select '没有该客户');
else
if sex = 'F' then
return(select '女');
else
return(select '男');
end if;
end if;
end $$

存储函数和存储过程的区别
存储函数不能有输出参数,存储过程有
存储函数必须包含一条return语句,存储过程不包含return
存储函数可以直接调用,不需要call,存储过程必须call

使用 select 调用存储函数

1
select fn_search(1)$$

删除存储函数

1
drop function fun_name

数据库安全和保护

数据库完整性

正确性和相容性

  • 列级约束,包含列的类型,取值范围,精度
  • 元组约束,指元组中各个字段之间的约束
  • 表级约束,指若干元组,关系之间的联系的约束

实体完整性:主键约束和候选键约束

主键

  • 一个表只能有一个主键
  • 主键唯一,不能为NULL
  • 复合主键不能包含不必要的多余列
  • 一个列名在复合主键的列表中只能出现一次

主键约束:primary key, create table 或 alter table,一个表只能创建一个主键
候选键约束:unique, create table 或 alter table,可以定义若干个候选键

参照完整性
1
2
3
4
5
references table_name(index_col_name)
[on delete reference_option]
[on update reference_option]

restrict|cascade|set null|no action

数据库系统原理第十二节

数据库安全和保护

数据库完整性

1
2
3
4
5
6
7
8
create table orders
order_id int not null auto_increment,
cust_id int not null default 0,
primary key(order_id),
foreign key(cust_id)
refrences table_name(id)
on delete restrict
on update restrict

用户定义完整性约束

非空 约束 not null
check 约束 check(sex = ‘f’)

命名完整性约束 constraint[指定的约束名字]
只能给基于表的完整性约束指定名字
基于列的不行

更新完整性约束
使用alter table更新与列或表有关的各种约束

  • 完整性约束不能直接修改(先删除,再增加)
  • alter table 独立删除完整性约束,而不会删除表,drop table删除表也会删除完整性约束

触发器

用户定义在关系表上的一类由事件驱动的数据对象,也是一种保证数据完整性的方法

创建触发器

1
2
create trigger 名称 时间 事件
onfor each row 主体

插入数据时改变变量值

1
2
create trigger t_name after insert
on table_name for each row set @str = 'one customer added';

使用触发器

insert 触发器

  • 可引用一个名为 new 的虚拟表,来访问被插入的行
  • before insert , new 中的值可以被更新
    delete 触发器
  • 可引用 old的虚拟表,来访问被删除的行
  • old 只读
    update 触发器
  • 可引用old的虚拟表来访问update执行前的值,也可以引用new访问更新后的值

安全性与访问控制

数据库安全:指保护数据库以防止不合法的使用而造成数据泄露,更改或破坏,所以安全性对于任何一个DBMS来说都是至关重要的

  • 身份验证
  • 数据库用户权限确认

查看用户

1
select user from mysql.user

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

模板和群体数据

模板

函数模板

如果不使用函数模板,需要写多个函数,比如求绝对值

  • 整数绝对值
  • 浮点数绝对值
1
2
3
4
5
6
7
int abs(int x) {
return x < 0 ? -x : x;
}

double abs(double x) {
return x < 0 ? -x : x;
}

使用模板函数可以只写一个函数

1
2
3
4
template<typename T>
T abs(T x) {
return x < 0 ? -x : x;
}

如果使用int参数调用,那么编译器会根据上面的模板生成一个int型的绝对值函数,也就是将T类型换成int类型

函数模板定义语法

  • 语法
    template <模板参数表>
    函数定义
  • 模板参数表的内容
    • 类型参数 class (或 typename) 标识符
    • 常量参数 类型说明符 标识符
    • 模板参数 template<参数表> class 标识符

注意:

  • 一个函数模板并非自动可以处理所有类型的数据
  • 只有能够进行函数模板中运算的类型 可以作为类型实参
  • 自定义的类,需要重载模板中的运算符,才能作为类型实参

类模板

类模板的作用
使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能取任意类型

声明

1
2
3
4
5
template<模板参数表>
class 类名
{

}

如果需要再类模板以外定义其成员函数,则要求采用以下的形式:

1
2
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数)

定义一个类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <class T>
class Store{
private:
T item;
bool haveValue;
public:
Store();
T &getElem();
void putElem(const T &x);
}

template<class T>
T Store<T>::getElem() {
return item;
}

template<class T>
void Store<T>::putElem(const T *x) {
item = x;
}

template <class T>
Store<T>::Store():haveValue(false){}

调用类模板

1
2
3
4
5
6
7
int main() {
Store<int> s1,s2;
s1.putElem(3);
s2.putElem(4);
cout << s1.getElem();
}

线性群体

群体的概念

  • 群体是指由多个数据元素组成的集合体
    群体可以分为两个大类:线性群体非线性群体
  • 线性群体中的元素按位置排列有序
    可以分为第一个元素,第二个元素等
  • 非线性群体不用位置顺序来标识元素

线性群体中的元素次序和逻辑位置关系是对应的。按照访问元素的不同方法分为直接访问,顺序访问索引访问

数组

直接访问的线性群体–数组类

  • 静态数组是具有固定元素个数的群体,其中的元素可以通过下标直接访问。
  • 缺点:大小在编译时确定,在运行时无法修改
  • 动态数组由一系列位置连续的,任意数量相同类型的元素组成
  • 优点:其元素个数可在程序运行时改变

链表类

链表是一种动态数据结构,可以用来表示顺序访问的线性群体
链表是由系列结点组成的,结点可以在运行时动态生成
每一个结点包括数据域和指向链表中下一个结点的指针
如果链表每个结点只有一个指向后继结点的指针,则为单链表

单链表结点模板

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
27
template<class T>
class Node{
private:
Node<T> *next;
public:
T data;
Node(const T& item, Node<T>* next = 0);
void insertAfter(Node<T> *p);
Node<T> *deleteAfter();
Node<T> *nextNode() const;
}

template<class T>
void Node<T>::insertAfter(Node<T> *p) {
p->next = next;
next = p;
}

template<class T>
Node<T>* Node<T>::deleteAfter() {
Node<T> *tempPtr = next;
if (next == 0) {
return 0;
}
next = tempPtr->next;
return tempPtr;
}

多态

链表类模板

链表的基本操作

  • 生成链表
  • 插入结点
  • 查找结点
  • 删除结点
  • 遍历链表
  • 清空链表

栈类模板

队列模板

插入排序

每一步将一个待排序元素插入已排序序列的适当位置

选择排序

每次从待排序序列中选择一个最小的元素,顺序排在已排序序列的最后

数据库系统原理第十节

数据库设计

数据查询

视图

什么是视图

  • 视图是一个对象,他是数据库提供给用户的以多种角度观察数据库中数据的一种重要机制
  • 视图不是数据库中真实的表,而是一张虚拟表,其自身并不存储数据

视图的优点

  • 集中分散数据
  • 简化查询语句
  • 重用SQL语句
  • 保护数据安全
  • 共享所需数据
  • 更改数据格式
创建视图

or replace 防止报错,存在替换,不存在创建
with check option 增删改查的时候检查视图条件

1
2
3
create or replace view view_name [(col_list)]
as select_statement
with check option
删除视图

drop view view_name

修改视图
1
2
3
alter view view_name [(col_list)]
as select_statement
with check option
查看视图定义
1
show create view view_name
更新视图数据
1
2
insert into table_name 
values(value1,...);
1
update table_name set col_name = 'value'
删除视图数据
1
delete from table_name where ...
查询视图数据

select

数据库编程

存储过程

存储过程 是一组为了完成某项特定功能的 SQL语句集

  • 可增强SQL语言的功能和灵活性
  • 良好的封装性
  • 高性能
  • 可减少网络流量
  • 可作为一种安全机制来确保数据库的安全性和数据的完整性
    其实质就是一段存储在数据库中的 代码
    它可以由声明式的sql语句和过程式sql语句组成

创建存储过程

DELIMITER $$ //用户定义的MYSQL 结束符

参数:in|out|inout 参数名 参数类型

1
2
3
4
5
DELIMITER $$
create procedure sp_name(参数)
BEGIN
body //存储过程代码
END $$

调用存储过程

call sp_name(参数)

删除存储过程

drop procedure sp_name

数据库系统原理第九节

数据库设计

数据查询

where 子句和条件查询

between 2 and 4 包含2,4

in (1,2,4)

is null

is not null

子查询

表子查询
行子查询
列子查询
标量子查询

比较运算符包括

  • ALL
  • SOME
  • ANY

结合exists

group

group by id asc|desc with rollup

having

group by id having count(*) < 3

order

order by id asc|desc

group 和 order的差别
group order
分组行,但输出可能不是分组的排序 排序产生的输出
只能使用选择列或表达式列 任意列都可以使用
若与聚合函数一起使用列或表达式, 则必须使用group 不一定需要
limit

limit 1,10

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

多态

运算符重载

重载规则

C++ 几乎可以重载全部的运算符,而且只能够重载C++中已经有的

  • 不能重载的:”.”,”.*”,”::”,”?:”

重载之后运算符的优先级和结合性都不会改变

运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。

例如:

  • 使复数类的对象可以用 + 运算符实现加法
  • 是时钟类对象可以用 ++ 运算符实现时间增加1秒

重载为类的非静态成员函数
重载为非成员函数

双目运算符重载为成员函数

重载为类成员的运算符函数定义形式:

1
2
3
4
函数类型 operator 运算符(形参)
{

}

参数个数 = 原操作数个数 - 1(后置++,–除外)

双目运算符重载规则

  • 如果要重载B为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为 A类对象,则B则应被重载为A的成员函数,形参类型应该是 oprd2 所属类型
  • 经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)

例子8-1 复数类加减法运算重载为成员函数

  • 要求
    将 +,-运算重载为复数类的成员函数
  • 规则
    实部和虚部分别相加减
  • 操作数
    两个操作数都是复数类的对象
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
using namespace std;

class Complex{
public:
Complex(double r = 0.0, double i = 0.0):real(r),imag(i) {};
//运算符+重载成员函数
Complex operator +(const Complex &c2) const;
//运算符-重载
Complex operator -(const Complex &c2) const;
//运算符 * 重载
Complex operator *(const Complex &c2) const;
void display() const;
private:
double real;
double imag;
};

Complex Complex::operator+(const Complex &c2) const {
return Complex(real + c2.real, imag + c2.imag);
}

Complex Complex::operator-(const Complex &c2) const {
return Complex(real - c2.real, imag - c2.imag);
}

Complex Complex::operator*(const Complex &c2) const{
return Complex(real * c2.real, imag * c2.imag);
}

void Complex::display() const {
cout << "(" << real << "," << imag << ")" << endl;
}

int main() {
Complex c1(1,2),c2(5,6);
c1.display();
c2.display();
Complex c3 = c1 + c2;
c3.display();
Complex c4 = c1 * c2;
c4.display();
}

单目运算符重载成为成员函数

前置单目运算符重载规则

  • 如果要重载U为类成员函数,使之能够实现表达式 U oprd, 其中 oprd 为 A 类对象,则 U 应被重载为 A 类的成员函数,无形参。
  • 重载后,表达式 U oprd 相当于 oprd.operator U()

后置单目运算符 ++ 和 – 重载规则

  • 如果要重载 ++ 或 – 为类成员函数,使之能实现表达式 oprd++ 或 oprd–,其中 oprd 为 A 类对象,则 ++,–应被重载为A类的成员函数,且具有一个int类型的形参
  • 经重载后,表达式 oprd++ 相当于 oprd.operator ++(0)
1
2
3
4
5
6
7
8
9
10
11
Complex & Complex::operator++() {
real++;
imag++;
return *this;
}

Complex Complex::operator++(int ) {
Complex that = *this;
++(*this);
return that;
}

运算符重载为非成员函数

规则

  • 函数的形参代表依自左至右次序排列的各操作数
  • 重载为非成员函数时
    • 参数个数 = 原操作个数(后置++,–除外)
    • 至少应该有一个自定义类型的参数
  • 后置单目运算符 ++ 和 – 的重载函数,形参列表中要增加一个int, 但不必写形参名
  • 如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元

双目运算符重载

  • oprd1 B oprd2 等于 operator B(oprd1,oprd2)

前置单目重载

  • B oprd 等于 operator B(oprd)

后置单目++,–重载

  • oprd B 等于 operator B(oprd, 0)

虚函数

虚函数

  • virtual 定义虚函数 使用运行时多态
  • 虚函数必须在类外实现函数体
  • 虚函数必须是非静态的成员函数,经过派生后,就可以实现运行时多态

什么函数可以是虚函数

  • 一般成员函数
  • 构造函数不能是虚函数
  • 析构函数可以

virtual 关键字

  • 派生类可以不显式的用virtual声明虚函数,这时系统就会用以下规则来判断派生类的一个函数成员是不是虚函数:
    • 该函数是否与基类的虚函数有相同的名称,参数个数及对应参数类型
    • 该函数是否与基类的虚函数有相同的返回值或者满足类型兼容规则的指针,引用型的返回值
  • 如果从名称,参数及返回值三个方面检查之后,派生类的函数满足上述条件,就会自动确定为虚函数。这时,派生类虚函数便会覆盖基类的虚函数
    • 派生类中的虚函数还会隐藏基类中同名函数的所有其他重载形式
    • 一般习惯于在派生类的函数中也使用 virtual 关键字,以增加程序的可读性

虚析构函数

通过基类指针调用对象的析构函数,就需要让基类的析构函数成为虚函数,否则 delete 的结果是不确定的

写成 虚析构函数,那么delete就会执行派生类的析构函数,不然只会静态绑定,永远执行基类的析构函数,派生类的成员就无法delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Base{
public:
//基类 虚析构函数
virtual ~Base();
}

class A:public Base{
public:
virtual ~A();
}

void fun(Base* B) {
delete B;
}

int main() {
Base* b = new A();
fun(b);
return 0;
}

虚表和动态绑定

虚表

  • 每个多态类有一个虚表
  • 虚表中有当前类的各个虚函数的入口地址
  • 每个对象有一个指向当前类的虚表的指针(虚指针 vptr)

动态绑定的实现

  • 构造函数中为对象的虚指针赋值
  • 通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
  • 通过该入口地址调用虚函数

抽象类

纯虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本

纯虚函数的声明:
virtual 函数类型 函数名(参数表) = 0;

带有纯虚函数的就是抽象类

抽象类的作用

  • 将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为
  • 对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现

注意

  • 抽象类只能作为基类使用
  • 不能定义抽象类的对象

override

override

  • 多态行为的基础:基类声明虚函数,派生类声明一个函数覆盖该虚函数

  • 覆盖要求:函数签名(signature)完全一致

  • 函数签名包括:函数名 参数列表 const

  • C++引入显式函数覆盖,在编译器而非运行期捕获此类错误

  • 在虚函数显式重载中运用,编译器会检查基类是否存在一虚拟函数,与派生类中带有声明override的虚拟函数,有相同的函数签名;若不存在,则回报错误

final

final 类

  • 不能被继承
  • 继承出现编译错误

final 方法

  • 不能被覆盖
  • 覆盖出现编译错误

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

继承与派生

继承的基本概念和语法

继承与派生

  • 继承与派生是同一过程从不同的角度看
    • 保持已有类的特性而构造新类的过程称为继承
    • 在已有类的基础上新增自己的特性而产生新类的过程称为派生
  • 被继承的已有类称为基类
  • 派生出的新类称为派生类
  • 直接参与派生出某类的基类称为直接基类
  • 基类的基类甚至更高层的基类称为间接基类

继承的目的

  • 实现设计与代码重用

派生的目的

  • 当新的问题出现,原有程序无法解决时,需要对原有程序进行改造

单继承时候派生类的语法

1
2
3
4
class 派生类名:继承方式 基类名
{

}

多继承时语法

1
2
3
4
class 派生类名:继承方式 基类名, 继承方式 基类名
{

}

派生类的构成

  • 吸收基类成员
    • 默认情况下派生类包含了全部基类中除构造和析构函数之外的所有成员
    • C++11 规定可以用using语句继承基类构造函数
  • 改造基类成员
  • 添加新的成员

继承方式

公有继承

不同继承方式区别

  • 派生类成员对基类成员的访问权限
  • 通过派生类对象对基类成员的访问权限

三种继承方式

  • 公有
  • 私有
  • 保护

公有继承

  • 继承的访问控制
    • 基类的 publicprotected 成员 :访问属性在派生类保持不变
    • 基类的 private 成员:不可直接访问
  • 访问权限
    • 派生类中的成员函数:可以直接访问基类中的 publicprotected 成员,但不能直接访问基类的 private 成员
    • 通过派生类的对象:只能访问 public 成员

私有继承和保护继承

私有继承

  • 继承的访问控制
    • 基类的 publicprotected 成员:变成 private 成员
    • 基类的 private 成员:不可直接访问
  • 访问权限
    • 派生类中的成员函数:可以直接访问基类中的 publicprotected 成员,但不能直接访问基类的 private 成员
    • 通过派生类的对象:不能访问任何成员

保护继承

  • 继承的访问控制
    • 基类的 publicprotected 成员:变成 private 成员,都已变成 protected 成员
    • 基类的 private 成员:不可直接访问
  • 访问权限
    • 派生类中的成员函数:可以直接访问基类中的 publicprotected 成员,但不能直接访问基类的 private 成员
    • 通过派生类的对象:不能直接访问

protected 成员的特点与作用

  • 对建立其所在类对象的模块来说,它与private的性质相同
  • 对于其派生类来说,它与public性质相同
  • 既实现了数据隐藏,又方便继承,实现代码重用

基类与派生类转换

转换

  • 公有派生类对象可以被当做基类的对象使用,反之则不可
    • 派生类的对象可以隐含转换为基类对象
    • 派生类对象可以初始化基类的引用
    • 派生类的指针可以隐含转换为基类的指针
  • 通过基类对象名,指针只能使用从基类继承的成员

派生类的构造和析构

派生类的构造函数

  • 基类的构造函数不被继承
  • 派生类需要定义自己的构造函数

C++11 规定

  • 可用 using 语句 继承基类构造函数
  • 但是 只能 初始化从基类继承的成员
  • 语法形式:
    • using B::B

若不继承构造函数

  • 派生类新增成员:派生类定义构造函数初始化
  • 继承来的成员:自动调用基类构造函数进行初始化
  • 派生类的构造函数需要给基类的构造函数传递参数

单继承 A继承与B

1
2
3
4
5
6
class A:public B{
A(int a, int b);
}
A::A(int a, int b): B(b),a(a) {

}

多继承 A 继承 B,C

1
2
3
4
5
6
class A:public B,public C{
A(int a, int b, int c);
}
A::A(int a, int b, int c): B(b), C(c), a(a) {

}
  • 当基类有默认构造函数时
    • 派生类构造函数可以不向基类构造函数传参
    • 构造派生类对象,基类默认构造函数将被调用
  • 如需执行基类中带参数的构造函数
    • 派生类需要向基类构造函数传参

派生类的复制构造函数

若派生类没有声明复制构造函数

  • 编译器会在需要时生成一个隐含的复制构造函数
  • 先调用基类的复制构造函数
  • 再为派生类新增的成员执行复制

若声明复制构造函数

  • 一般都要为基类的复制构造函数传递参数
  • 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将传递给基类的复制构造函数
  • 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用

派生类的析构函数

  • 析构函数不被继承,派生类如果需要,要自行声明析构函数
  • 声明方法与无继承关系时类的析构函数相同
  • 不需要显式调用基类的析构函数,系统会隐式调用
  • 限制性派生类析构函数,在执行基类析构函数

派生类成员的标识和访问

访问从基类继承的成员

当派生类与基类中有相同成员时

  • 若未特别限定,则通过派生类对象使用的是派生类中的同名成员
  • 如果通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符::
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class B{
public:
f();
}
class A:public B{
public:
f();
}

class main() {
A a();
a.f(); //调用派生类 f 函数
a.B::f(); //调用基类 f 函数
}

二义性问题

  • 如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员 存在二义性
    • 派生类对象名或引用名.成员名
    • 派生类指针->成员名
  • 解决方式:用类名限定

虚基类

要解决的问题:

  • 当派生类从多个基类派生,而这些基类又有共同基类,则在访问此共同基类中的成员时,将产生冗余,并可能带来不一致性

虚基类声明

  • virtual 说明基类继承方式
  • 例如: class B1:virtual public B

作用

  • 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题
  • 为最远的派生类提供唯一的基类成员,而不重复产生多次复制

注意

  • 在第一级继承时就要将共同基类设计为虚基类

虚基类及派生类构造函数

  • 建立对象时候所指定的类称为最远派生类
  • 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中为虚基类的构造函数列出参数。如果未列出,则表示调用该虚基类的默认构造函数
  • 在建立对象时,只有 最远派生类 的构造函数调用虚基类的构造函数,其他类对虚基类构造函数的调用被忽略

数据库系统原理第八节

数据库设计

数据更新

插入数据

insert values

1
insert into table_name[(col_name)] values ();

insert set

1
2
insert into table_name
set col_name = '值', col_name = '值';

insert select

1
2
insert into table_name
select * from table_name;

删除数据

1
delete from table_name where id = 1

修改数据

1
2
update table_name set col_name = '值' where id = 1

数据查询

select 语句

select * from table_name

列的选择与指定

select col_name from table_name

定义别名

select col_name as alias from table_name

替换查询结果集中的数据

1
2
3
4
5
case 
when 条件1 then 表达式1
when 条件2 then 表达式2
else 表达式
end as alias

计算列值

select col_name + 100 from table_name

from 子句与多表连接查询

交叉连接,笛卡尔积

select * from table_namme1 cross join table_name2

简写:
select * from table_name1,table_name2;

内连接

select col_name from table_name inner join table_name2 on table_name.id = table_name2.t_id;

外连接

left join

right join