Init
This commit is contained in:
351
02-Note/读书笔记/地平线的世界设计.md
Normal file
351
02-Note/读书笔记/地平线的世界设计.md
Normal file
@@ -0,0 +1,351 @@
|
||||
地平线的世界设计
|
||||
|
||||
1、开方世界——玩家有很大的探索区域
|
||||
|
||||
2、昼夜循环
|
||||
|
||||
3、动态天气
|
||||
|
||||
4、史诗般的场景
|
||||
|
||||
5、天空是地形的一部分
|
||||
|
||||
|
||||
|
||||
•地平线是一个巨大的开放世界,你可以去任何你看到的地方,包括山顶。(我表示呵呵,这个已经被吐槽,真正做到的只有荒野之息了)
|
||||
|
||||
•由于这是一个活生生的现实世界,我们模拟地球的自转周期。
|
||||
|
||||
•天气是环境的一部分,因此它也会发生变化和演变。
|
||||
|
||||
•有很多史诗般的风景:高山,森林,平原和湖泊。
|
||||
|
||||
•天空是地平线的一个重要组成部分。他们占了屏幕的一半。天空也是故事和世界建筑的重要组成部分。
|
||||
|
||||
五
|
||||
|
||||
|
||||
|
||||
云的实现目标
|
||||
|
||||
1、美术方面的可操控性
|
||||
|
||||
2、真实性(需要实现多种云的类型)
|
||||
|
||||
3、与天气继承
|
||||
|
||||
4、可移动的
|
||||
|
||||
5、EPIC:叫我干什么?
|
||||
|
||||
|
||||
|
||||
早期的模拟方案:
|
||||
|
||||
1、使用流体解算器,(MAYA、houdini),效果很好。但是工作室模拟云彩的经验比较少……说到底就是没钱请不来影视特效大佬
|
||||
|
||||
2、使用简单的几何体配合体素,然后运行流体解算器直到他们像云。
|
||||
|
||||
3、然后我们开发了一种照明模型,用于预先计算主和次散射。在Houdini完成解算需要10S时间
|
||||
|
||||
4、把云转换层多边形,烘培了光照等数据。再使用Billboards————缺点:无法轻易生成云阴影(本人表示严重质疑)
|
||||
|
||||
5、我们试着把我们所有的voxel云作为一个云来生产,它也可以混入大气层。类型的工作。在这一点上,我们后退一步,来评估什么行不通。没有一个解决方案使云随着时间的推移而进化。没有一种好的方法可以使云层通过头顶。并且对所有方法都有很高的内存使用和透支。所以,也许传统的基于资产的方法是不可取的。
|
||||
|
||||
6、于是开始考虑体素云了
|
||||
|
||||
7、所以我们进入了Houdini,并从模拟的云形状中生成了一些3d纹理。使用Houdini的GL扩展,我们构建了一个原型GL着色来开发云系统和照明模型。
|
||||
|
||||
8、这让我们看到了地平线的云系统。为了更好地解释,我把它分成了四个部分:建模、照明、渲染和优化。在了解我们对云的建模之前,我们应该对云是什么以及它们如何演变成不同的形状有一个基本的了解。
|
||||
|
||||
9、There are many examples of real-time volume clouds on the internet. The usual approach involves drawing them in a height zone above the camera using something called fBm, Fractal Brownian Motion. This is done by layering Perlinnoises of different frequencies until you get something detailed.
|
||||
|
||||
•This noise is then usually combined somehow with a gradient to define a change in cloud density over height.
|
||||
|
||||
10、由于光噪声本身不能切割,我们开发了自己的分层噪音。
|
||||
|
||||

|
||||
|
||||
Worley噪音是由Steven Worley在1996年引入的,经常用于因果关系和水的影响。如果它是倒立的,就像你在这里看到的:
|
||||
|
||||
它的形状非常紧密。
|
||||
|
||||
我们把它像标准的perlinfbm方法一样分层
|
||||
|
||||
然后,我们用它来作为对dilate perlin噪声的偏移量。这使我们能够保持perlin噪声的连接,但是增加了一些billowy的形状。
|
||||
|
||||
我们把这叫做“佩林-沃利”的噪音
|
||||
|
||||
11、在游戏中,把声音存储为tiling 3d纹理通常是最好的。
|
||||
|
||||
•你想保持纹理读取到最低…
|
||||
|
||||
•尽可能小的保持决心。
|
||||
|
||||
•在我们的例子中我们已经压缩的声音…
|
||||
|
||||
•两个3 d纹理…
|
||||
|
||||
•和1 2 d纹理。
|
||||
|
||||

|
||||
|
||||
第一个3 d纹理…
|
||||
|
||||
•4个通道
|
||||
|
||||
•这是128 ^ 3决议…
|
||||
|
||||
第一个频道是我刚才描述的perlin- worley噪声。
|
||||
|
||||
另外3个是在频率增加的Worley噪声。和标准方法一样,这个3d纹理用来定义云的基本形状。
|
||||
|
||||

|
||||
|
||||
我们的第二个3 d纹理…
|
||||
|
||||
•有3个频道…
|
||||
|
||||
•它是32 ^ 3决议…
|
||||
|
||||
•使用Worley噪音来增加频率。这个纹理被用来向第一个3d噪音定义的基本云形状添加细节。
|
||||
|
||||

|
||||
|
||||
•我们的2 d纹理…
|
||||
|
||||
•有3个频道…
|
||||
|
||||
•这是128 ^ 2分辨率…
|
||||
|
||||
•并使用curl噪音。它不是发散的,是用来假装流体运动的。我们使用这种噪音来扭曲我们的云形状并增加气流的感觉。
|
||||
|
||||

|
||||
|
||||
回想一下,标准解决方案要求高度梯度来改变高度的噪音信号。相反,我们使用…
|
||||
|
||||
•3数学预设,代表了主要的低空…
|
||||
|
||||
•当我们在样本位置混合时,云的类型。
|
||||
|
||||
•我们也有一个值,告诉我们希望在样本位置上有多少云覆盖。这是0和1之间的值。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
我们在屏幕右边看到的是一个大约30度的视图。我们将会在相机上方的一个区域中按标准方法绘制云。
|
||||
|
||||
首先,我们建立一个基本的云形状,通过采样我们的第一个3d纹理并将它乘以我们的高度信号。
|
||||
|
||||
•下一步是将结果乘上覆盖范围,并减少云层底部的密度。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
使用2D燥波增加基础云的细节
|
||||
|
||||

|
||||
|
||||
•通过在云边缘减去第二个3d纹理来侵蚀基本的云形状。小提示,如果你在云的基础上颠倒了Worley的噪音,你会得到一些很好的声音形状。
|
||||
|
||||
•我们也扭曲第二旋度由2 d纹理噪声假纠结的扭曲的大气湍流可以看到…
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
在这个图像中你可以看到一个小地图在左下角。这代表了在我们的世界地图上驱动云层的天气设置。您所看到的粉红色白色图案是来自我们的天气系统的输出。红色是覆盖层,绿色是降水,蓝色是云类型。天气系统通过模拟在游戏中进行的模拟来调节这些通道。这里的图像有积雨云直接在头顶(白色)和普通积云在远处。我们有控制偏见的方法来保持事物在一般意义上是直接的。
|
||||
|
||||
|
||||
|
||||
其实就是通过贴图来控制雨云的分布
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
我们还利用我们的天气系统来确保云是地平线,这是很有趣的。
|
||||
|
||||
•我们画的cloudscape withina球员…周围半径35000米。
|
||||
|
||||
•和从15000米的距离…
|
||||
|
||||
•我们开始向积云过渡到50%的覆盖率。
|
||||
|
||||
|
||||
|
||||
总结
|
||||
|
||||
所以总结我们的建模方法…
|
||||
|
||||
我们遵循标准的ray - march / sampler框架
|
||||
|
||||
但是我们用两个层次的细节来构建云
|
||||
|
||||
低频率的云基形状
|
||||
|
||||
高频细节和失真
|
||||
|
||||
我们的噪音是由Perlin、Worley和Curl杂音制成的
|
||||
|
||||
我们为每个云类型使用一组预先设置来控制在高度和云覆盖上的密度
|
||||
|
||||
这些都是由我们的天气模拟或定制纹理来驱动的,用于使用剪切场景
|
||||
|
||||
它在给定的风向中都是动态的。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
云的定向散射或发光质量…
|
||||
|
||||
•银衬里当你看向太阳通过云…
|
||||
|
||||
当你从太阳中移开时,云上的暗边可以看到。
|
||||
|
||||
前两个有标准的解决方案,但第三个是我们必须解决的问题。
|
||||
|
||||
|
||||
|
||||
在光线进入云再到结束,要么被吸收、要么色散
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
Beer's law说,我们可以根据所经过的介质的光学厚度来确定光的到达点。根据比尔斯定律,我们有一种基本的方法来描述云中的特定点的光量。
|
||||
|
||||
•如果我们用能量来替代云中的透光率,并把它画出来,你就可以看到能量指数的下降。这是我们照明模型的基础。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
在云层中,有更高的光散射的可能性。这被称为各向异性散射。
|
||||
|
||||
在1941年,henyey - greenstein模型被开发用来帮助天文学家在星系尺度上进行光计算,但今天它被用于在云照明中可靠地复制各向异性。
|
||||
|
||||

|
||||
|
||||
每次我们采样光能量时,我们用henyey - greenstein相函数乘以它。
|
||||
|
||||

