Cpp 单例模式
单例模式作为最简单但最常用的设计模式,整理一下 C++的相关语法吧。 单例模式简介 单例模式(Singleton Pattern),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例被构造,并提供一个访问它的全局访问接口,该实例被程序的所有模块共享。 大致的做法为: 定义一个单例类; 私有化构造函数,防止外界直接创建单例类的对象; 禁用拷贝构造,移动赋值等函数,可以私有化,也可以直接使用=delete; 使用一个公有的静态方法获取该实例; 确保在第一次调用之前该实例被构造。 在 C++中我们需要考虑: 在什么时机构造?程序一开始,第一次调用前? 如何构造这个实例?在堆上构造,还是通过静态变量?如果单例在堆上构造,是否存在内存泄漏? 实例构造失败会如何?抛异常?返回空指针? 通过接口返回指针还是引用? 线程安全?多线程下会不会重复构造?加锁? 我们暂时不考虑类的继承问题,以及需要给单例的构造传参数的问题。 关于构造时机的不同,有以下两种习惯的称呼: 饿汉模式(Eager Singleton),在程序启动后立刻构造单例; 懒汉模式(Lazy...
Cpp 基本数据类型
C/C++很烦人的一点在于,它最基本的数据类型都是不确定的,为了兼容某些奇怪的设备,C++标准并没有强制规定基本数据类型的位数,这可能导致很多 bug。 我们只考虑 64 位系统,考虑 x86-64 的 Windows/Linux 平台(32 位系统可能字节数更少,但是已经很少用了,本文不考虑)。 非常不建议使用整数类型long,以及浮点数类型long double,因为它们在不同的平台很可能位数不同。 整数类型 基本的整数类型大概有如下几种:(有符号类型,还有对应的无符号类型) char short (int) int long (int) long long (int) 注意:标准并没有严格规定它们的字节数大小,但是规定了字节数的大小关系(即表示范围的大小关系),以及它们至少需要的字节数。 1short <= int <= long <= long long 主要参考 cpp reference 和 wiki,下图取自cpp reference 上图中的 64 位数据模型包括 LP64(主要对应 Unix-like 平台)和 LLP64(主要对应...
Cpp 历史介绍与学习资源
C++历史和标准 简要回顾 C++的起源: 1979 年,Bjarne Stroustrup(C++之父)受到 Simula 语言(首个支持面向对象的语言)的启发,将面向对象的语法和 C 语言结合,得到了最早版本的"C with Classes",它提供面向对象的基本语法,然后先翻译成 C 的源码进一步进行处理。 1983 年,这个新语言正式被命名为 C++。许多重要的特性被加入,包括引用机制(符号为&)、虚函数、函数重载、const 关键字等。 1985 年,Stroustrup 的 C++参考手册《C++ Programming Language》出版,此时并没有 C++语法规范,只有这份参考手册。 1998 年,C++标准委员会发布了 C++语言的第一个国际标准:C++98。最早的标准模块库(Standard Template Library,STL)也被纳入了该版标准。 C++的主要标准: C++98: 第一个标准。 C++03: C++98 的修订版。 C++11: C++98之后最重要的一个标准,它带来了巨大的变革,此后的 C++称为 Modern...
Euler方程组笔记
关于 Euler 方程组的一些常用的基本知识。 控制方程与状态方程 一维的可压缩流体满足 Euler 方程组,主要由三个控制方程组成 \[ \begin{pmatrix} \rho \\ \rho u \\ E \end{pmatrix}_t + \begin{pmatrix} \rho u \\ \rho u^2 + p \\ u(E + p) \end{pmatrix}_x = 0 \] 分别对应质量守恒、动量守恒和能量守恒。涉及的记号含义为: 密度(density) \(\rho=\rho(x,t)\); 速度(velocity)\(u=u(x,t)\); 压强(pressure)\(p=p(x,t)\); 单位体积的总能量(total energy)\(E=E(x,t)\)。 这里还缺少一个方程与控制方程组一起组成封闭的方程组。 可压缩流体的单位体积总能量\(E\)包括动能和内能两部分 \[ E = \frac12 \rho u^2 + \rho e \] 其中\(e\)是比内能(specific internal energy),由流体的压强和密度决定:\(e =...
Cpp练习——SafeInput获取安全的输入
概述 在学习 C/C++ 的一开始,我们就接触到基本的输入输出,例如printf/scanf或者cout/cin, 但是在涉及用户输入时总是存在可靠性问题:我们无法确保用户输入的信息是合法的,比如要求输入一个数字,但是用户提供的是一串字符串等等,或者要求数字满足一定范围等,我们只能每次手动在外层加一个循环和判断条件,非常繁琐。 在本文中实现了一个简单的获取安全输入的封装SafeInput,使得在编程中尽可能摆脱这类涉及用户输入参数的处理细节。 使用示例 指定获取一个int类型的值,要求大于 0 且小于 5,如果输入不满足要求,则会提示重新输入 12int s = SafeInput().get<int>("Please input a int in (0,5): ", [](int p) { return 0 < p && p < 5;...
Mathematica生成高阶微分公式
本文包括常见的高阶的有限差分公式,以及使用 Mathematica 自动计算数值微分公式的代码。 原理就是基于待定系数法求解得到如下形式的有限差分公式 \[ f^{(\alpha)}(x) = \frac{1}{h^\alpha}\left( c_{-m} f(x-m h) + c_{-m+1} f(x-(m-1)h) +\cdots + c_n f(x+n h) \right) + O(h^\beta) \] 其中的\(m,n \ge 0\) 代表模板宽度,要求\(m+n \ge \alpha\)。 Mathematica 源代码 1234567891011121314FDFormula[m_, n_, a_, {f_, x_, h_}] := Module[{mat, b, coeff, expr, i, j}, mat = Table[If[j == 0, 1, i^j], {j, 0, m + n}, {i, -m, n}]; b = Table[If[i == a, a!, 0],...
INSE学习笔记——7. ALE方法
任意拉格朗日法是一种用于处理动边界问题的方法,尤其在流体问题中得到应用,以这个方法的学习作为这一系列笔记的结束。 参考文献如下 Donea, Jean, Antonio Huerta, and A Rodrıguez-Ferran. “Chapter 14 Arbitrary Lagrangian–Eulerian Methods,” n.d. Fehn, Niklas, Johannes Heinz, Wolfgang A. Wall, and Martin Kronbichler. “High-Order Arbitrary Lagrangian-Eulerian Discontinuous Galerkin Methods for the Incompressible Navier-Stokes Equations.” Journal of Computational Physics 430 (April 2021): 110040. https://doi.org/10.1016/j.jcp.2020.110040. 1. 不可压 NS 方程 在区域...
Gauss数值积分
关于 Gauss 数值积分公式的推导以及相关性质的笔记,注意并不是与正态分布相关的高斯积分 \(\int e^{-x^2}\,dx\),在 wiki 上 Gauss 数值积分公式又称为 Gauss 求积公式。 在本文中使用的下标范围为 \(0-n\) 而非 \(1-n\),这样与多项式次数更加对应,但是与某些资料上的公式不一致。 Gauss 积分推导 最优代数精度 对于标准积分区间 \([-1,1]\) 的数值积分公式,节点和系数的选取有 2n+2 个自由度,至多可以达到 2n+1 阶的代数精度。 \[ I(f) = \int_{-1}^1 f(x)\,dx \approx I_n(f) = \sum_{i=0}^n A_i f(x_i) \] 断言:无论节点和系数如何选取,上述形式的数值积分公式不可能具有 2n+2 阶的代数精度。 证明断言:构造 2n+2 次多项式 \(p(x)\) \[ p(x) = \Pi_{i=0}^n (x-x_i)^2 \ge 0 \] 显然有 \[ \begin{aligned} 0 < \int_{-1}^1 p(x)\,dx =...
最优化——牛顿法和拟牛顿法
主要内容是最优化中的牛顿法和拟牛顿法,还包括最简单的最速下降法,都是用于求解无约束最优化问题的梯度类最经典算法。 对于无约束最优化问题 \[ \min_{x \in R^n} f(x) \] 通用的算法流程如下: 初始化,选取起点 \(x^0\),记 \(k=0\) 在当前位置 \(x^k\),获取搜索方向 \(d^k\),通常是基于当前位置的一阶导和二阶导信息 在给定方向上进行一维搜索,\(a^k = \text{argmin}_{a \ge 0} f(x + a d^k)\),获取步长因子 \(a^k\) 更新 \(x^{k+1} = x^k + a^k d^k\),\(k=k+1\),回到第二步 关于一维搜索的细节,这里不做讨论,这篇笔记只关注搜索方向的确定。 最速下降法 每一次的搜索相当于对 \(f\) 进行了一阶的泰勒展开。 \[ \begin{aligned} f(x) &= f(x^k) + \nabla f(x^k)^T (x-x^k) + O(|x-x^k|^2) \\ \end{aligned} \] 这里记...
一维最优化问题
本文只考虑最简单的一维问题,包括求根和求最小值点两个子问题: 非线性方程求根 \(f(x)=0\) 非线性方程求最小值点 \(x_0 = \text{argmin} f(x)\) 精确一维搜索 非精确一维搜索 由于线性问题是平凡的,因此默认\(f(x)\)是非线性的。 非线性方程求根 对分法 假设\(f(x)\)在有限区间\([a,b]\)满足:\(f(a)f(b)<0\) 异号,那么\(f(x)\)在\((a,b)\)至少有一个零点 \(x^*\)。 算法: 起始的计算区间 \([x_l,x_r]=[a,b]\),此时满足两端异号 \(f(x_l)f(x_r)<0\) 。 选取中点 \(x_m = \frac12(x_l+x_r)\) ,计算\(f(x_m)\)。 如果中点 \(|f(x_m)| \le \varepsilon\),或者区间长度\(|x_r-x_l|\le \varepsilon\) 已经足够小,则认为中点已经足够精确,已经找到零点,求解完成。 否则左右两个区间...