Cpp 面向对象——虚函数进阶
前面简单介绍了重定义和重写,以及重写所涉及到的虚函数的基本概念,这里整理一下虚函数和动态多态的进阶内容。 C++使用虚函数机制来实现运行期的多态(动态多态),与之相对的是编译期的多态(静态多态), 对于C++来说,静态多态可以通过函数重载和泛型编程实现,而动态多态则通过虚函数实现。 在讨论的范围内,基类或派生类指针/引用的使用方法通常是完全等效的,示例主要以指针的使用为主。 为了简化讨论,我们不关注public,private,protected修饰的区别,无论是对类的方法还是继承关系的修饰,统一使用public。 我们不关注多继承、菱形继承以及虚继承等复杂情景,只考虑简单的单继承情景,不考虑模板类的处理。 虚析构函数 对于含有虚函数的类,非常建议将基类的析构函数也设置为虚函数,此时派生类的析构函数也全部会自动变为虚函数,这可以保证基类指针指向派生类时, 在销毁时可以正确调用派生类的析构函数。 考虑下面的例子 123456789101112131415161718192021222324252627282930#include <iostream>struct...
Cpp 面向对象——重定义与重写、虚函数
在一个作用域中,同名函数但是形参列表不同的函数构成重载的关系, 在考虑面向对象时,则会产生一系列新的问题,因为基类和派生类的作用域是一种非常微妙的关系,既不能说它们是简单的两个独立的作用域,也不能说是一个作用域,而且这种关系是天然不对称的。 在面向对象的语法中对基类和派生类的同名函数(相同或者不同的形参列表)都有着特殊的设计,在本文中我们主要讨论的是不涉及虚函数的部分。 为了简化讨论,我们不关注public,private,protected修饰的区别,无论是对类的方法还是继承关系的修饰,统一使用public。 我们不关注多继承、菱形继承以及虚继承等复杂情景,只考虑简单的单继承情景,不考虑模板类的处理。 重定义(隐藏) 例子 假设基类Base定义了一个方法hello,有好几个版本,相互之间构成重载关系。 派生类Derived如果没有实现hello方法,那么派生类对象仍然是可以直接调用hello方法的,包括基类所实现的各种版本,通过重载决议调用,这是继承所赋予的特点。 例如 12345678910111213141516#include...
C/Cpp 类型别名
typedef typedef是用于为类型定义别名的关键字,在C和C++中都可以使用。 最简单的用法是对基本类型起别名,例如 1typedef int Length; 下面的是MSVC的stdint.h中的部分源码,对基本数据类型起了意义更明确的别名 12345678typedef signed char int8_t;typedef short int16_t;typedef int int32_t;typedef long long int64_t;typedef unsigned char uint8_t;typedef unsigned short uint16_t;typedef unsigned int uint32_t;typedef unsigned long long uint64_t; 数组类型和指针类型可以通过typedef起别名达到简化语法和提升可读性的目的,例如 12345typedef int IntArray4[4];typedef...
Cpp 类型转换
关于C++的类型转换的整理笔记,主要关注C++相比于C语言多的部分,而且讨论的范围比较简单,不关注涉及到右值引用的类型转换。 概述 C++基本继承了C语言的类型转换规则,但是对类型转换的处理有很多区别,例如: 对于C语言,隐式转换和显式转换并没有太多区别,仅仅是危险的隐式转换可能会发出警告,使用显式转换会消除警告。 对于C++,安全的隐式转换可以通过编译,但是危险的隐式转换无法通过编译,必须使用显式转换才能顺利编译,C语言风格或C++风格的显式转换均可。 此外,C++相对于C语言还需要考虑很多很多额外的内容,我们将主要关注面向对象的部分,即自定义类型之间的类型转换,以及新加入的四个显式的强制类型转换关键词的使用 const_cast reinterpret_cast static_cast dynamic_cast 现代 C++ 不建议这么做: 避免使用C语言中常用的void *类型,C++提供了更好的工具; 避免使用C语言风格的显式类型转换,可读性太差,不利于后期检查。 现代 C++...
C语言类型转换
...
Cpp 函数参数默认值
C语言不支持函数重载,也不支持函数参数的默认值,这既可以说体现了C语言的简陋,也可以说是避免了很多麻烦。 C++的函数参数支持默认值的机制就比较烦人,因此需要整理一下。 为了提高代码的可读性,C++尽量也不要使用函数默认值,在讨论的最后提供了几种简单的方式可以替代。 基础 C++支持给函数参数提供默认值,例如 12345678910void func(int a, int b=1){ ...}int main(){ func(1,2); func(3); // == func(3,1), a=3,b=1 return 0;} C++要求参数的默认值必须从右向左连续地提供,保证无默认值的参数不能出现在有默认值的参数的右侧,否则编译器无法判断参数缺省时的对应关系,这是语法错误,例如 1234567void func(int a, int b=1, int c){ // compile error ...}void func(int a, int b=1, int c=2){ //...
Cloudreve个人云盘搭建
记录一下Cloudreve云盘的搭建。 概述 Cloudreve云盘的优点: 颜值可以; 部署很简单,连docker都不需要(还没鼓捣清楚docker),数据库之类的不用管,傻瓜式操作即可。 缺点: 功能比较简单,没什么插件扩展(其实也不需要); 部分开源,有一个付费的版本包含更多功能,社区环境不太友好; 只支持网页端和ios的app,不支持Windows和安卓(也不需要,因为有的开源网盘就算提供了app,UI也很简陋) 本地部署 Linux下直接下载压缩包到合适位置,解压执行即可完成本地部署,目前放置在~/cloudreve/目录下。 12345678#解压获取到的主程序tar -zxvf cloudreve_VERSION_OS_ARCH.tar.gz# 赋予执行权限chmod +x ./cloudreve# 启动...
C语言 数组和指针笔记
虽然在C++中完全不需要处理C语言中原始数组和原始指针等,C++提供了很多更好的替代实现,但是还是顺手整理一下吧。 数组基础 一维数组 定义数组 1int a[5]; 在定义时可以赋初值,此时可以省略数组长度,例如 1int a[] = {1,2,3,4,5}; 可以用如下方式获取数组长度 1int len = sizeof(a)/sizeof(a[0]); 如果我们不赋初值,则数组的初值是随机的,这可能导致程序BUG。 需要通过索引读写数组的元素,数组的索引从0开始,例如 123int a[5];a[0] = 100;print("%d",a[1]); C语言不会检查索引的合法性,对越界的索引进行的读写操作是未定义的危险行为,需要结合下面的指针理解。 二维数组 定义二维数组 1int a[2][3]; 二维数组赋初值 1int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; 二维数组通过行索引和列索引读写元素 123int a[2][3];a[0][1]...
密码学笔记——AES算法
现在仔细学习整理一下对称加密算法的典型代表——AES算法,并进行C++实现。 算法结构 这里我们只考虑AES-128,即密钥是128bit 的具体算法。 对于输入的明文,首先经过某种扩充处理,保证长度是128bit 的整数倍,然后对明文进行分组,每一组128bit 即16个字节的数据,在具体算法描述中通常会按列拼接为一个\(4\times 4\)的字节矩阵,并称为状态矩阵。 对于每一个分组执行相同的加密处理得到密文,将每一组的密文拼接即可得到最终的密文。 我们主要考虑单个分组的处理,下面的明文以及密钥均为16个字节的数据, 记明文为\(P\),密钥为\(K\),密文为\(C\)。 首先我们要进行密钥的扩展,从16字节的原始密钥\(K\)经过某种扩展算法得到10个辅助密钥\(K_1,\dots,K_{10}\),每一个\(K_i\)均是16字节的,对应一个状态矩阵,记\(K_0 =...