|
||||
|
||||
在这里你可以看到结果。左边的只是我们照明模型的Beer‘s law部分。在右边,我们应用了henyey - greenstein相函数。请注意,在右边的太阳周围的云层更明亮。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
当我们在云上越走越深时,我们在散射上的潜力就会增加,更多的信息将会到达我们的眼睛。
|
||||
|
||||
•如果你把这两个函数得到描述这样的东西…
|
||||
|
||||
•效应以及传统方法。
|
||||
|
||||
我还在ACM数字图书馆寻找啤酒粉的近似方法,但我还没有发现任何与这个名字有关的东西。
|
||||
|
||||
|
||||
|
||||
将Power公式与Beer's law相结合,得到了Beer's power
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
但我们必须记住,这是一个依赖于视图的效果。我们只看到,我们的视图矢量接近光矢量,所以Power函数也应该解释这个梯度。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
So, in review our model has 3 components:
|
||||
|
||||
•Beer’s Law
|
||||
|
||||
•Henyen-Greenstein
|
||||
|
||||
•our powder sugar effect
|
||||
|
||||
•And Absorption increasing for rain clouds
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
低海拔1500~4000用体素云。超过4000就用2D ray march云
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
回想一下,采样器有一种低细节的噪声,它能形成基本的云形状
|
||||
|
||||
•高细节的噪音,增加我们所需要的现实细节。
|
||||
|
||||
高细节的噪声总是应用于从基云形状边缘的侵蚀。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
这意味着我们只需要做高细节的噪声和所有相关的指令,其中低细节的样本返回一个非零的结果。
|
||||
|
||||
•这就产生了在我们的云所处的区域周围产生一个等表面的效应。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
因为射线长度增加我们看向地平线,我们开始…
|
||||
|
||||
•一个初始potential64样本和结束…
|
||||
|
||||
•潜在的128位。我之所以说潜力,是因为这些优化可能导致3月份提前退出。我们真的希望他们能做到。
|
||||
|
||||
这就是我们如何利用样本来建立我们图像的alpha通道。为了计算光强度,我们需要取更多的样品。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
In our approach, we sample 6 times in a cone toward the sun. This smooth's the banding we would normally get with 6 simples and weights our lighting function with neighboring density values, which creates a nice ambient effect. The last sample is placed far away from the rest …
|
||||
|
||||
•in order to capture shadows cast by distant clouds.
|
||||
|
||||
|
||||
|
||||
自阴影算法…………
|
2053
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/c++17带来的代码变化.md
Normal file
2053
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/c++17带来的代码变化.md
Normal file
File diff suppressed because it is too large
Load Diff
494
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第一章.md
Normal file
494
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第一章.md
Normal file
@@ -0,0 +1,494 @@
|
||||
## 1.1.1 auto笔记
|
||||
### auto与引用作用则不会抛弃const
|
||||
```
|
||||
int x=0;
|
||||
const auto& g=x; //g->const int&
|
||||
auto& h =g; //h->const int&
|
||||
```
|
||||
### 什么时候用auto
|
||||
比如在使用迭代器遍历的时候,对类型及获取并不关心的时候。
|
||||
### auto推断模板函数对应函数返回值
|
||||
```
|
||||
struct Foo {
|
||||
static int get(void) { return 0; }
|
||||
};
|
||||
|
||||
struct Bar {
|
||||
static const char* get(void) { return "0"; };
|
||||
};
|
||||
|
||||
struct Bar2 {
|
||||
|
||||
};
|
||||
|
||||
template <class A>
|
||||
void func(void)
|
||||
{
|
||||
auto val = A::get();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
func<Foo>();
|
||||
func<Bar>();
|
||||
func<Bar2>();//Bar2类中不包含get方法,此时编译器会报错
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### c++14 增加了函数返回值推导
|
||||
在C++11下,如果你想要打印出一个数的平方,可能需要这样:
|
||||
```
|
||||
auto square_int = [](int x) { return x * x; };
|
||||
auto square_double = [](double x) { return x * x; };
|
||||
|
||||
std::cout<<square_int(10)<<std::endl;
|
||||
std::cout<<square_int(10.1)<<std::endl;
|
||||
```
|
||||
为了保持函数的局部性,我们才选择的lambda,但C++11的lambda却导致多个类型时代码膨胀且重复,此时我们需要回过头来借助全局的模板了。
|
||||
|
||||
但C++ 14可以完美的解决上面的问题,因为C++14中lambda的参数可以用auto代替具体的类型:
|
||||
```
|
||||
auto square = [](auto x) { return x * x; };
|
||||
|
||||
std::cout<<square_int(10)<<std::endl;
|
||||
std::cout<<square_int(10.1)<<std::endl;
|
||||
```
|
||||
|
||||
## 1.1.2 decltype
|
||||
### decltype推导规则
|
||||
decltype(exp)
|
||||
1. exp是标识符、类访问表达式,decltype(type)和exp类型一样。
|
||||
2. exp是函数调用,decltype(exp)和返回值的类型一致。
|
||||
3. 其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp类型一致。
|
||||
|
||||
```
|
||||
class Foo{
|
||||
static const int Number=0;
|
||||
int x;
|
||||
};
|
||||
|
||||
int n=0;
|
||||
volatile const int &x=n;
|
||||
|
||||
decltype(n) a=n; //a->int
|
||||
decltype(x) b=n; //b->const volatile int &
|
||||
decltype(Foo::Number) c=0; //c->const int
|
||||
|
||||
Foo foo;
|
||||
decltype(foo.x) d=0; //d->int
|
||||
```
|
||||
```
|
||||
int& func_int_r(void);
|
||||
int&& func_int_rr(void);
|
||||
int func_int(void);
|
||||
|
||||
const int& func_cint_r(void);
|
||||
const int&& func_cint_rr(void);
|
||||
const int func_cint(void);
|
||||
|
||||
const Foo func_cfoo(void);
|
||||
|
||||
int x=0;
|
||||
|
||||
decltype(func_int_r()) a1=x; //a1->int &
|
||||
decltype(func_int_rr()) b1=x; //b1->int &&
|
||||
decltype(func_int()) c1=x; //c1->int
|
||||
|
||||
decltype(func_cint_r()) a2=x; //a2->const int &
|
||||
decltype(func_cint_r()) b2=x; //b2->const int &&
|
||||
decltype(func_cint()) c2=x; //c2->int & 因为是纯右值,所以舍弃掉cv符,只有类类型可以携带
|
||||
|
||||
decltype(func_cfoo()) ff=foo(); //ff->const foo
|
||||
```
|
||||
```
|
||||
struct Foo{int x;};
|
||||
const Foo foo=Foo();
|
||||
|
||||
decltype(foo.x) a=0; //a->int
|
||||
decltype((foo.x)) b=a; //b->const int &
|
||||
|
||||
int n=0,m=0;
|
||||
decltype(n+m) c=; c=0; //c->int
|
||||
decltype(n+=m) d=c; d=c; //d->int &
|
||||
```
|
||||
a和b的结果:仅仅多加了一对括号,他们得到的类型却是不同的。具体解释请见P12
|
||||
++++
|
||||
### decltype实际应用
|
||||
具体解释请见P12
|
||||
|
||||
## 1.1.3 返回类型后置语法——auto和decltype结合使用
|
||||
|
||||
如何实现类似:
|
||||
```
|
||||
template <typename R,typename T,typename U>
|
||||
R add(T t,U u)
|
||||
{
|
||||
return t+u;
|
||||
}
|
||||
|
||||
int a=1;floatb=2.0;
|
||||
auto c=add<decltype(a+b)>(a+b);
|
||||
```
|
||||
|
||||
错误,定义返回值时参数变量还不存在
|
||||
```
|
||||
template <typename T,typename U>
|
||||
decltype(t+u) add(T t,U u)
|
||||
{
|
||||
return t+u;
|
||||
}
|
||||
```
|
||||
|
||||
正确,但是如果T和U是无参构造函数的类就不行了
|
||||
```
|
||||
template <typename T,typename U>
|
||||
decltype(T()+U()) add(T t,U u)
|
||||
{
|
||||
return t+u;
|
||||
}
|
||||
```
|
||||
|
||||
使用c++返回类型后置语法
|
||||
```
|
||||
template <typename T,typename U>
|
||||
auto add(T t,U u)->decltype(t+u)
|
||||
{
|
||||
return t+u;
|
||||
}
|
||||
```
|
||||
另一个例子,根据使用的重载函数推导返回类型
|
||||
```
|
||||
int& foo(int& i);
|
||||
float foo(float& f);
|
||||
|
||||
template<typename T>
|
||||
auto func(T& val)-> decltype(foo(val)
|
||||
{
|
||||
return foo(val);
|
||||
}
|
||||
```
|
||||
### 1.2.2 模板的别名
|
||||
using和typedef都可以定义一个新的类型别名,但是using可以直接定义一个模板别名,而不像typedef需要一个struct 外敷类来实现。而且using的可读性更高。
|
||||
```
|
||||
template <typename T>
|
||||
struct func_t{
|
||||
typedef void (*type)(t,t);
|
||||
};
|
||||
func_t<int>::type xx_1;
|
||||
|
||||
template<typename T>
|
||||
using func_t=void(*)(T,T);
|
||||
func_t<int> xx_2;
|
||||
```
|
||||
另外using可以定义任意类型的模板表达方式
|
||||
```
|
||||
template<typename T>
|
||||
using type_tt = T;
|
||||
type_tt<int> a;
|
||||
```
|
||||
### 1.3.2初始化列表的使用细节
|
||||
初始化列表可以用于对应类型函数的返回值中。
|
||||
例如:就等于Foo(123,321.0),(假设有Foo(int,float)这个构造函数)
|
||||
```
|
||||
Foo function(void)
|
||||
{
|
||||
return {123,321.0};
|
||||
}
|
||||
```
|
||||
### 1.3.3初始化列表
|
||||
自己定义的类并没有类似Vector、Map容器的任意长度的初始化功能,如果想要实现可以添加一下构造函数来实现。
|
||||
```
|
||||
class Foo
|
||||
{
|
||||
public:
|
||||
Foo(std::initializer_list<int> list){
|
||||
|
||||
}
|
||||
};
|
||||
```
|
||||
之后就可以执行Foo foo={1,2,3,4,5,6,7};(需要自己实现list对类内部变量的赋值)
|
||||
|
||||
同时std::initializer_list可以用于传值
|
||||
```
|
||||
void func(std::initializer_list<int> l){
|
||||
for(auto it=l.begin();it!=l.end();++it)
|
||||
{std::cout<<*it<<std::endl;}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
func({});
|
||||
func({1,2,3})
|
||||
}
|
||||
```
|
||||
PS.std::initializer_list为了提高初始化效率,使用的是引用传值,所以当传值结束时,这些里面的变量也都被销毁了。所以如果需要传值,这需要传递,它初始化容器的值。
|
||||
|
||||
初始化列表同时可以用于检查类型收窄,即因为类型转换而导致的数据丢失。
|
||||
### 1.4.1基于范围的for循环
|
||||
当遍历需要修改容器中的值时,则需要使用引用:
|
||||
```
|
||||
for(auto& n: arr)
|
||||
{
|
||||
std::cout<<n++<<std::endl;
|
||||
}
|
||||
```
|
||||
如果只是希望遍历而不希望修改可以使用const auto& n;
|
||||
### 1.4.2基于范围的for循环使用细节
|
||||
在:后面可以写带返回遍历对象的函数,而且这个函数只执行一次。
|
||||
```
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
std::vector<int> arr={1,2,3,4,5,6};
|
||||
|
||||
std::vector<int>& get_range()
|
||||
{
|
||||
std::cout<<"get_range->"<<std::endl;
|
||||
return arr;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
for(auto val: get_range())
|
||||
{
|
||||
std::cout<<val<<std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
在使用基于范围的for循环时,如果遍历时修改容器大小,很可能出现因为迭代器失效而导致的异常情况。
|
||||
|
||||
### 1.4.3 让基于范围的for循环支持自定义类型
|
||||
```
|
||||
#include <iostream>
|
||||
|
||||
namespace detail_range {
|
||||
template <typename T>
|
||||
class iterator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
|
||||
private:
|
||||
size_type cursor_;
|
||||
const value_type step_;
|
||||
value_type value_;
|
||||
public:
|
||||
iterator(size_type cur_start, value_type begin_val, value_type step_val)
|
||||
:cursor_(cur_start), step_(step_val), value_(begin_val)
|
||||
{
|
||||
value_ += (step_*cursor_);
|
||||
}
|
||||
|
||||
value_type operator*() const { return value_; }
|
||||
|
||||
bool operator!=(const iterator& rhs) const
|
||||
{
|
||||
return (cursor_!=rhs.cursor_);
|
||||
}
|
||||
|
||||
iterator& operator++(void)
|
||||
{
|
||||
value_ += step_;
|
||||
++cursor_;
|
||||
return (*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class impl {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = const value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = const detail_range::iterator<value_type>;
|
||||
using const_iterator= const detail_range::iterator<value_type>;
|
||||
using size_type = typename iterator::size_type;//这里的typename是告诉编译器后面跟着的是类型
|
||||
|
||||
private:
|
||||
const value_type begin_;
|
||||
const value_type end_;
|
||||
const value_type step_;
|
||||
const size_type max_count_;
|
||||
|
||||
size_type get_adjusted_count(void) const
|
||||
{
|
||||
if (step_ > 0 && begin_ >= end_)
|
||||
throw std::logic_error("End value must be greater than begin value.");
|
||||
else if (step_ < 0 && begin_ <= end_)
|
||||
throw std:: logic_error("End value must be less than begin value.");
|
||||
|
||||
size_type x = static_cast<size_type>((end_ - begin_) / step_);
|
||||
if (begin_ + (step_*x) != end_)
|
||||
++x;//为了防止出现类似{0,12,0.5}之类的迭代要求,而做的升位处理
|
||||
return x;
|
||||
}
|
||||
public:
|
||||
impl(value_type begin_val, value_type end_val, value_type step_val)
|
||||
:begin_(begin_val),
|
||||
end_(end_val),
|
||||
step_(step_val),
|
||||
max_count_(get_adjusted_count())
|
||||
{}
|
||||
size_type size(void) const
|
||||
{
|
||||
return max_count_;
|
||||
}
|
||||
|
||||
const_iterator begin(void) const
|
||||
{
|
||||
return{ 0,begin_,step_ };
|
||||
}
|
||||
|
||||
const_iterator end(void) const
|
||||
{
|
||||
return{ max_count_,begin_,step_ };
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
detail_range::impl<T> range(T end)
|
||||
{
|
||||
return{ {},end,1 };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
detail_range::impl<T> range(T begin, T end)
|
||||
{
|
||||
return{ begin,end,1 };
|
||||
}
|
||||
|
||||
template <typename T,typename U>
|
||||
auto range(T begin, T end, U step)->detail_range::impl<decltype(begin + step)>
|
||||
{
|
||||
using r_t = detail_range::impl<decltype(begin + step)>;
|
||||
return r_t(begin, end, step);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
for (int i : detail_range::range(15))
|
||||
{
|
||||
std::cout << " " << i;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
for (auto i : detail_range::range(2,8,0.5))
|
||||
{
|
||||
std::cout << " " << i;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### 1.5.2 可调用对象包装器———std::function
|
||||
用于解决以下可调用对象的统一操作的问题
|
||||
1. 函数指针
|
||||
2. 具有operator() 成员函数的类对象(仿函数)
|
||||
3. 可被转化为函数指针的类对象
|
||||
4. 类成员(函数)指针
|
||||
|
||||
std::function也可以作为函数参数来使用,也就是相当于回调函数的作用
|
||||
### 1.5.3 std::bind绑定器
|
||||
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以用std::function进行保存,并延迟调用到任何我们需要调用的时候。
|
||||
作用:
|
||||
1. 将可调用对象与其他参数一起绑定成一个仿函数
|
||||
2. 将多元可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。
|
||||
|
||||
关于bind的使用:
|
||||
bind(&要调用的函数,&对象, 要调用函数的参数1,要调用函数的参数2...,_1(bind函数的参数1),_2(bind函数的参数2)...)
|
||||
|
||||
注:如果bind的是一个非静态成员函数,第二个参数一定是一个该成员的一个指针,后面才是正常的参数。
|
||||
|
||||
数this指针作为一个隐含参数传递给非静态成员函数,用以指向该成员函数所属类所定义的对象。当不同对象调用同一个类的成员函数代码时,编译器会依据该成员函数的this指针所指向的不同对象来确定应该一用哪个对象的数据成员。下面举一个简单例子。
|
||||
|
||||
(1)bind(&f)() 假设f是一个全局函数,绑定全局函数并调用;
|
||||
|
||||
(2)bind (&A::f, A())() 假设A是一个构造函数为空的类,这个形式绑定了类的成员函数,故第二个参数需要传入一个成员(成员静态函数除外);
|
||||
### 1.6 lambda表达式
|
||||
```
|
||||
[capture](params)opt->ret{body;};
|
||||
auto f=[](int a)->int {return a+1;};
|
||||
```
|
||||
c++11允许省略lambada表达式的返回值定义:
|
||||
```
|
||||
auto x1=[](int i) {return i;};
|
||||
auto x2=[](){return {1,2};); //error:初始化列表无法推断
|
||||
```
|
||||
在没有参数列表时,()是可以省略的
|
||||
```
|
||||
auto f1=[]{return 1;};
|
||||
```
|
||||
```
|
||||
class A{
|
||||
public:
|
||||
int i_=0;
|
||||
void func(int x,int y)
|
||||
{
|
||||
auto x1=[]{return i_;}; //error,没有捕获外部变量
|
||||
auto x2=[=]{return i_+x+y;}; //ok
|
||||
auto x3=[&]{return i_+x+y;}; //ok
|
||||
auto x4=[this]{return i_;}; //ok
|
||||
auto x5=[this]{return i_+x+y;}; //error,没有捕获x与y
|
||||
auto x6=[this,x,y]{return i_+x+y;};//Ok
|
||||
auto x7=[this]{return i_++;}; //ok
|
||||
}
|
||||
};
|
||||
int a=0,b=1;
|
||||
auto f1=[]{return a;}; //error,没有捕获外部变量
|
||||
auto f2=[&]{return a++;}; //ok
|
||||
auto f3=[=]{return a;}; //ok
|
||||
auto f4=[=]{return a++;}; //error, a是以复制方式捕获的,无法修改
|
||||
auto f5=[a]{return a+b;}; //error,没有捕获b变量
|
||||
auto f6=[a,&b]{return a+(b++);};//ok
|
||||
auto f7=[=,&b]{return a+(b++);};//ok
|
||||
```
|
||||
需要注意的是,默认状态下lambda表达式无法修改通过复制方式捕获的外部变量,如果希望修改,需啊需要使用引用方式来捕获。
|
||||
```
|
||||
int a=0;
|
||||
auto f=[=]{return a;};
|
||||
a+=1;
|
||||
std::cout<<f()<<std::endl;
|
||||
```
|
||||
在以上lambda表示式中修改外部变量并不会真正影响到外部,我们却仍然无法修改他们。
|
||||
那么如果希望去修改按值捕获的外部变量就需要显式致命表达式为mutable:
|
||||
```
|
||||
int a=0;
|
||||
auto f1=[=]{return a++;}; //error
|
||||
auto f2=[=]()mutable{return a++;}; //ok,mutable
|
||||
```
|
||||
需要注意的是:被mutable修饰的lambda就算没有参数也需要写参数列表。
|
||||
|
||||
我们可以使用std::function与std::bind来操作lambda表达式,另外对于没有捕获任何变量的lambda表达式,还可以被转化为一个普通的函数指针:
|
||||
```
|
||||
using func_t=int(*)(int);
|
||||
func_t f=[](int a){return a;};
|
||||
f(123);
|
||||
```
|
||||
lambda表示式可以基本代替std::function(一些老式库不支持lambda)
|
||||
### 1.7 tupe元组
|
||||
```
|
||||
tuple<const char*,int>tp=make_tuple(sendPack,nSendSize);//构建一个tuple
|
||||
```
|
||||
使用tuple相当于构建一个对应的结构体<br>
|
||||
可以使用std::tie来进行解包来获取对应的数值,也可以用来创建元组。
|
||||
```
|
||||
int x=1;
|
||||
int y=2;
|
||||
string s="aa";
|
||||
auto tp=std::tie(x,s,y);
|
||||
//tp是实际类型是std::tuple<int&,string&,int&>
|
||||
const char* data=tp.get<0>();
|
||||
int len=tp.get<1>();
|
||||
|
||||
int x,y;
|
||||
string a;
|
||||
std::tie(x,a,y)=tp;
|
||||
```
|
||||
你可以使用std::ignore占位符来表示不解某个位置的值。
|
||||
```
|
||||
std::tie(std::ignore,std::ignore,y)=tp;//只获取了第三个值
|
||||
```
|
65
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第七章.md
Normal file
65
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第七章.md
Normal file
@@ -0,0 +1,65 @@
|
||||
### 7.1 委托构造函数和继承构造函数
|
||||
#### 7.1.1 委托构造函数
|
||||
委托构造函数允许在同一个类中一个构造函数可以调用另一个构造函数,从而可以在初始化时简化变量的初始化<br>
|
||||
```
|
||||
class class_c
|
||||
{
|
||||
public:
|
||||
int max;
|
||||
int min;
|
||||
int middle;
|
||||
|
||||
class_c(int my_max) { max = my_max > 0 ? my_max : 10; }
|
||||
|
||||
class_c(int my_max, int my_min) :class_c(my_max) {
|
||||
min = my_min > 0 && my_min < max ? my_min : 1;
|
||||
}
|
||||
|
||||
class_c(int my_max, int my_min, int my_middle) :class_c(my_max, my_min) {
|
||||
middle = my_middle<max&&my_middle>min ? my_middle : 5;
|
||||
}
|
||||
};
|
||||
```
|
||||
#### 7.1.2 继承构造函数
|
||||
让派生类直接使用基类的构造函数
|
||||
```
|
||||
struct A:B
|
||||
{
|
||||
using B::B;
|
||||
}
|
||||
```
|
||||
这个特性对普通函数也有效
|
||||
### 7.2 原始字面量
|
||||
```
|
||||
string str="D:\A\B\test.test";
|
||||
string str1="D:\\A\\B\\test.test";
|
||||
string str2=R"()";
|
||||
//输出
|
||||
D:AB est.test
|
||||
D:\A\B\test.test
|
||||
D:\A\B\test.test
|
||||
```
|
||||
可以通过R直接得到原始意义的字符串,只有()中的才有效果
|
||||
### 7.3 final和override
|
||||
final用来限制某个类不能被继承,或者某个虚函数不能被重写
|
||||
```
|
||||
struct A{
|
||||
virtual void foo() final;
|
||||
}
|
||||
|
||||
struct B final:A{
|
||||
|
||||
}
|
||||
```
|
||||
override确保在派生类中声明的重写函数与基类的虚函数有相同的签名(一样的形参,防止写错),同时也表明将要重写基类的虚函数,还可以防止因疏忽把本来想重写积累的虚函数声明承载
|
||||
### 7.4 内存对齐
|
||||
#### 7.4.1 内存对齐
|
||||
主要讲了一些概念,以及msvc与gcc的内存对齐机制区别
|
||||
http://blog.csdn.net/markl22222/article/details/38051483
|
||||
我们经常会看到内存对齐的应用,是在网络收发包中。一般用于发送的结构体,都是1字节对齐的,目的是统一收发双方(可能处于不同平台)之间的数据内存布局,以及减少不必要的流量消耗。
|
||||
#### 7.4.2 堆内存的内存对齐
|
||||
msvc下使用_aligned_malloc,使用默认的对齐大小,32位是8字节,64位是16字节,当然也可以自己实现,这里提供一个案例。
|
||||
#### 7.4.3 利用alignas指定内存对齐大小
|
||||
#### 7.4.4 利用alignof和std::alignment_of获取内存对齐大小
|
||||
#### 7.4.5 内存对齐的类型std::aligned_storage
|
||||
### 7.5 c++11新增便利算法
|
508
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第三章.md
Normal file
508
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第三章.md
Normal file
@@ -0,0 +1,508 @@
|
||||
## 使用c++11消除重复,提高代码质量
|
||||
### 3.1 type_traits———类型萃取
|
||||
type_traits提供了丰富的编译期计算、查询、判断、转换和选择的帮助类,在很多场合都会使用到。
|
||||
### 3.1.1 基本的type_traits
|
||||
##### 1.基本的type_traits<br>
|
||||
无需自己定义static const int或者enum类型,只需要从std::integral_constant派生:
|
||||
```
|
||||
template<typename Type>
|
||||
struct GetLeftSize : std::integral_constant<int,1>
|
||||
```
|
||||
之后用GetLeftSize::value来取值。
|
||||
##### 2.类型判断的type_traits
|
||||
这些从std::integral_constant派生的type_traits,可以通过std::is_xxxx来判断T的类型。也可以通过std::is_xxxx::value是否为true来判断模板类型是否为目标类型。
|
||||
```
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
int main(){
|
||||
std::cout<<"is_const:"<<std::endl;
|
||||
std::cout<<"int:"<<std::is_const<int>::value<<std::endl;
|
||||
std::cout<<"const int:"<<std::is_const<const int>::value<<std::endl;
|
||||
std::cout<<"const int&:"<<std::is_const<const int&>::value<<std::endl;
|
||||
std::cout<<"const int*:"<<std::is_const<const int*>::value<<std::endl;
|
||||
std::cout<<"int* const:"<<std::is_const<int* const>::value<<std::endl;
|
||||
}
|
||||
```
|
||||
##### 3.判断两类型之间的关系traits
|
||||
```
|
||||
struct is_same;//判断两类型是否相同
|
||||
struct is_base_of;//判断一个是否为另一个的基类
|
||||
struct is_convertible;//判断模板参数类型能否转化为后面的模板参数类型
|
||||
```
|
||||
##### 4.类型转换traits
|
||||
常用的类型的转换traits包括对const的修改————const的移除与添加,引用的修改————引用的移除与添加,数组的修改和指针的修改。<br>
|
||||
创建对象时需要去除引用:
|
||||
```
|
||||
template<typename T>
|
||||
typnename std::remove_reference<t>::type* Create()
|
||||
{
|
||||
typedef typename std::remove_reference<t>::type U;
|
||||
return new U();
|
||||
}
|
||||
```
|
||||
模板参数T可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用引用。同样const也需要考虑进去。<br>
|
||||
为了简洁可以使用std::decay,移除const与引用。<br>
|
||||
另一个用处就是用来保存函数指针。
|
||||
```
|
||||
{
|
||||
using FnType=typename std::decay<F>::type;
|
||||
SimpFunction(F& f):m_fn(f){
|
||||
|
||||
}
|
||||
void run()
|
||||
{
|
||||
m_fn();
|
||||
}
|
||||
FnType m_fn;
|
||||
|
||||
}
|
||||
```
|
||||
### 3.1.2根据条件选择traits
|
||||
```
|
||||
template<bool B,class T,class F>
|
||||
struct conditional;
|
||||
|
||||
typedef std::conditional<true,int,float>::type A;//int
|
||||
```
|
||||
### 3.1.3获取可调用对象返回类型的traits
|
||||
一些模板函数的返回值不能直接确定,虽然可以通过decltype来进行推断,但是可读性较差。<br>
|
||||
可以通过auto -> decltype进行和简化。
|
||||
```
|
||||
template<typename F,template Arg>
|
||||
auto Func(F f,Arg arg)->decltype(f(arg))
|
||||
{
|
||||
return f(arg);
|
||||
}
|
||||
```
|
||||
不过在类没有构造函数的情况下就不行了。此时如果希望编译器进行推导,就需要使用std::declval。
|
||||
```
|
||||
decltype(std::declval<int>(std::declval<int>())) i=4;
|
||||
```
|
||||
declval获得的临时值引用不能用于求值,因此需要使用decaltype来推断出最终值。<br>
|
||||
当然可以用更加简洁的方法:
|
||||
```
|
||||
std::result_of<A(int)>::type i=4;
|
||||
```
|
||||
### 3.1.4 根据条件禁用或者启用某种或者某些类型traits
|
||||
```
|
||||
template<class T>
|
||||
typnename std::enable_if<std::is_arithmetic<T>::value,T>::type foo(T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
auto r=foo(1);
|
||||
auto r1=foo(1.2);
|
||||
auto r2=foo("test");//error
|
||||
```
|
||||
不但可以作用于返回值,也可以作用于模板定义、类模板特化和入参类型的限定。
|
||||
|
||||
## 3.2 可变参数模板
|
||||
在c++11中允许模板定义中包含0到任意个数模板参数。声明可变参数模板需要在typename或class后面带上省略号
|
||||
省略号作用有两个:
|
||||
1. 声明一个参数包,这个参数包中可以包含0到任意个模板参数。
|
||||
2. 在模板定义的右边,可以将参数包展开成一个一个独立参数。
|
||||
### 3.2.1 可变参数模板函数
|
||||
```
|
||||
template<class... T>
|
||||
void f(T... args)
|
||||
{
|
||||
std::cout<<sizeof...(args)<<std::endl;
|
||||
}
|
||||
|
||||
f(); //0
|
||||
f(1,2); //2
|
||||
f(1,2.5,""); //3
|
||||
```
|
||||
1. 递归函数方式展开参数包
|
||||
```
|
||||
void print()
|
||||
{
|
||||
std::cout << "empty" << std::endl;
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
void print(T head, Args... rest)
|
||||
{
|
||||
std::cout << "parameter" << head << std::endl;
|
||||
print(rest...);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
print(1,2,3,4,5);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
当然递归终止函数也可以写成,或者使用更多参数
|
||||
```
|
||||
template<typename T,typename T1>
|
||||
void print(T t,T1, t1)
|
||||
{
|
||||
std::cout<<t<<""<<t1<<std::endl;
|
||||
}
|
||||
```
|
||||
2. 逗号表达式和初始化列表方式展开参数包
|
||||
|
||||
逗号运算符:<br>
|
||||
运算符有左右两个表达式,先计算左侧的,之后丢弃结果再计算右侧。
|
||||
```
|
||||
d=(a=b,c);
|
||||
```
|
||||
b会先赋值给a,接着括号中的逗号表达式将会返回c的值。
|
||||
```
|
||||
template <class T>
|
||||
void printarg(T t)
|
||||
{
|
||||
std::cout<<t<<std::endl;
|
||||
}
|
||||
|
||||
template <class ...Args>
|
||||
void expand(Args... args)
|
||||
{
|
||||
int arr[]={(printarg(args),0)...};
|
||||
}
|
||||
|
||||
expand(1,2,3,4);
|
||||
```
|
||||
这里使用了c++11的初始化列表特性,{(printarg(args),0)...}将会展开成{(printarg(args1),0),(printarg(args2),0),(printarg(args3),0),(printarg(args4),0),}<br>
|
||||
之后也可以使用std::initializer_list来优化,这样就无需定义一个数组了,使用lambda的话printarg函数就无需定义了。
|
||||
### 3.2.2可变参数模板
|
||||
std::tuple是一个可变参数模型
|
||||
```
|
||||
template<class... Type>
|
||||
class tuple;
|
||||
```
|
||||
1. 模板递归和特化方式展开参数包
|
||||
```
|
||||
//前向声明
|
||||
template<typename... Args>
|
||||
struct Sum;
|
||||
|
||||
//基本定义
|
||||
template<typename First,typne... Rest>
|
||||
struct Sum<First.Rest...>
|
||||
{
|
||||
enum{value=Sum<First>::value+Sum::<Rest...>::value};
|
||||
};
|
||||
|
||||
//递归终止
|
||||
template<typename Last>
|
||||
struct Sum<Last>
|
||||
{
|
||||
enum{value=sizeof(Last)};
|
||||
};
|
||||
```
|
||||
2. 继承方式展开参数包
|
||||
有点复杂不做笔记了<br>
|
||||
除了用继承还可以使用using来实现。
|
||||
```
|
||||
template<int N,int... Indexes>
|
||||
struct MakeIndexes{
|
||||
using type=MakeIndexes<N-1,N-1,Indexes...>::type
|
||||
};
|
||||
|
||||
template<int... Indexes>
|
||||
struct MakeIndexes<0,Indexes...>
|
||||
{
|
||||
using type=IndexSeq<Indexes...>;
|
||||
};
|
||||
|
||||
MakeIndexes<3>=>IndexSeq<0,1,2>
|
||||
```
|
||||
### 3.2.3可变参数模板
|
||||
免去手写大量的模板类代码,而让编译器来自动实现。
|
||||
```
|
||||
template<typename... Args>
|
||||
T* Instance(Args... args)
|
||||
{
|
||||
return new T(args);
|
||||
}
|
||||
|
||||
A* pa=Instance<A>(1);
|
||||
B* pb=Instance<B>(1,2);
|
||||
```
|
||||
上面代码中的Args是拷贝值,使用右值引用与完美转发来消除损耗。
|
||||
```
|
||||
template<typename... Args>
|
||||
T* Instance(Args&&... args)
|
||||
{
|
||||
return new T(std::forward<Args>(args)...);
|
||||
}
|
||||
```
|
||||
### 3.3 可变参数模板和type_taits的综合应有
|
||||
#### 3.3.1 optional的实现
|
||||
使用placement new的时候容易出现内存对齐的问题,可以使用:
|
||||
```
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
struct A
|
||||
{
|
||||
int avg;
|
||||
A(int a,int b):avg((a+b)/2){}
|
||||
};
|
||||
|
||||
typedef std::aligned_storage<sizeof(A),alignof(A)>::type Aligned_A;
|
||||
|
||||
int main()
|
||||
{
|
||||
Aligned_A a,b;
|
||||
new (&a) A(10,20);
|
||||
b=a;
|
||||
std::cout<<reinterpret_cast<A&>(b).avg<<std::endl;
|
||||
return0;
|
||||
}
|
||||
```
|
||||
#### 3.3.2 惰性求值类Lazy的实现
|
||||
```
|
||||
#ifndef _LAZY_HPP_
|
||||
#define _LAZY_HPP_
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
template<typename T>
|
||||
struct Lazy
|
||||
{
|
||||
Lazy(){}
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
Lazy(Func& f, Args && ... args)
|
||||
{
|
||||
m_func = [&f, &args...]{return f(args...); };
|
||||
}
|
||||
|
||||
T& Value()
|
||||
{
|
||||
if (!m_value.is_initialized())
|
||||
{
|
||||
m_value = m_func();
|
||||
}
|
||||
|
||||
return *m_value;
|
||||
}
|
||||
|
||||
bool IsValueCreated() const
|
||||
{
|
||||
return m_value.is_initialized();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<T()> m_func;
|
||||
boost::optional<T> m_value;
|
||||
};
|
||||
|
||||
template<class Func, typename... Args>
|
||||
Lazy<typename std::result_of<Func(Args...)>::type>
|
||||
lazy(Func && fun, Args && ... args)
|
||||
{
|
||||
return Lazy<typename std::result_of<Func(Args...)>::type>(
|
||||
std::forward<Func>(fun), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
```
|
||||
```
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
struct BigObject
|
||||
{
|
||||
BigObject()
|
||||
{
|
||||
std::cout << "lazy load big object" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
struct MyStruct
|
||||
{
|
||||
MyStruct()
|
||||
{
|
||||
m_obj = lazy([]{return std::make_shared<BigObject>(); });
|
||||
}
|
||||
|
||||
void Load()
|
||||
{
|
||||
m_obj.Value();
|
||||
}
|
||||
|
||||
Lazy<std::shared_ptr<BigObject>> m_obj;
|
||||
};
|
||||
|
||||
int Foo(int x)
|
||||
{
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
void TestLazy()
|
||||
{
|
||||
//带参数的普通函数
|
||||
int y = 4;
|
||||
auto lazyer1 = lazy(Foo, y);
|
||||
std::cout << lazyer1.Value() << std::endl;
|
||||
|
||||
//不带参数的lamda
|
||||
Lazy<int> lazyer2 = lazy([]{return 12; });
|
||||
std::cout << lazyer2.Value() << std::endl;
|
||||
|
||||
//带参数的fucntion
|
||||
std::function < int(int) > f = [](int x){return x + 3; };
|
||||
auto lazyer3 = lazy(f, 3);
|
||||
std::cout << lazyer3.Value() << std::endl;
|
||||
|
||||
//延迟加载大对象
|
||||
MyStruct t;
|
||||
t.Load();
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestLazy();
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 3.3.3 dll帮助类实现
|
||||
首先需要封装GetProcAddress函数,需要解决:
|
||||
1. 函数的返回值可能是不同类型,如果以一种通用的返回值来消除这种不同返回值导致的差异
|
||||
2. 函数的入参数目可能任意个数,且类型也不尽相同,如何来消除入参个数和类型的差异
|
||||
```
|
||||
#ifndef _DLLPARSER_HPP_
|
||||
#define _DLLPARSER_HPP_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
class DllParser
|
||||
{
|
||||
public:
|
||||
|
||||
DllParser()
|
||||
{
|
||||
}
|
||||
|
||||
~DllParser()
|
||||
{
|
||||
UnLoad();
|
||||
}
|
||||
|
||||
bool Load(const std::string& dllPath)
|
||||
{
|
||||
m_hMod = LoadLibrary(dllPath.data());
|
||||
if (nullptr == m_hMod)
|
||||
{
|
||||
std::cout << "LoadLibrary failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
typename std::result_of<std::function<T>(Args...)>::type
|
||||
ExcecuteFunc(const std::string& funcName, Args&&... args)
|
||||
{
|
||||
auto f = GetFunction<T>(funcName);
|
||||
|
||||
if (f == nullptr)
|
||||
{
|
||||
std::string s = "can not find this function " + funcName;
|
||||
throw std::exception(s.c_str());
|
||||
}
|
||||
|
||||
return f(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
bool UnLoad()
|
||||
{
|
||||
if (m_hMod == nullptr)
|
||||
return true;
|
||||
|
||||
auto b = FreeLibrary(m_hMod);
|
||||
if (!b)
|
||||
return false;
|
||||
|
||||
m_hMod = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* GetFunction(const std::string& funcName)
|
||||
{
|
||||
auto addr = GetProcAddress(m_hMod, funcName.c_str());
|
||||
return (T*) (addr);
|
||||
}
|
||||
|
||||
private:
|
||||
HMODULE m_hMod;
|
||||
std::map<std::string, FARPROC> m_map;
|
||||
};
|
||||
|
||||
#endif //_DLLPARSER_HPP_
|
||||
```
|
||||
### 3.3.4 lambda链式调用
|
||||
```
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T>
|
||||
class Task;
|
||||
|
||||
template<typename R, typename...Args>
|
||||
class Task<R(Args...)>
|
||||
{
|
||||
public:
|
||||
Task(std::function<R(Args...)>&& f) : m_fn(std::move(f)){}
|
||||
|
||||
Task(std::function<R(Args...)>& f) : m_fn(f){}
|
||||
|
||||
template<typename... Args>
|
||||
R Run(Args&&... args)
|
||||
{
|
||||
return m_fn(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto Then(F& f)->Task<typename std::result_of<F(R)>::type(Args...)>
|
||||
{
|
||||
using return_type = typename std::result_of<F(R)>::type;
|
||||
|
||||
auto func = std::move(m_fn);
|
||||
|
||||
return Task<return_type(Args...)>([func, f](Args&&... args)
|
||||
{return f(func(std::forward<Args>(args)...)); });
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<R(Args...)> m_fn;
|
||||
};
|
||||
|
||||
|
||||
void TestTask()
|
||||
{
|
||||
Task<int(int)> task = [](int i){ return i; };
|
||||
auto result = task.Then([](int i){return i + 1; }).Then([](int i){
|
||||
return i + 2; }).Then([](int i){return i + 3; }).Run(1);
|
||||
std::cout << result << std::endl; // 7
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestTask();
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
后面若干章节较难,为了节约时间所以跳过了
|
210
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第九章.md
Normal file
210
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第九章.md
Normal file
@@ -0,0 +1,210 @@
|
||||
### 使用c++11开发一个半同步半异步线程池
|
||||
```
|
||||
#pragma once
|
||||
#include<list>
|
||||
#include<mutex>
|
||||
#include<thread>
|
||||
#include<condition_variable>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
template<typename T>
|
||||
class SyncQueue
|
||||
{
|
||||
public:
|
||||
SyncQueue(int maxSize) :m_maxSize(maxSize), m_needStop(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Put(const T&x)
|
||||
{
|
||||
Add(x);
|
||||
}
|
||||
|
||||
void Put(T&&x)
|
||||
{
|
||||
Add(std::forward<T>(x));
|
||||
}
|
||||
|
||||
void Take(std::list<T>& list)
|
||||
{
|
||||
std::unique_lock<std::mutex> locker(m_mutex);
|
||||
m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
|
||||
|
||||
if (m_needStop)
|
||||
return;
|
||||
list = std::move(m_queue);
|
||||
m_notFull.notify_one();
|
||||
}
|
||||
|
||||
void Take(T& t)
|
||||
{
|
||||
std::unique_lock<std::mutex> locker(m_mutex);
|
||||
m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
|
||||
|
||||
if (m_needStop)
|
||||
return;
|
||||
t = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
m_notFull.notify_one();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_needStop = true;
|
||||
}
|
||||
m_notFull.notify_all();
|
||||
m_notEmpty.notify_all();
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
bool Full()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.size() == m_maxSize;
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
int Count()
|
||||
{
|
||||
return m_queue.size();
|
||||
}
|
||||
private:
|
||||
bool NotFull() const
|
||||
{
|
||||
bool full = m_queue.size() >= m_maxSize;
|
||||
if (full)
|
||||
cout << "full, waiting,thread id: " << this_thread::get_id() << endl;
|
||||
return !full;
|
||||
}
|
||||
|
||||
bool NotEmpty() const
|
||||
{
|
||||
bool empty = m_queue.empty();
|
||||
if (empty)
|
||||
cout << "empty,waiting,thread id: " << this_thread::get_id() << endl;
|
||||
return !empty;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Add(F&&x)
|
||||
{
|
||||
std::unique_lock< std::mutex> locker(m_mutex);
|
||||
m_notFull.wait(locker, [this] {return m_needStop || NotFull(); });
|
||||
if (m_needStop)
|
||||
return;
|
||||
|
||||
m_queue.push_back(std::forward<F>(x));
|
||||
m_notEmpty.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<T> m_queue; //缓冲区
|
||||
std::mutex m_mutex; //互斥量和条件变量结合起来使用
|
||||
std::condition_variable m_notEmpty;//不为空的条件变量
|
||||
std::condition_variable m_notFull; //没有满的条件变量
|
||||
int m_maxSize; //同步队列最大的size
|
||||
|
||||
bool m_needStop; //停止的标志
|
||||
};
|
||||
```
|
||||
```
|
||||
#pragma once
|
||||
#include<list>
|
||||
#include<thread>
|
||||
#include<functional>
|
||||
#include<memory>
|
||||
#include <atomic>
|
||||
#include "SyncQueue.hpp"
|
||||
|
||||
const int MaxTaskCount = 100;
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
using Task = std::function<void()>;
|
||||
ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount)
|
||||
{
|
||||
Start(numThreads);
|
||||
}
|
||||
|
||||
~ThreadPool(void)
|
||||
{
|
||||
//如果没有停止时则主动停止线程池
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
std::call_once(m_flag, [this]{StopThreadGroup(); }); //保证多线程情况下只调用一次StopThreadGroup
|
||||
}
|
||||
|
||||
void AddTask(Task&&task)
|
||||
{
|
||||
m_queue.Put(std::forward<Task>(task));
|
||||
}
|
||||
|
||||
void AddTask(const Task& task)
|
||||
{
|
||||
m_queue.Put(task);
|
||||
}
|
||||
|
||||
private:
|
||||
void Start(int numThreads)
|
||||
{
|
||||
m_running = true;
|
||||
//创建线程组
|
||||
for (int i = 0; i <numThreads; ++i)
|
||||
{
|
||||
m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
|
||||
}
|
||||
}
|
||||
|
||||
void RunInThread()
|
||||
{
|
||||
while (m_running)
|
||||
{
|
||||
//取任务分别执行
|
||||
std::list<Task> list;
|
||||
m_queue.Take(list);
|
||||
|
||||
for (auto& task : list)
|
||||
{
|
||||
if (!m_running)
|
||||
return;
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StopThreadGroup()
|
||||
{
|
||||
m_queue.Stop(); //让同步队列中的线程停止
|
||||
m_running = false; //置为false,让内部线程跳出循环并退出
|
||||
|
||||
for (auto thread : m_threadgroup) //等待线程结束
|
||||
{
|
||||
if (thread)
|
||||
thread->join();
|
||||
}
|
||||
m_threadgroup.clear();
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<std::thread>> m_threadgroup; //处理任务的线程组
|
||||
SyncQueue<Task> m_queue; //同步队列
|
||||
atomic_bool m_running; //是否停止的标志
|
||||
std::once_flag m_flag;
|
||||
};
|
||||
```
|
151
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第二章.md
Normal file
151
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第二章.md
Normal file
@@ -0,0 +1,151 @@
|
||||
## 使用c++改进出程序性能
|
||||
### 2.1右值引用
|
||||
左值:表达式结束后依然存在的持久对象</br>
|
||||
右值:表达式结束时就不存在的临时对象</br>
|
||||
一个区分左值与右值的便捷方法:看能不能对表达式取地址,如果能则为左值,否则为右值。所有的具名变量或者对象都未左值,而右值不具名。</br>
|
||||
右值有两个概念构成,一个是将亡值,另一个是纯右值。
|
||||
### 2.1.1 &&的特性
|
||||
右值引用就是对一个右值进行引用的类型。因为右值不具名,所以我们只能通过引用的方式找到它。
|
||||
```
|
||||
template<typnename T>
|
||||
void f(T&& param); //T的类型需要推断,所以是个universal references
|
||||
|
||||
template<typename T>
|
||||
class Test{
|
||||
Test(Test&& rhs); //已经定义类型,所以&&是个右值
|
||||
};
|
||||
|
||||
void f(Test&& rhs); //同上
|
||||
|
||||
template<typnename T>
|
||||
void f(const T&& params); //&&是个右值,因为universal references仅仅在T&&下发生。
|
||||
```
|
||||
T&&被左值或者右值初始化被称为引用折叠。<br>
|
||||
总结:
|
||||
1. 左值和右值是独立于他们的类型的,右值引用类型可能是左值也可以是右值。
|
||||
2. auto&&或是函数类型自动推导的T&&是一个未定的引用类型。
|
||||
3. 所有的右值引用叠加到右值引用上仍然是右值引用,其他都是左值引用。当T&&为函数模板时,输入左值,它会变成左值引用,而输入右值时则会变成具名的右值引用。
|
||||
4. 编译器会将已经命名的右值引用视为左值,而将未命名的右值引用视为右值。
|
||||
|
||||
### 2.1.2 右值引用优化性能,避免深拷贝
|
||||
对于含有堆内存的类,我们需要提供深拷贝的拷贝构造函数,如果使用默认构造函数,会导致堆内存的重复删除:
|
||||
```
|
||||
class A{
|
||||
public:
|
||||
A():m_ptr(new int(0))
|
||||
{
|
||||
|
||||
}
|
||||
~A()
|
||||
{
|
||||
delete m_ptr;
|
||||
}
|
||||
private:
|
||||
int* m_ptr;
|
||||
};
|
||||
|
||||
A Get(bool flag)
|
||||
{
|
||||
A a;
|
||||
A b;
|
||||
if(flag)
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
A a=Get(false); //运行出错
|
||||
}
|
||||
```
|
||||
在上面的代码中,默认构造函数是浅拷贝,a和b会指向同一个指针m_ptr,在析构时会导致重复删除该指针,正确的方法是提供深拷贝函数。<br>
|
||||
```
|
||||
A(const A& a):m_ptr(new int(*a.m_ptr))
|
||||
{
|
||||
|
||||
}
|
||||
```
|
||||
但是有时这种拷贝构造函数是没有必要的,使用移动构造函数会更好。<br>
|
||||
```
|
||||
A(A&& a):m_ptr(a.m_ptr)
|
||||
{
|
||||
a.m_ptr=nullptr;
|
||||
}
|
||||
```
|
||||
这里的A&&用来根据参数是左值还是右值来建立分支,如果是临时值,则会选择移动构造函数。<br>
|
||||
需要注意的是:一般在提供右值引用的构造函数同时,也会提供左值引用的拷贝构造函数,以保证移动不成还可以用拷贝;提供了移动也同时提供拷贝构造函数。<br>
|
||||
|
||||
有关拷贝构造函数相关的一些笔记:<br>
|
||||
拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。
|
||||
当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象
|
||||
A a;
|
||||
A b(a);
|
||||
A b=a; 都是拷贝构造函数来创建对象b
|
||||
强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!
|
||||
|
||||
浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)<br>
|
||||
深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。
|
||||
|
||||
拷贝构造函数重载声明如下:
|
||||
```
|
||||
A (const A&other)
|
||||
```
|
||||
下面为拷贝构造函数的实现:
|
||||
```
|
||||
class A
|
||||
{
|
||||
int m_i
|
||||
A(const A& other):m_i(other.m_i)
|
||||
{
|
||||
Cout<<”拷贝构造函数”<<endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
赋值函数<br>
|
||||
当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。
|
||||
当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作
|
||||
A a;
|
||||
A b;
|
||||
b=a; <br>
|
||||
强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!
|
||||
|
||||
赋值运算的重载声明如下:
|
||||
```
|
||||
A& operator = (const A& other)
|
||||
```
|
||||
1. 拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。
|
||||
```
|
||||
class A;
|
||||
A a;
|
||||
A b=a; //调用拷贝构造函数(b不存在)
|
||||
A c(a) ; //调用拷贝构造函数
|
||||
|
||||
/****/
|
||||
|
||||
class A;
|
||||
A a;
|
||||
A b;
|
||||
b = a ; //调用赋值函数(b存在)</span>
|
||||
```
|
||||
2. 一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
|
||||
3. 实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。
|
||||
### 2.2 move语义
|
||||
普通左值也可以借助移动语义进行优化<br>
|
||||
c++11提供std::move方法将左值转化为右值,从而方便使用移动语义。即强制将左值转化为右值引用。
|
||||
|
||||
```
|
||||
{
|
||||
std::list<std::string> tokens;
|
||||
std::list<std::string> t=tokens;
|
||||
}
|
||||
std::list<std::string> tokens;
|
||||
std::list<std::string> t=std::move(tokens);
|
||||
```
|
||||
事实上标准库容器都实现了move语义,而一些int之类的基础类型计算使用move,但仍然会发生拷贝。
|
||||
### 2.3 forward和完美转发
|
||||
右值引用类型独立于值,一个右值引用参数作为函数的形参,在函数内部再转发该参数时它已经变成一个左值了。<br>
|
||||
所以我们需要“完美转发”,是指在函数模板中,完全依照模板的参数的类型(即保持参数的左值、右值特征),讲参数传递给函数模板中调用另一个函数。c++11中提供了std::forward
|
||||
### 2.4emplace_back减少内存拷贝与移动
|
||||
emplace_back能通过参数构造对象,不需要拷贝或者移动内存,相比push_back能够更好得避免内存的拷贝与移动,使得容器性能得到进一步提升。大多数情况下应该优先使用emplace_back。<br>
|
||||
其原理是直接利用构造函数直接构造对象<br>
|
||||
当然在push_back还不能完全被代替,比如在没有提供构造函数的的struct。
|
647
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第五章.md
Normal file
647
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第五章.md
Normal file
@@ -0,0 +1,647 @@
|
||||
## 使用c++11让线程线程开发变得简单
|
||||
### 5.1 线程创建
|
||||
1. jon()
|
||||
```
|
||||
#include <thread>
|
||||
void func(){}
|
||||
int main()
|
||||
{
|
||||
std::thread t(func);
|
||||
t.join();//join将会阻塞当前线程,直到线程执行结束
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
2. detach
|
||||
```
|
||||
#include <thread>
|
||||
void func(){}
|
||||
int main()
|
||||
{
|
||||
std::thread t(func);
|
||||
t.detach();//线程与对象分离,让线程作为后台线程去运行,但同时也无法与线程产生联系(控制)了
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
线程不能被复制,但是可以被移动
|
||||
```
|
||||
#include <thread>
|
||||
void func(){}
|
||||
int main()
|
||||
{
|
||||
std::thread t(func);
|
||||
std::thread t2(std::move(t));
|
||||
t2.join();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
需要注意线程的生命周期
|
||||
```
|
||||
#include <thread>
|
||||
void func(){}
|
||||
int main()
|
||||
{
|
||||
std::thread t(func);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
以上代码会报错,因为线程对象先于线程结束了,可以通过join()或者detach()来解决,当然也可以放到一个容器中来保存,以保证线程生命周期
|
||||
```
|
||||
#include <thread>
|
||||
std::vector<std::thread> g_list;
|
||||
std::vector<std::shared_ptr<std::thread>> g_list2;
|
||||
void func(){}
|
||||
void CreateThread()
|
||||
{
|
||||
std::thread t(func);
|
||||
g_list.push_back(std::move(t));
|
||||
g_list2.push_back(std::make_shared<std::thread>(func));
|
||||
}
|
||||
int main()
|
||||
{
|
||||
void CreateThread();
|
||||
for(auto& thread : g_list)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
for(auto& thread : g_list2)
|
||||
{
|
||||
thread->join();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 线程的基本用法
|
||||
1. 获取当前信息
|
||||
```
|
||||
void func()
|
||||
{
|
||||
|
||||
}
|
||||
int main()
|
||||
{
|
||||
std::thread t(func);
|
||||
std::cout << t.get_id() << std::endl;//当前线程id
|
||||
//获得cpu核心数
|
||||
std::cout << std::thread::hardware_concurrency() << std::endl;
|
||||
}
|
||||
```
|
||||
2. 线程休眠
|
||||
```
|
||||
void f() {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
std::cout << "time out" << std::endl;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
std::thread t(f);
|
||||
t.join();
|
||||
}
|
||||
```
|
||||
### 互斥量
|
||||
#### 独占互斥锁std::mutex
|
||||
```
|
||||
std::mutex g_lock;
|
||||
void func() {
|
||||
g_lock.lock();
|
||||
std::cout << "entered thread" << std::this_thread::get_id() << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << "leaving thread" << std::this_thread::get_id() << std::endl;
|
||||
|
||||
g_lock.unlock();
|
||||
}
|
||||
int main()
|
||||
{
|
||||
std::thread t1(func);
|
||||
std::thread t2(func);
|
||||
std::thread t3(func);
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
}
|
||||
```
|
||||
使用lock_guard可以简化lock/unlock,同时也更加安全。因为是在构造时自动锁定互斥量,析构时自动解锁。
|
||||
```
|
||||
void func() {
|
||||
std::lock_guard<std::mutex> locker(g_lock);
|
||||
std::cout << "entered thread" << std::this_thread::get_id() << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << "leaving thread" << std::this_thread::get_id() << std::endl;
|
||||
}
|
||||
```
|
||||
#### 5.2.2 递归的独占互斥量std::recursive_mutex
|
||||
递归锁允许同一个线程多次获得该锁,可以用来解决同一线程需要多次获取互斥量时死锁的问题。
|
||||
```
|
||||
struct Complex
|
||||
{
|
||||
std::mutex mutex;
|
||||
int i;
|
||||
Complex() :i(0) {}
|
||||
void mul(int x) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
i *= x;
|
||||
}
|
||||
void div(int x) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
i *= x;
|
||||
}
|
||||
void both(int x,int y) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
mul(x);
|
||||
div(y);
|
||||
}
|
||||
};
|
||||
int main()
|
||||
{
|
||||
Complex complex;
|
||||
complex.both(32, 23);
|
||||
}
|
||||
```
|
||||
简单的方法就是使用std::recursive_mutex
|
||||
```
|
||||
struct Complex
|
||||
{
|
||||
std::recursive_mutex mutex;
|
||||
int i;
|
||||
Complex() :i(0) {}
|
||||
void mul(int x) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
i *= x;
|
||||
}
|
||||
void div(int x) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
i *= x;
|
||||
}
|
||||
void both(int x,int y) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
mul(x);
|
||||
div(y);
|
||||
}
|
||||
};
|
||||
int main()
|
||||
{
|
||||
Complex complex;
|
||||
complex.both(32, 23);
|
||||
}
|
||||
```
|
||||
需要注意的是:
|
||||
1. 需要用到递归锁定的多线程互斥处理往往本身就可以简化,允许递归互斥很容易放纵复杂逻辑的产生
|
||||
2. 递归锁比起非递归锁效率会低一些
|
||||
3. 递归锁虽然允许同一个线程多次获得同一个互斥锁,可重复获得的最大次数并未具体说明,一旦超过一定次数,再对lock进行调用就会抛出std::system错误
|
||||
#### 5.2.3带超时的互斥量std::timed_mutex和std::recursive_timed_mutex
|
||||
主要用于在获取锁时增加超时等待功能。不至于一直等待
|
||||
```
|
||||
std::timed_mutex mutex;
|
||||
|
||||
void work()
|
||||
{
|
||||
std::chrono::milliseconds timeout(100);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (mutex.try_lock_for(timeout))
|
||||
{
|
||||
std::cout << std::this_thread::get_id() << ": do work with the mutex" << std::endl;
|
||||
|
||||
std::chrono::milliseconds sleepDuration(250);
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
|
||||
mutex.unlock();
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << std::this_thread::get_id() << ": do work without the mutex" << std::endl;
|
||||
|
||||
std::chrono::milliseconds sleepDuration(100);
|
||||
std::this_thread::sleep_for(sleepDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::thread t1(work);
|
||||
std::thread t2(work);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 5.3 条件变量
|
||||
条件变量时c++11提供的另外一种用于等待的同步机制,它能柱塞一个或者多个线程,直到收到另一个线程发出的通知或者超时,才会唤醒当前阻塞的线程。<br>
|
||||
condition_variable
|
||||
condition_variable_any
|
||||
```
|
||||
#include<list>
|
||||
#include<mutex>
|
||||
#include<thread>
|
||||
#include<condition_variable>
|
||||
#include <iostream>
|
||||
|
||||
template<typename T>
|
||||
class SyncQueue
|
||||
{
|
||||
private:
|
||||
bool IsFull() const
|
||||
{
|
||||
return m_queue.size() == m_maxSize;
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
public:
|
||||
SyncQueue(int maxSize) : m_maxSize(maxSize)
|
||||
{
|
||||
}
|
||||
|
||||
void Put(const T& x)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
|
||||
while (IsFull())
|
||||
{
|
||||
std::cout << "缓冲区满了,需要等待..." << std::endl;
|
||||
m_notFull.wait(m_mutex);
|
||||
}
|
||||
|
||||
m_queue.push_back(x);
|
||||
m_notFull.notify_one();
|
||||
}
|
||||
|
||||
void Take(T& x)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
|
||||
while (IsEmpty())
|
||||
{
|
||||
std::cout << "缓冲区空了,需要等待..." << std::endl;
|
||||
m_notEmpty.wait(m_mutex);
|
||||
}
|
||||
|
||||
x = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
m_notFull.notify_one();
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
bool Full()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.size() == m_maxSize;
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
int Count()
|
||||
{
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<T> m_queue; //缓冲区
|
||||
std::mutex m_mutex; //互斥量和条件变量结合起来使用
|
||||
std::condition_variable_any m_notEmpty;//不为空的条件变量
|
||||
std::condition_variable_any m_notFull; //没有满的条件变量
|
||||
int m_maxSize; //同步队列最大的size
|
||||
};
|
||||
```
|
||||
更好的方法是使用unique_lock,unique可以随时释放锁
|
||||
```
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
|
||||
template<typename T>
|
||||
class SimpleSyncQueue
|
||||
{
|
||||
public:
|
||||
SimpleSyncQueue(){}
|
||||
|
||||
void Put(const T& x)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
m_queue.push_back(x);
|
||||
m_notEmpty.notify_one();
|
||||
}
|
||||
|
||||
void Take(T& x)
|
||||
{
|
||||
std::unique_lock<std::mutex> locker(m_mutex);
|
||||
m_notEmpty.wait(locker, [this]{return !m_queue.empty(); });
|
||||
x = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
}
|
||||
|
||||
bool Empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_mutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<T> m_queue;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_notEmpty;
|
||||
};
|
||||
```
|
||||
#### 5.4 原子变量
|
||||
使用原子变量就不需要来使用互斥量来保护该变量了<br>
|
||||
使用std::mutex
|
||||
```
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
struct Counter
|
||||
{
|
||||
int value = 0;
|
||||
std::mutex mutex;
|
||||
|
||||
void increment()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
++value;
|
||||
std::cout << value << std::endl;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
--value;
|
||||
std::cout << value << std::endl;
|
||||
}
|
||||
|
||||
int get()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
Counter g_counter;
|
||||
|
||||
void Increments()
|
||||
{
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
g_counter.increment();
|
||||
}
|
||||
}
|
||||
|
||||
void Decrements()
|
||||
{
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
g_counter.decrement();
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::thread t1(Increments);
|
||||
std::thread t2(Decrements);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
使用std::atomic<T>
|
||||
```
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
struct Counter
|
||||
{
|
||||
std::atomic<int> value = 0;
|
||||
|
||||
void increment()
|
||||
{
|
||||
++value;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
--value;
|
||||
}
|
||||
|
||||
int get()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
Counter g_counter;
|
||||
|
||||
void Increments()
|
||||
{
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
g_counter.increment();
|
||||
std::cout << g_counter.get() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Decrements()
|
||||
{
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
g_counter.decrement();
|
||||
std::cout << g_counter.get() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::thread t1(Increments);
|
||||
std::thread t2(Decrements);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 5.5 call_once/once_flag的使用
|
||||
为了保证多线程环境中某个函数仅被调用一次,比如需要初始化某个对象,而这个对象只能初始化一次。
|
||||
```
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
std::once_flag flag;
|
||||
|
||||
void do_once() {
|
||||
std::call_once(flag, [] {std::cout << "Called once" << std::endl; });
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::thread t1(do_once);
|
||||
std::thread t2(do_once);
|
||||
std::thread t3(do_once);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 5.6 异步操作类
|
||||
#### 5.6.1 std::future
|
||||
thread库提供了future用来访问异步操作结果,因为异步操作结果是一个未来的期待值,所以被称为future。future提供了异步获取操作结果的通道,我们可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作结果。
|
||||
```
|
||||
std::future_status status;
|
||||
do
|
||||
{
|
||||
status = future.wait_for(std::chromno::seconds(1));
|
||||
if (status == std::future_status::deferred) {
|
||||
|
||||
}
|
||||
else if (status == std::future_status::timeout) {
|
||||
|
||||
}
|
||||
else if (status==std::future_status::ready) {
|
||||
|
||||
}
|
||||
} while (status!= std::future_status::ready);
|
||||
```
|
||||
获取future有三种方式:
|
||||
1. get:等待异步操作结束并返回结果
|
||||
2. wait:等待异步操作完成
|
||||
3. wait_for:超时等待返回结果
|
||||
|
||||
#### 5.6.2 std::promise
|
||||
std::promise将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promise的future获取该值。
|
||||
```
|
||||
std::promise<int> pr;
|
||||
std::thread t([](std::promise<int>&p) {
|
||||
p.set_value_at_thread_exit(9);
|
||||
}, std::ref(pr));
|
||||
std::future<int> f = pr.get_future();
|
||||
auto r = f.get();
|
||||
```
|
||||
#### 5.6.3 std::package_task
|
||||
```
|
||||
std::packaged_task<int()> task([]() {return 7; });
|
||||
std::thread t1(std::ref(task));
|
||||
std::future<int> f1 = task.get_future();
|
||||
auto r1 = f1.get();
|
||||
```
|
||||
|
||||
#### 5.6.4 以上三者关系
|
||||
std::future提供了一个访问异步操作结果的机制,它和线程是一个级别的,属于低层次对象。之上是std::packaged_task和std::promise,他们内部都有future以便访问异步操作结果,std::packaged_task包装的是一个异步操作,std::promise包装的是一个值。那这两者又是什么关系呢?可以将一个异步操作的结果放到std::promise中。<br>
|
||||
future被promise和package_task用来作为异步操作或者异步的结果的连接通道,用std::future和std::shared_future来获取异步的调用结果。future是不可拷贝的,shared_future是可以拷贝的,当需要将future放到容器中则需要shared_future。
|
||||
```
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
int func(int x) { return x + 2; }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::packaged_task<int(int)> tsk(func);
|
||||
std::future<int> fut = tsk.get_future(); //获取future
|
||||
|
||||
std::thread(std::move(tsk), 2).detach();
|
||||
|
||||
int value = fut.get(); //等待task完成并获取返回值
|
||||
std::cout << "The result is " << value << ".\n";
|
||||
|
||||
std::vector<std::shared_future<int>> v;
|
||||
std::shared_future<int> f = std::async(std::launch::async, [](int a, int b){return a + b; }, 2, 3);
|
||||
|
||||
v.push_back(f);
|
||||
std::cout << "The shared_future result is " << v[0].get() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 5.7 线程异步操作函数
|
||||
std::async可以直接创建异步的task,异步任务结果也保存在future中,调用furturn.get()获取即可,如果不关心结果,可以使用furturn.wait()<br>
|
||||
两种线程创建策略:<br>
|
||||
1. std::launch::async:在调用async时就开始创建线程。
|
||||
2. std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用future的get或者wait时才创建。
|
||||
```
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
|
||||
void TestAsync()
|
||||
{
|
||||
std::future<int> f1 = std::async(std::launch::async, [](){
|
||||
return 8;
|
||||
});
|
||||
|
||||
std::cout << f1.get() << std::endl; //output: 8
|
||||
|
||||
std::future<void> f2 = std::async(std::launch::async, [](){
|
||||
std::cout << 8 << std::endl; return;
|
||||
});
|
||||
|
||||
f2.wait(); //output: 8
|
||||
|
||||
std::future<int> future = std::async(std::launch::async, [](){
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
return 8;
|
||||
});
|
||||
|
||||
std::cout << "waiting...\n";
|
||||
std::future_status status;
|
||||
|
||||
do {
|
||||
status = future.wait_for(std::chrono::seconds(1));
|
||||
|
||||
if (status == std::future_status::deferred)
|
||||
{
|
||||
std::cout << "deferred\n";
|
||||
}
|
||||
else if (status == std::future_status::timeout)
|
||||
{
|
||||
std::cout << "timeout\n";
|
||||
}
|
||||
else if (status == std::future_status::ready)
|
||||
{
|
||||
std::cout << "ready!\n";
|
||||
}
|
||||
} while (status != std::future_status::ready);
|
||||
|
||||
std::cout << "result is " << future.get() << '\n';
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestAsync();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
249
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第八章.md
Normal file
249
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第八章.md
Normal file
@@ -0,0 +1,249 @@
|
||||
### 使用c++11改进我们的模式
|
||||
### 8.1 改进单例模式
|
||||
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
|
||||
```
|
||||
#pragma once
|
||||
|
||||
template <typename T>
|
||||
class Singleton
|
||||
{
|
||||
public:
|
||||
template<typename... Args>
|
||||
static T* Instance(Args&&... args)
|
||||
{
|
||||
if(m_pInstance==nullptr)
|
||||
m_pInstance = new T(std::forward<Args>(args)...);
|
||||
|
||||
return m_pInstance;
|
||||
}
|
||||
|
||||
static T* GetInstance()
|
||||
{
|
||||
if (m_pInstance == nullptr)
|
||||
throw std::logic_error("the instance is not init, please initialize the instance first");
|
||||
|
||||
return m_pInstance;
|
||||
}
|
||||
|
||||
static void DestroyInstance()
|
||||
{
|
||||
delete m_pInstance;
|
||||
m_pInstance = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Singleton(void);
|
||||
virtual ~Singleton(void);
|
||||
Singleton(const Singleton&);
|
||||
Singleton& operator=(const Singleton&);
|
||||
private:
|
||||
static T* m_pInstance;
|
||||
};
|
||||
|
||||
template <class T> T* Singleton<T>::m_pInstance = nullptr;
|
||||
```
|
||||
使用了可变参数与完美转发,避免了写N个模板函数
|
||||
|
||||
单例模式应用的场景一般发现在以下条件下:
|
||||
1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
|
||||
2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。
|
||||
3. 只需一个存在就可以的情况
|
||||
### 8.2 改进观察者模式
|
||||
观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都能得到通知并被自动更新。
|
||||
|
||||
```
|
||||
#pragma once
|
||||
class NonCopyable
|
||||
{
|
||||
public:
|
||||
NonCopyable(const NonCopyable&) = delete; // deleted
|
||||
NonCopyable& operator = (const NonCopyable&) = delete; // deleted
|
||||
NonCopyable() = default; // available
|
||||
};
|
||||
```
|
||||
|
||||
```
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
template<typename Func>
|
||||
class Events : NonCopyable
|
||||
{
|
||||
public:
|
||||
//注册观察者,支持右值引用
|
||||
int Connect(Func&& f)
|
||||
{
|
||||
return Assgin(f);
|
||||
}
|
||||
|
||||
//注册观察者
|
||||
int Connect(const Func& f)
|
||||
{
|
||||
return Assgin(f);
|
||||
}
|
||||
//移除观察者
|
||||
void Disconnect(int key)
|
||||
{
|
||||
m_connections.erase(key);
|
||||
}
|
||||
|
||||
//通知所有的观察者
|
||||
template<typename... Args>
|
||||
void Notify(Args&&... args)
|
||||
{
|
||||
for (auto& it: m_connections)
|
||||
{
|
||||
it.second(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//保存观察者并分配观察者的编号
|
||||
template<typename F>
|
||||
int Assgin(F&& f)
|
||||
{
|
||||
int k=m_observerId++;
|
||||
m_connections.emplace(k, std::forward<F>(f));
|
||||
return k;
|
||||
}
|
||||
|
||||
int m_observerId=0;//观察者对应的编号
|
||||
std::map<int, Func> m_connections;//观察者列表
|
||||
};
|
||||
```
|
||||
### 8.3 改进访问者模式
|
||||
访问者模式需要注意的问题:定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重新定义对所有访问者的接口。<br>
|
||||
通过可变参数模板就可以实现一个稳定的接口层,可以让访问者的接口层访问任意个数的访问者,这样就不需要每增加一个新的被访问者就修改接口层,从而使接口层保证稳定。
|
||||
```
|
||||
template<typename... Types>
|
||||
struct Visitor;
|
||||
|
||||
template<typename T, typename... Types>
|
||||
struct Visitor<T, Types...> : Visitor<Types...>
|
||||
{
|
||||
using Visitor<Types...>::Visit;//通过using避免隐藏基类的visit同名方法
|
||||
virtual void Visit(const T&) = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Visitor<T>
|
||||
{
|
||||
virtual void Visit(const T&) = 0;
|
||||
};
|
||||
```
|
||||
### 8.4 改进命令模式
|
||||
命令模式的作用是将请求封装成一个对象,将请求的发起者和执行者解耦,支持对请求排队、撤销和重做。由于将请求都封装成一个一个命令对象了,使得我们可以集中处理或者延迟处理这些命令请求,而且不同客户对象可以共享这些命令,还可以控制请求的优先权、排队、支持请求的撤销和重做。<br>
|
||||
缺点:越来越多的命令会导致类爆炸,难以管理<br>
|
||||
接收function、函数对象、lambda和普通函数的包装器:
|
||||
```
|
||||
template<class F,class... Args,class=typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
|
||||
void Wrap(F&& f, Args && ...args)
|
||||
{
|
||||
return f(std::forward<Args>(args)...);
|
||||
}
|
||||
```
|
||||
接收成员函数的包装器:
|
||||
```
|
||||
template<class R,class C,class... DArgs,class P,class... Args>
|
||||
void Wrap(R(C::*f)(DArgs...),P&& p,Args&& ... args)
|
||||
{
|
||||
return; (*p.*f)(std::forward<Args>(args)...);
|
||||
}
|
||||
```
|
||||
```
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
template<typename Ret = void>
|
||||
struct CommCommand
|
||||
{
|
||||
private:
|
||||
std::function < Ret()> m_f;
|
||||
|
||||
public:
|
||||
//接受可调用对象的函数包装器
|
||||
template< class F, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
|
||||
void Wrap(F && f, Args && ... args)
|
||||
{
|
||||
m_f = [&]{return f(args...); };
|
||||
}
|
||||
|
||||
//接受常量成员函数的函数包装器
|
||||
template<class R, class C, class... DArgs, class P, class... Args>
|
||||
void Wrap(R(C::*f)(DArgs...) const, P && p, Args && ... args)
|
||||
{
|
||||
m_f = [&, f]{return (*p.*f)(args...); };
|
||||
}
|
||||
|
||||
//接受非常量成员函数的函数包装器
|
||||
template<class R, class C, class... DArgs, class P, class... Args>
|
||||
void Wrap(R(C::*f)(DArgs...), P && p, Args && ... args)
|
||||
{
|
||||
m_f = [&, f]{return (*p.*f)(args...); };
|
||||
}
|
||||
|
||||
Ret Excecute()
|
||||
{
|
||||
return m_f();
|
||||
}
|
||||
};
|
||||
```
|
||||
### 8.5 改进对象池
|
||||
```
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include "NonCopyable.hpp"
|
||||
using namespace std;
|
||||
|
||||
const int MaxObjectNum = 10;
|
||||
|
||||
template<typename T>
|
||||
class ObjectPool : NonCopyable
|
||||
{
|
||||
template<typename... Args>
|
||||
using Constructor = std::function<std::shared_ptr<T>(Args...)>;
|
||||
public:
|
||||
//默认创建多少个对象
|
||||
template<typename... Args>
|
||||
void Init(size_t num, Args&&... args)
|
||||
{
|
||||
if (num<= 0 || num> MaxObjectNum)
|
||||
throw std::logic_error("object num out of range.");
|
||||
|
||||
auto constructName = typeid(Constructor<Args...>).name(); //不区分引用
|
||||
for (size_t i = 0; i <num; i++)
|
||||
{
|
||||
m_object_map.emplace(constructName, shared_ptr<T>(new T(std::forward<Args>(args)...), [this, constructName](T* p) //删除器中不直接删除对象,而是回收到对象池中,以供下次使用
|
||||
{
|
||||
m_object_map.emplace(std::move(constructName), std::shared_ptr<T>(p));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
//从对象池中获取一个对象
|
||||
template<typename... Args>
|
||||
std::shared_ptr<T> Get()
|
||||
{
|
||||
string constructName = typeid(Constructor<Args...>).name();
|
||||
|
||||
auto range = m_object_map.equal_range(constructName);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
auto ptr = it->second;
|
||||
m_object_map.erase(it);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
multimap<string, std::shared_ptr<T>> m_object_map;
|
||||
};
|
||||
```
|
120
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第六章.md
Normal file
120
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第六章.md
Normal file
@@ -0,0 +1,120 @@
|
||||
### 使用c++11中便利的工具
|
||||
#### 6.1 处理日期和时间的chrono库
|
||||
#### 6.1.1 记录时长的duration
|
||||
```
|
||||
std::chrono::duration <rep,std::ratio<1,1>> seconds;//表示秒
|
||||
```
|
||||
chrono的count(),可以获取时钟周期数<br>
|
||||
需要注意的是:当两个duration时钟周期不同的时候,会先统一周期,再进行计算。<br>
|
||||
还可以通过duration_cast<>()进行时钟周期的转换
|
||||
#### 6.1.2 表示时间点的time point
|
||||
#### 6.1.3 获得系统时钟的clocks
|
||||
#### 6.1.4 计时器timer
|
||||
```
|
||||
#include<chrono>
|
||||
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer() : m_begin(std::chrono::high_resolution_clock::now()) {}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_begin = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
//默认输出毫秒
|
||||
int64_t elapsed() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
//微秒
|
||||
int64_t elapsed_micro() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
//纳秒
|
||||
int64_t elapsed_nano() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
//秒
|
||||
int64_t elapsed_seconds() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
//分
|
||||
int64_t elapsed_minutes() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::minutes>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
//时
|
||||
int64_t elapsed_hours() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::hours>(
|
||||
std::chrono::high_resolution_clock::now() - m_begin).count();
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_begin;
|
||||
};
|
||||
|
||||
void fun()
|
||||
{
|
||||
std::cout << "hello world" << std::endl;
|
||||
}
|
||||
|
||||
void Test()
|
||||
{
|
||||
std::cout << "\nTest()\n";
|
||||
|
||||
Timer t; //开始计时
|
||||
fun();
|
||||
|
||||
std::cout << t.elapsed_seconds() << std::endl; //打印fun函数耗时多少秒
|
||||
std::cout << t.elapsed_nano() << std::endl; //打印纳秒
|
||||
std::cout << t.elapsed_micro() << std::endl; //打印微秒
|
||||
std::cout << t.elapsed() << std::endl; //打印毫秒
|
||||
std::cout << t.elapsed_seconds() << std::endl; //打印秒
|
||||
std::cout << t.elapsed_minutes() << std::endl; //打印分钟
|
||||
std::cout << t.elapsed_hours() << std::endl; //打印小时
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Test();
|
||||
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
#### 6.2 数值类型和字符类型的互相转换
|
||||
std::string to_string(type value)
|
||||
|
||||
std::string to_wstring(type value)
|
||||
|
||||
1. atoi:字符串转int
|
||||
2. atol:字符串转long
|
||||
3. atoll:字符串转long long
|
||||
4. atof:字符串转float
|
||||
|
||||
#### 6.3 宽窄字符转换
|
||||
```
|
||||
std::wstring str=L"中国人";
|
||||
//宽窄字符转换器
|
||||
std::codecvt_utf8
|
||||
std::codecvt_utf16
|
||||
std::codecvt_utf8_utf16
|
||||
std::wstring_convert
|
||||
```
|
121
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十一章.md
Normal file
121
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十一章.md
Normal file
@@ -0,0 +1,121 @@
|
||||
### 使用c++11开发一个轻量级的Ioc容器
|
||||
Ioc容器具备两种能力,一种是对象工厂,不仅可以创建所有的对象,还可以根据配置去创建对象;另一种能力是可以去创建依赖对象,应有不需要直接创建对象,由Ioc容器去创建,实现控制反转。<br>
|
||||
实现Ioc容器需要解决三个问题,第一个问题是创建所有类型的对象,第二个问题是类型擦除,第三个问题是如何创建依赖对象。
|
||||
|
||||
#### 类型擦除的常用方法
|
||||
类型擦除就是讲原有类型消除或者隐藏。常用的方法有:
|
||||
1. 通过多态来擦除类型
|
||||
2. 通过模板来擦除类型
|
||||
3. 通过某种类型的模板容器擦除类型(Variant)
|
||||
4. 通过某种通用类型来擦除类型(any)
|
||||
5. 通过闭包来擦除类型(通过模板函数,将数值包入函数中,再包入std::function)
|
||||
```
|
||||
#pragma once
|
||||
#include<string>
|
||||
#include<unordered_map>
|
||||
#include<memory>
|
||||
#include<functional>
|
||||
using namespace std;
|
||||
#include<Any.hpp>
|
||||
#include <NonCopyable.hpp>
|
||||
|
||||
class IocContainer : NonCopyable
|
||||
{
|
||||
public:
|
||||
IocContainer(void){}
|
||||
~IocContainer(void){}
|
||||
|
||||
template<class T, typename Depend, typename... Args>
|
||||
void RegisterType(const string& strKey)
|
||||
{
|
||||
std::function<T* (Args...)> function = [](Args... args){ return new T(new Depend(args...)); };//通过闭包擦除了参数类型
|
||||
RegisterType(strKey, function);
|
||||
}
|
||||
|
||||
template<class T, typename... Args>
|
||||
T* Resolve(const string& strKey, Args... args)
|
||||
{
|
||||
if (m_creatorMap.find(strKey) == m_creatorMap.end())
|
||||
returnnullptr;
|
||||
|
||||
Any resolver = m_creatorMap[strKey];
|
||||
std::function<T* (Args...)> function = resolver.AnyCast<std::function<T* (Args...)>>();
|
||||
|
||||
return function(args...);
|
||||
}
|
||||
|
||||
template<class T, typename... Args>
|
||||
std::shared_ptr<T> ResolveShared(const string& strKey, Args... args)
|
||||
{
|
||||
T* t = Resolve<T>(strKey, args...);
|
||||
|
||||
return std::shared_ptr<T>(t);
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterType(const string& strKey, Any constructor)
|
||||
{
|
||||
if (m_creatorMap.find(strKey) != m_creatorMap.end())
|
||||
throw std::invalid_argument("this key has already exist!");
|
||||
|
||||
//通过Any擦除了不同类型的构造器
|
||||
m_creatorMap.emplace(strKey, constructor);
|
||||
}
|
||||
|
||||
private:
|
||||
unordered_map<string, Any> m_creatorMap;
|
||||
};
|
||||
/*test code
|
||||
struct Base
|
||||
{
|
||||
virtual void Func(){}
|
||||
virtual ~Base(){}
|
||||
};
|
||||
struct DerivedB : public Base
|
||||
{
|
||||
DerivedB(int a, double b):m_a(a),m_b(b)
|
||||
{
|
||||
}
|
||||
void Func()override
|
||||
{
|
||||
cout<<m_a+m_b<<endl;
|
||||
}
|
||||
private:
|
||||
int m_a;
|
||||
double m_b;
|
||||
};
|
||||
struct DerivedC : public Base
|
||||
{
|
||||
};
|
||||
struct A
|
||||
{
|
||||
A(Base * ptr) :m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
void Func()
|
||||
{
|
||||
m_ptr->Func();
|
||||
}
|
||||
|
||||
~A()
|
||||
{
|
||||
if(m_ptr!=nullptr)
|
||||
{
|
||||
delete m_ptr;
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
private:
|
||||
Base * m_ptr;
|
||||
};
|
||||
void TestIoc()
|
||||
{
|
||||
IocContainer ioc;
|
||||
ioc.RegisterType<A, DerivedC>(“C”); //配置依赖关系
|
||||
auto c = ioc.ResolveShared<A>(“C”);
|
||||
ioc.RegisterType<A, DerivedB, int, double>(“C”); //注册时要注意DerivedB的参数int和double
|
||||
auto b = ioc.ResolveShared<A>(“C”, 1, 2.0); //还要传入参数
|
||||
b->Func();
|
||||
}
|
||||
*/
|
||||
```
|
235
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十三章.md
Normal file
235
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十三章.md
Normal file
@@ -0,0 +1,235 @@
|
||||
### 使用c++11封装sqlite库
|
||||
#### 13.1 sqlite基本用法
|
||||
对于带参数的sql语句:<br>
|
||||
其中sqlite3_perpare_v2用于解析sql文本并且保存到sqlite_stmt对象中,sqlite3_stmt将作为后面一些函数的入参;sqlite3_bind_XXX用于绑定sql文本中的参数
|
||||
```
|
||||
#include <sqlite3.h>
|
||||
#include <string>
|
||||
bool test()
|
||||
{
|
||||
sqlite3* dbHandle = nullptr;
|
||||
int result = sqlite3_open("test.db", &dbHandle);
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
sqlite3_close(dbHandle);
|
||||
return false;
|
||||
}
|
||||
const char* sqlcreat = "CREATE TABLE if not exists PersonTable(ID INTEGER NOT NULL, Name Text,Address BLOB);";
|
||||
result = sqlite3_exec(dbHandle, sqlcreat, nullptr, nullptr, nullptr);
|
||||
//插入数据
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
const char* sqlinsert = "INSERT INTO PersonTable(ID,Name,Adress) VALUE(?,?,?);";
|
||||
//解析并且保存sql脚本
|
||||
sqlite3_prepare_v2(dbHandle, sqlinsert, strlen(sqlinsert), &stmt, nullptr);
|
||||
|
||||
int id = 2;
|
||||
const char* name = "peter";
|
||||
for (int i=0;i<10;++i)
|
||||
{
|
||||
sqlite3_bind_int(stmt, 1, id);
|
||||
sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_null(stmt, 3);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE)
|
||||
{
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(dbHandle);
|
||||
}
|
||||
//重新初始化stmt对象,下次再用
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
//使用完需要释放,不然会内存泄露
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
sqlite3_close(dbHandle);
|
||||
return result = SQLITE_OK;
|
||||
}
|
||||
```
|
||||
最后通过
|
||||
sqlite3_colume_xxx(sqlite3_stmt*,int iCol)取得结果
|
||||
```
|
||||
int colCount=sqlite3_column_count(stmt);
|
||||
while(true)
|
||||
{
|
||||
int r=sqlite3_step(stmt);
|
||||
if(r==SQLITE_DONE)
|
||||
{
|
||||
break;//数据行都已经获取,跳出循环
|
||||
}
|
||||
if(r==SQLITE_ROW)
|
||||
{
|
||||
break;//获得某一行数据失败,跳出循环
|
||||
}
|
||||
|
||||
//获得每一列数据
|
||||
for(int i=0;i<colCount;++i)
|
||||
{
|
||||
int coltype=sqlite3_column_type(stmt,i);
|
||||
if(coltype==SQLITE_INTEGER)
|
||||
{
|
||||
int val=sqlite3_column_int(stmt,i);
|
||||
}else if(coltype==SQLITE_FLOAT)
|
||||
{
|
||||
double val=sqlite3_column_double(stmt,i);
|
||||
}else if(coltype==SQLITE_TEXT)
|
||||
{
|
||||
const char* val=(const char*)sqlite3_column_text(stmt,i);
|
||||
}else if(coltype==SQLITE_NULL)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
```
|
||||
事务
|
||||
```
|
||||
sqlite3_exec(dbHandle,"BEGIN");
|
||||
|
||||
sqlite3_exec(dbHandle,"ROLLBACK");
|
||||
|
||||
sqlite3_exec(dbHandle,"COMMIT");
|
||||
|
||||
sqlite3_exec(dbHandle,"END");
|
||||
```
|
||||
```
|
||||
/**
|
||||
* 不带占位符。执行sql,不带返回结果, 如insert,update,delete等
|
||||
* @param[in] query: sql语句, 不带占位符
|
||||
* @return bool, 成功返回true,否则返回false
|
||||
*/
|
||||
bool Excecute(const string& sqlStr)
|
||||
{
|
||||
m_code = sqlite3_exec(m_dbHandle, sqlStr.data(), nullptr, nullptr, nullptr);
|
||||
return SQLITE_OK == m_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 带占位符。执行sql,不带返回结果, 如insert,update,delete等
|
||||
* @param[in] query: sql语句, 可能带占位符"?"
|
||||
* @param[in] args: 参数列表,用来填充占位符
|
||||
* @return bool, 成功返回true,否则返回false
|
||||
*/
|
||||
template <typename... Args>
|
||||
bool Excecute(const string& sqlStr, Args && ... args)
|
||||
{
|
||||
if (!Prepare(sqlStr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ExcecuteArgs(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作之前准备sql接口,必须和ExcecuteBulk一起调用,准备批量操作的sql,可能带占位符
|
||||
* @param[in] query: sql语句, 带占位符"?"
|
||||
* @return bool, 成功返回true,否则返回false
|
||||
*/
|
||||
bool Prepare(const string& sqlStr)
|
||||
{
|
||||
m_code = sqlite3_prepare_v2(m_dbHandle, sqlStr.data(), -1, &m_statement, nullptr);
|
||||
if (m_code != SQLITE_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作接口,必须先调用Prepare接口
|
||||
* @param[in] args: 参数列表
|
||||
* @return bool, 成功返回true,否则返回false
|
||||
*/
|
||||
template <typename... Args>
|
||||
bool ExcecuteArgs(Args && ... args)
|
||||
{
|
||||
if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_code = sqlite3_step(m_statement);
|
||||
|
||||
sqlite3_reset(m_statement);
|
||||
return m_code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
template<typename Tuple>
|
||||
bool ExcecuteTuple(const string& sqlStr, Tuple&& t)
|
||||
{
|
||||
if (!Prepare(sqlStr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_code = detail::ExcecuteTuple(m_statement, detail::MakeIndexes<std::tuple_size<Tuple>::value>::type(), std::forward<Tuple>(t));
|
||||
return m_code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
bool ExcecuteJson(const string& sqlStr, const char* json)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
doc.Parse<0>(json);
|
||||
if (doc.HasParseError())
|
||||
{
|
||||
cout << doc.GetParseError() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Prepare(sqlStr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return JsonTransaction(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行sql,返回函数执行的一个值, 执行简单的汇聚函数,如select count(*), select max(*)等
|
||||
* 返回结果可能有多种类型,返回Value类型,在外面通过get函数去取
|
||||
* @param[in] query: sql语句, 可能带占位符"?"
|
||||
* @param[in] args: 参数列表,用来填充占位符
|
||||
* @return int: 返回结果值,失败则返回-1
|
||||
*/
|
||||
template < typename R = sqlite_int64, typename... Args>
|
||||
R ExecuteScalar(const string& sqlStr, Args&&... args)
|
||||
{
|
||||
if (!Prepare(sqlStr))
|
||||
return GetErrorVal<R>();
|
||||
|
||||
if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
|
||||
{
|
||||
return GetErrorVal<R>();
|
||||
}
|
||||
|
||||
m_code = sqlite3_step(m_statement);
|
||||
|
||||
if (m_code != SQLITE_ROW)
|
||||
return GetErrorVal<R>();
|
||||
|
||||
SqliteValue val = GetValue(m_statement, 0);
|
||||
R result = val.Get<R>();// get<R>(val);
|
||||
sqlite3_reset(m_statement);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<rapidjson::Document> Query(const string& query, Args&&... args)
|
||||
{
|
||||
if (!PrepareStatement(query, std::forward<Args>(args)...))
|
||||
nullptr;
|
||||
|
||||
auto doc = std::make_shared<rapidjson::Document>();
|
||||
|
||||
m_buf.Clear();
|
||||
m_jsonHelper.BuildJsonObject(m_statement);
|
||||
|
||||
doc->Parse<0>(m_buf.GetString());
|
||||
|
||||
return doc;
|
||||
}
|
||||
```
|
||||
详细代码见
|
||||
https://github.com/qicosmos/SmartDB1.03/blob/master/SmartDB.hpp
|
@@ -0,0 +1,2 @@
|
||||
### 使用c++11开发一个对象的消息总线库
|
||||
大致了解消息总线的设计思路,但是没有一定的模板基础,代码看了也白看
|
132
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十五章.md
Normal file
132
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十五章.md
Normal file
@@ -0,0 +1,132 @@
|
||||
### 使用c++11开发一个轻量级的并行task库
|
||||
#### 15.1 TBB的基本用法
|
||||
#### 15.1.1 TBB概述
|
||||
TBB 是inter用标准c++写的一个开源的并行计算库,它的目的是提升数据并行计算的能力。主要功能如下:
|
||||
1. 并行计算
|
||||
2. 任务调度
|
||||
3. 并行容器
|
||||
4. 同步原语
|
||||
5. 内存分配器
|
||||
#### 15.1.2 TBB并行算法
|
||||
1. parallel_for:以并行的方式遍历一个区间
|
||||
2. parallel_do和parallel_for_each:将算法用于一个区间
|
||||
3. parallel_reduce:并行汇聚
|
||||
4. parallel_pipeline:并行的管道过滤器
|
||||
5. parallel_sort和parallel_invoke:并行排序和调和
|
||||
#### 15.1.3 TBB的任务组
|
||||
```
|
||||
tbb::task_group g;
|
||||
g.run([]{task();});
|
||||
g.run([]{task();});
|
||||
g.run([]{task();});
|
||||
g.wait();
|
||||
```
|
||||
#### 15.2 PPL的基本用法
|
||||
两者差异:
|
||||
1. parallel_reduce的原型有些不同。
|
||||
2. PPL中没有parallel_pipeline接口
|
||||
3. TBB的task没有PPL的task强大,PPL的task可以链式连续执行,还可以组合任务,而TBB的task不行。
|
||||
#### 15.5 TaskCpp的任务
|
||||
#### 15.5.1 task的实现
|
||||
基于task的并行编程模型最基本的执行单元是task,一个task就代表了一个要执行的任务。外部只需要简单调用接口就可以创建task并且执行,另一个细节就是异步执行。
|
||||
```
|
||||
template<typename T>
|
||||
class Task;
|
||||
|
||||
template<typename R, typename...Args>
|
||||
class Task<R(Args...)>
|
||||
{
|
||||
std::function<R(Args...)> m_fn;
|
||||
|
||||
public:
|
||||
typedef R return_type;
|
||||
|
||||
template<typename F>
|
||||
auto Then(F&& f)//->Task<typename std::result_of<F(R)>::type(Args...)>
|
||||
{
|
||||
typedef typename std::result_of<F(R)>::type ReturnType;
|
||||
auto func = std::move(m_fn);
|
||||
return Task<ReturnType(Args...)>([func, &f](Args&&... args)
|
||||
{
|
||||
std::future<R> lastf = std::async(func, std::forward<Args>(args)...);
|
||||
return std::async(f, lastf.get()).get();
|
||||
});
|
||||
}
|
||||
|
||||
Task(std::function<R(Args...)>&& f) :m_fn(std::move(f)){}
|
||||
Task(std::function<R(Args...)>& f) :m_fn(f){}
|
||||
|
||||
~Task()
|
||||
{
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
std::async(m_fn).wait();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
R Get(Args&&... args)
|
||||
{
|
||||
return std::async(m_fn, std::forward<Args>(args)...).get();
|
||||
}
|
||||
|
||||
std::shared_future<R> Run()
|
||||
{
|
||||
return std::async(m_fn);
|
||||
}
|
||||
};
|
||||
```
|
||||
#### 15.5.2 task的延续
|
||||
```
|
||||
#include <functional>
|
||||
namespace Cosmos
|
||||
{
|
||||
template<typename T>
|
||||
class Task;
|
||||
|
||||
template<typename R, typename...Args>
|
||||
class Task<R(Args...)>
|
||||
{
|
||||
std::function<R(Args...)> m_fn;
|
||||
|
||||
public:
|
||||
typedef R return_type;
|
||||
|
||||
template<typename F>
|
||||
auto Then(F&& f)//->Task<typename std::result_of<F(R)>::type(Args...)>
|
||||
{
|
||||
typedef typename std::result_of<F(R)>::type ReturnType;
|
||||
auto func = std::move(m_fn);
|
||||
return Task<ReturnType(Args...)>([func, &f](Args&&... args)
|
||||
{
|
||||
std::future<R> lastf = std::async(func, std::forward<Args>(args)...);
|
||||
return std::async(f, lastf.get()).get();
|
||||
});
|
||||
}
|
||||
|
||||
Task(std::function<R(Args...)>&& f) :m_fn(std::move(f)){}
|
||||
Task(std::function<R(Args...)>& f) :m_fn(f){}
|
||||
|
||||
~Task()
|
||||
{
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
std::async(m_fn).wait();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
R Get(Args&&... args)
|
||||
{
|
||||
return std::async(m_fn, std::forward<Args>(args)...).get();
|
||||
}
|
||||
|
||||
std::shared_future<R> Run()
|
||||
{
|
||||
return std::async(m_fn);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
@@ -0,0 +1 @@
|
||||
所有类型的容器和数组都抽象为一个Range,这个Range由一族迭代器组成,然后就可以基于这个抽象的Range实现更为抽象、规范、统一的算法了。Boost库已经实现了Range。
|
109
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十章.md
Normal file
109
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第十章.md
Normal file
@@ -0,0 +1,109 @@
|
||||
### 使用c++11开发一个轻量级的AOP库
|
||||
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务处理
|
||||
|
||||
```
|
||||
#pragma once
|
||||
#define HAS_MEMBER(member)\
|
||||
template<typename T, typename... Args>struct has_member_##member\
|
||||
{\
|
||||
private:\
|
||||
template<typename U> static auto Check(int) -> decltype(std::declval<U>().member(std::declval<Args>()...), std::true_type()); \
|
||||
template<typename U> static std::false_type Check(...);\
|
||||
public:\
|
||||
enum{value = std::is_same<decltype(Check<T>(0)), std::true_type>::value};\
|
||||
};\
|
||||
|
||||
HAS_MEMBER(Foo)
|
||||
HAS_MEMBER(Before)
|
||||
HAS_MEMBER(After)
|
||||
|
||||
#include <NonCopyable.hpp>
|
||||
template<typename Func, typename... Args>
|
||||
struct Aspect : NonCopyable
|
||||
{
|
||||
Aspect(Func&& f) : m_func(std::forward<Func>(f))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<has_member_Before<T, Args...>::value&&has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
|
||||
{
|
||||
aspect.Before(std::forward<Args>(args)...);//核心逻辑之前的切面逻辑
|
||||
m_func(std::forward<Args>(args)...);//核心逻辑
|
||||
aspect.After(std::forward<Args>(args)...);//核心逻辑之后的切面逻辑
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<has_member_Before<T, Args...>::value&&!has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
|
||||
{
|
||||
aspect.Before(std::forward<Args>(args)...);//核心逻辑之前的切面逻辑
|
||||
m_func(std::forward<Args>(args)...);//核心逻辑
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<!has_member_Before<T, Args...>::value&&has_member_After<T, Args...>::value>::type Invoke(Args&&... args, T&& aspect)
|
||||
{
|
||||
m_func(std::forward<Args>(args)...);//核心逻辑
|
||||
aspect.After(std::forward<Args>(args)...);//核心逻辑之后的切面逻辑
|
||||
}
|
||||
|
||||
template<typename Head, typename... Tail>
|
||||
void Invoke(Args&&... args, Head&&headAspect, Tail&&... tailAspect)
|
||||
{
|
||||
headAspect.Before(std::forward<Args>(args)...);
|
||||
Invoke(std::forward<Args>(args)..., std::forward<Tail>(tailAspect)...);
|
||||
headAspect.After(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Func m_func; //被织入的函数
|
||||
};
|
||||
template<typename T> using identity_t = T;
|
||||
|
||||
//AOP的辅助函数,简化调用
|
||||
template<typename... AP, typename... Args, typename Func>
|
||||
void Invoke(Func&&f, Args&&... args)
|
||||
{
|
||||
Aspect<Func, Args...> asp(std::forward<Func>(f));
|
||||
asp.Invoke(std::forward<Args>(args)..., identity_t<AP>()...);
|
||||
}
|
||||
|
||||
/*TEST CODE
|
||||
struct TimeElapsedAspect
|
||||
{
|
||||
void Before(int i)
|
||||
{
|
||||
m_lastTime = m_t.elapsed();
|
||||
}
|
||||
void After(int i)
|
||||
{
|
||||
cout <<"time elapsed: "<< m_t.elapsed() - m_lastTime << endl;
|
||||
}
|
||||
private:
|
||||
double m_lastTime;
|
||||
Timer m_t;
|
||||
};
|
||||
struct LoggingAspect
|
||||
{
|
||||
void Before(int i)
|
||||
{
|
||||
std::cout <<"entering"<< std::endl;
|
||||
}
|
||||
void After(int i)
|
||||
{
|
||||
std::cout <<"leaving"<< std::endl;
|
||||
}
|
||||
};
|
||||
void foo(int a)
|
||||
{
|
||||
cout <<"real HT function: "<<a<< endl;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
Invoke<LoggingAspect, TimeElapsedAspect>(&foo, 1); //织入方法
|
||||
cout <<"-----------------------"<< endl;
|
||||
Invoke<TimeElapsedAspect, LoggingAspect>(&foo, 1);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
```
|
140
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第四章.md
Normal file
140
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第四章.md
Normal file
@@ -0,0 +1,140 @@
|
||||
## 使用c++11解决内存泄露的问题
|
||||
### 4.1 shared_ptr共享的智能指针
|
||||
1. 初始化<br>
|
||||
优先通过make_shared来构造智能指针
|
||||
```
|
||||
std::shared_ptr<int> p(new int(1));
|
||||
std::shared_ptr<int> p2=p;
|
||||
std::shared_ptr<int> ptr;
|
||||
ptr.reset(new int(1));
|
||||
```
|
||||
2. 获取原始指针<br>
|
||||
```
|
||||
std::shared_ptr<int> ptr(new int(1));
|
||||
int *p=ptr.get();
|
||||
```
|
||||
3. 指定删除器
|
||||
```
|
||||
void DeleteIntPtr(int *p){
|
||||
delete p;
|
||||
}
|
||||
std::shared_ptr<int> p(new int,DeleteIntPtr);
|
||||
//当然也可以用lambda
|
||||
std::shared_ptr<int> p(new int,[](int* p){delete p;});
|
||||
```
|
||||
#### 4.2.1 使用shared_ptr需要注意的问题
|
||||
1. 不要用一个原始指针初始化多个shared_ptr
|
||||
```
|
||||
int* ptr=new int;
|
||||
std::shared_ptr<int> p1(ptr);
|
||||
std::shared_ptr<int> p2(ptr);
|
||||
```
|
||||
2. 不要在函数实参中创建shared_ptr
|
||||
```
|
||||
function(shared_ptr<int>(new int),g()) //有缺陷,不同编译器的执行顺序不一样
|
||||
shared_ptr<int> p(new int());
|
||||
f(p,g());
|
||||
```
|
||||
3. 通过shared_from_this()返回的this指针,不要将指针作为shared_ptr返回出来,因为this本子上是个裸指针。
|
||||
4. 避免循环引用
|
||||
```
|
||||
struct A;
|
||||
struct B;
|
||||
|
||||
struct A{
|
||||
std::shared_ptr<B> bptr;
|
||||
};
|
||||
|
||||
struct B{
|
||||
std::shared_ptr<A> bptr;
|
||||
};
|
||||
|
||||
void TestPtr(){
|
||||
{
|
||||
std::shared_ptr<A> ap(new A);
|
||||
std::shared_ptr<B> bp(new B);
|
||||
ap->bptr=bp;
|
||||
bp->aptr=ap;
|
||||
//计数不会归零,无法正确销毁
|
||||
}
|
||||
}
|
||||
```
|
||||
### 4.2 unique_ptr独占的智能指针
|
||||
它不允许其他的智能指针共享内部的指针,不允许通过复制将unique_ptr赋值给另一个unique_ptr<br>
|
||||
但是可以通过std::move将拥有权转移
|
||||
```
|
||||
std::shared_ptr<T> myPtr(new T);
|
||||
std::shared_ptr<T> myOtherPtr=std::move(myPtr);
|
||||
```
|
||||
### 4.3 weak_ptr弱引用的智能指针
|
||||
弱引用指针weak_ptr是用来监视shared_ptr的生命周期的,不会使引用计数加1.同时他可以解决返回this指针和解决循环引用的问题。
|
||||
#### 4.3.1 weak_ptr基本用法
|
||||
1. 通过use_count()方法来获得当前观测资源的引用计数
|
||||
```
|
||||
std::shared_ptr<int> sp(new int(10));
|
||||
std::weak_ptr<int> wp(sp);
|
||||
|
||||
std::cout<<wp.use_count()<<std::endl;
|
||||
```
|
||||
2. 通过expired()来判断资源是否释放
|
||||
```
|
||||
std::shared_ptr<int> sp(new int(10));
|
||||
std::weak_ptr<int> wp(sp);
|
||||
|
||||
if(wp.expired())
|
||||
{
|
||||
std::cout<<"weak_ptr无效,资源释放”<<std::endl;
|
||||
}else{
|
||||
std::cout<<"weak_ptr有效"<<std::endl;
|
||||
}
|
||||
```
|
||||
3. 通过lock方法获取所监视的shared_ptr
|
||||
#### 4.3.2 weak_ptr返回this指针
|
||||
通过派生std::enable_shared_from_this类,并通过shader_from_this来返回智能指针
|
||||
#### 4.3.3 weak_ptr解决循环引用问题
|
||||
```
|
||||
struct A;
|
||||
struct B;
|
||||
|
||||
struct A{
|
||||
std::shared_ptr<B> bptr;
|
||||
};
|
||||
|
||||
struct B{
|
||||
std::weak_ptr<A> bptr;
|
||||
//改成weak_ptr就能正常销毁了
|
||||
};
|
||||
|
||||
void TestPtr(){
|
||||
{
|
||||
std::shared_ptr<A> ap(new A);
|
||||
std::shared_ptr<B> bp(new B);
|
||||
ap->bptr=bp;
|
||||
bp->aptr=ap;
|
||||
}
|
||||
}
|
||||
```
|
||||
### 4.4通过智能指针管理第三方库分配的内存
|
||||
```
|
||||
void* p=GetHandle()->Create();
|
||||
std::shared_ptr<void> sp(p,[this](void*p){GetHandle()->Release(p);});
|
||||
```
|
||||
包装公共函数
|
||||
```
|
||||
std::shared_ptr<void> Guard(void* p)
|
||||
{
|
||||
return std::shared_ptr<void> sp(p,[this](void *p){GetHandle()->Release(p);});
|
||||
}
|
||||
```
|
||||
但是不够安全
|
||||
```
|
||||
void *p=GetHandle()->Create();
|
||||
Guard(p);
|
||||
//Guard(p);是个右值,如果没有被赋予给auto指针,这句结束就会释放从而导致p提前释放
|
||||
```
|
||||
可以使用宏来解决
|
||||
```
|
||||
#define GUARD(p) std::shared_ptr<void> p##p(p,
|
||||
void *p=GetHandle()->Create();
|
||||
Guard(p);
|
||||
```
|
28
02-Note/读书笔记/深入理解c++11/第三章.md
Normal file
28
02-Note/读书笔记/深入理解c++11/第三章.md
Normal file
@@ -0,0 +1,28 @@
|
||||
#### 继承构造函数
|
||||
```
|
||||
struct A{
|
||||
void f(double i){}
|
||||
|
||||
A(){}
|
||||
|
||||
A(int i){}
|
||||
|
||||
A(float f){}
|
||||
};
|
||||
|
||||
struct B:A{
|
||||
using A:A;
|
||||
|
||||
using A:f;
|
||||
|
||||
void f(double i){}
|
||||
}
|
||||
|
||||
int main(){
|
||||
B b;//会调用A的对应构造函数
|
||||
b.f;//会调用A的函数
|
||||
}
|
||||
```
|
||||
不过使用了继承构造函数,就无法通过构造函数初始化派生类成员了。
|
||||
#### 用户自定义字面量
|
||||
可以定义出2.0f或者123_w之类的写法,表达瓦特与浮点
|
96
02-Note/读书笔记/深入理解c++11/第二章.md
Normal file
96
02-Note/读书笔记/深入理解c++11/第二章.md
Normal file
@@ -0,0 +1,96 @@
|
||||
#### 2.1.2 __func__
|
||||
__func__ 宏:返回函数名称 c++11兼容c99标准
|
||||
#### 2.1.4 变长参数宏以及__VA_ARGS__
|
||||
```
|
||||
#define LOG(...){\
|
||||
fprintf(stderr,"%s: Line %d:\t",__FINE__,__LINE__);\
|
||||
fprintf(stderr,__VA_ARGS__);\
|
||||
fprintf(stderr,"\n");\
|
||||
}
|
||||
|
||||
int main(){
|
||||
int x=3;
|
||||
LOG("x=%d",x);//2-1-5.cpp: line 12: x=3
|
||||
}
|
||||
```
|
||||
#### 2.4 宏__cplusplus
|
||||
可以用于判断是否是c++编译环境,同时也可以判断c++版本,防止c++11程序在c++98编译器下编译
|
||||
#### 2.5.1
|
||||
assert宏定义在cassert头文件中
|
||||
```
|
||||
assert(expr);
|
||||
```
|
||||
首先对expr求值,如果表达式为假,assert输出信息,并且终止程序运行。为真则什么也不做。
|
||||
|
||||
在开头使用(需要写在cassert头文件之前,因为本质上还是宏判断)
|
||||
```
|
||||
#define NDEBUG
|
||||
```
|
||||
可以跳过检测。在VC++里面,release会在全局定义NDEBUG。<br>
|
||||
既然定义了NDEBUG,还可以:
|
||||
```
|
||||
#ifndef
|
||||
//do any debug thing
|
||||
//__FILE__ 文件名字面值
|
||||
//__LINE__ 当前行号字面值
|
||||
//__TIME__ 文件编译时间字面值
|
||||
//__DATE__ 文件编译时间字面值
|
||||
//__func__ 函数名称 c++11兼容c99标准
|
||||
//用于打印debug信息
|
||||
#endif
|
||||
```
|
||||
## error用法
|
||||
```
|
||||
#ifndef UNIX
|
||||
#error This software requires the UNIX OS.
|
||||
#endif
|
||||
|
||||
#if !defined(__cplusplus)
|
||||
#error C++ compiler required.
|
||||
#endif
|
||||
```
|
||||
#### 2.5.2 静态断言与static_assert
|
||||
编译期断言
|
||||
static_assert(false,"error string");
|
||||
#### 2.6 noexcept修饰符和noexcept操作符
|
||||
```
|
||||
void excpt_func() noexcept;//虽然这样函数就不会抛出异常,但是如果抛出了异常,程序会直接被终止
|
||||
|
||||
void excpt_func() noexcept(expr);//如果为true则不会抛出异常
|
||||
```
|
||||
#### 2.9 扩展的friend语法
|
||||
```
|
||||
class A{
|
||||
|
||||
};
|
||||
class B{
|
||||
friend class A;//都通过
|
||||
};
|
||||
class C{
|
||||
friend B;//c++11通过,c++98失败
|
||||
};
|
||||
```
|
||||
可以省略class直接声明类的友元,这个改动的意义在于类模板,不过使用内置对象时就会实例化成一个没有友元的类
|
||||
```
|
||||
class A;
|
||||
template <class T>
|
||||
class People{
|
||||
friend T;
|
||||
};
|
||||
```
|
||||
#### 模板函数的默认模板参数
|
||||
```
|
||||
template<typename T=int>
|
||||
void func(){
|
||||
|
||||
}
|
||||
```
|
||||
#### 2.12.1 外部模板
|
||||
在头文件中定义模板类的情况下,为了防止编译器给每个包含头文件的cpp文件生成代码,可以添加extern关键字。当然至少要有一个是不加extern的。
|
||||
```
|
||||
template <typename T> void func(T){
|
||||
|
||||
}
|
||||
|
||||
extern template void func<int>(int);
|
||||
```
|
11
02-Note/读书笔记/深入理解c++11/第五章.md
Normal file
11
02-Note/读书笔记/深入理解c++11/第五章.md
Normal file
@@ -0,0 +1,11 @@
|
||||
#### 5.1.3 强类型枚举
|
||||
```
|
||||
enum class Type {a,b,c,d};
|
||||
```
|
||||
优势:
|
||||
1. 强作用域
|
||||
2. 转换限制(禁止隐式转换)
|
||||
3. 可以指定底层类型
|
||||
```
|
||||
enum class Type : char {a,b,c,d};//指定了类型
|
||||
```
|
Reference in New Issue
Block a user