配置文件
redis的配置文件位于Redis的安装目录下,文件名为redis.conf,windows下为redis.windows.conf,这个文件是一个文本文件。
配置命名
在redis命令行下,你可以使用CONFIG命令获取或者设置相关配置
例如:
1 | CONFIG GET loglevel |
1 | CONFIG GET * # 用于获取所有配置 |
1 | CONFIG SET loglevel "notice" |
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一系列构建镜像所需的指令和说明。
Dockerfile字段介绍:
FROM: 定制的镜像所基于的基础的镜像,后续的操作都将基于这个基础镜像。
RUN: 后面接命令行命令,它有两种形式:
Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。如果命令太多,可以使用&&符号连接命令,例如
1 | RUN yum install wget \ |
COPY: 复制指令,从上下文目录中复制文件或者目录到容器里指定路径:
1 |
|
WORKDIR: 指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
其他更多命令见这里
1、切换Registry
1 | docker pull <image-name> |
1 | docker search <image-name> |
1 | docker images |
列出来表格有以下几个字端
同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。如果不指定TAG,默认会使用为TAG被标记为latest的那个。
1 | docker rmi [-f] <image-id> |
1 | docker build -t <image name>:<tag name> . |
-t 参数是标签,如果不给tag name,会自动将tag name设置为latest,如果该镜像之前已经有被标记为latest tag 的镜像,那个这个之前的镜像的tag会被标记为none
。所以最好为每个镜像给一个tag。
“.”表示上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。可以类比cmake命令的”cmake .”
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
docker ps -a
docker stop <container id>
docker restart <container id>
docker rm <container id>
docker rm $(docker ps -aq)
docker run -it <image_name>:<image_tag> /bin/bash
docker run -d <image_name>:<image_tag> [--name <custom_container_name>] [-p <outter_port>:<inner_port>]
docker exec -it <container id> /bin/bash
docker export <container id> > <filename>
docker export 1e560fca3906 > ubuntu.tar
docker import <filePath | url> <imageName:tag>
inline function在编译后实质上是函数代码块的替换,编译器不一定会实现inline function,如果编译器认为inline function太复杂或者有递归调用。inline function的链接性为内部链接,所以inline function的定义和声明不能放在不同的文件中。
如果函数的参数为引用类型,如果传递的参数类型正确但不是左值或者传递的参数类型不正确,在其可以转换为正确的类型的情况下,编译将会创建一个临时变量,而将引用参数引用到该临时变量上。
1 | double refcube(const double &ra){ |
它右边的所有参数提供默认值
。1 | int sum(int n, ...); |
...
代表可以接受任意类型的数据任意多个。
获取到可变参数列表的方法
1 | int sum(int n, ...) { |
在使用va_arg宏时,要求type是POD(Plain Old Data)类型
,能用 C 的 memcpy() 等函数进行操作的类、结构体就是 POD 类型的数据,所有基本数据类型都是POD。并且如果type和对应的参数不对的话会产生强制类型转换。如果类型转换失败就会将数据设置为0或者其他初始化的值。
函数的参数列表也称为函数特征标
函数重载要求函数的参数数目或参数类型或参数的顺序
不同,而参数名是无关紧要,且编译器在检查时不一定
会将引用类型和本身类型视为同一特征标,但是如果调用重载函数存在二义性则编译器会报错。
对于非引用传参,形参是否const是等价的
。但是当使用引用传参时,有无const是不同的
。使用指针传参时,指向const对象的指针和指向非const对象的指针做形参的函数是不同的
只是函数返回值不同不能构成函数重载
在类中,C++将区分常量和非常量函数(即函数本身是否为const)的特征标
函数重载匹配原则
模版函数匹配原则:越具体越适配原则
1 | template<typename T> |
decltype(x) y,用于说明y的类型和x的类型一样,decltype的运用场景大多是模版
1 | template<typename T1, typename T2> |
函数指针的声明方式:
1 | return_type (*pointer_name)(parameter); |
*
和pointer_name要用括号括起来,否则就变成了返回指针的函数。
不能声明一个指向模版函数的指针,因为模版函数并没有实现真的函数,而只是说明如何声明函数
1 | #define MAX(a, b) (a) > (b) ? (a) : (b) |
有些时候用宏定义的代码被do { } while(0);包含了起来,例如:
1 | #define MAX(a, b) do { \ |
这是因为宏的处理是文本替换,有时候将宏用文本替换后代码就产生了语法错误,而使用do {} while(0)是为了避免由于文本替换后带来的语法错误,并保证宏函数无论如何都会执行一次。
函数内的static对象称为local static对象,其他static对象成为non-local static对象。
所谓编译单元是指产出单一目标文件(single object file)的那些源码,基本上它是单一源码文件加上其所含入的头文件。
现在问题涉及至少两个源码文件,每一个内含至少一个non-local static对象,如果某个编译单元内的某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,它所用到的这个对象可能尚未被初始化,因为C++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。
例子:
1 | //in a.h |
在初始化tmpDirectory时需要用到tfs,这时候tfs可能还没有被初始化,这就会出现问题。
解决办法:将每个non-local static对象放到一个属于它自己的专属函数内,成为一个local static对象,这些函数返回一个reference指向它所含的对象。
接上例:
1 | //in a.h |
这种方法的基础在于:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象的定义式”时被初始化。这种方式也是Singleton模式的一个常见实现方法。
在程序的整个运行过程中都存在
静态初始化:在编译器处理文件时对变量进行初始化
动态初始化:编译后初始化
编译器将分配固定的内存块来存储所有的静态变量
,这些变量在整个程序执行期间一直存在。并且,如果没有显示地初始化静态变量,编译器将把它设置为0
。在默认情况下,静态数组和结构将每个元素或成员的所有位(bit)
都设置为0。
声明三种链接性的静态持续变量的方法:
1 | int global = 1000;//静态持续,外部链接 |
静态持续性
,外部链接性
的变量,定义
有两种方式:
1 | double up = 0.0; |
声明
外部变量的方式:
1 | extern double up;//up 在其他文件定义 |
使用static定义并同时初始化声明的变量就是静态内部变量
static声明的静态内部变量将会隐藏常规同名外部变量
1 | //in file1 |
同名变量覆盖规则:外部可见变量被本文件可见变量覆盖,局部变量覆盖全局变量
默认情况下全局变量
的链接性为外部的,但
const全局变量
的链接性为内部的。也就是全局const定义就像使用了static说明符一样。
1 | const int value = 10;//same as static const int value = 10; |
但是const可以和extern连用,声明链接性为外部的常量:
1 | extern const int value = 10; |
函数默认情况下是静态的,即在整个程序执行期间都一直存在,默认情况下,函数的链接性为外部的
,即多个文件可见
可以使用static关键字将函数的链接性设置为内部,且静态定义将覆盖外部定义
,且必须在函数的定义和实现中都使用static关键字
内联函数不受这项规则的约束,内联函数的链接性为内部的,但是c++要求同一个函数的所有内联定义都必须相同
如果定义了一个与库函数同名的函数,编译器将使用程序员定义的版本
链接程序要求每个不同的函数都有不同的符号名,在C语言中,一个名称只对应一个函数。例如c编译器可能将spiff这样的函数翻译为_spiff。
但在c++中,由于函数重载,一个名称可能对应多个函数,必须将这些函数翻译为不同的符号名称。例如可能将spiff(int)翻译为spiff_i,将spiff(double, double)翻译成spiff_d_d。
如果要在c++程序中使用c库中预编译的函数,由于有c编译器预编译的函数符号和c++要寻找的函数符号不相同,所有可能c++编译器会找不到这些函数。为了解决这个问题,可以用函数原型来指出要使用的约定:
1 | extern "C" void spiff(int);//use C protocol for name look-up |
另外的形式:
1 | #ifdef __cplusplus |
注意由于extern "C"之后将按照C的方式翻译函数的名称,所以此时将不能在extern "C" 中进行函数重载
。
new、new[]的调用实质上是分别调用:
1 | void * operator new(std::size_t); |
要使用定位new特性,首先需要包含头文件new。定位new的用法:
1 | struct chaff { |
delete和delete[]只能用于指向常规new运算符分配的堆内存,所以delete不一定能作用于定位new运算符所指向的区域,所以最好不要delete定位new运算符返回的指针
new 和 delete、new[] 和 delete[]要配套使用,delete和delete[]可以作用于空指针,delete和delete[]不能同时释放同一内存空间两次,除非是空指针,否则将会出现runtime error。
假设编译器发现,程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中。这种优化假设变量的值在这两次使用之间不会发生变化。如果不将变量声明为volatile,则编译器讲进行这种优化;将变量声明为volatile则相当于告诉编译器,不要进行这种优化。volatile的使用一般在多进程或多线程的情况下,可能其他进程或线程或改变某个内存空间的值,如果不两次获取的话,程序就会出错。
mutable用来指出即使结构或类变量为const,其某个成员也可以被修改。
1 | struct data { |
“::”放在变量前面,该运算符表示使用变量的全局版本
1 | cout << ::value << "\n" |
使用namespace创建名称空间
1 | namespace Tom { |
名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。
名称空间是开放的,即可以把名称加入到已有的名称空间中:
1 | namespace Tom { |
using声明使特定的标示符可用,using编译指令使整个名称空间可用
1 | using Tom::func;//using声明 |
如果某个名称已经在函数中声明了,则不能用using声明导入相同的名称。然而,使用了using编译指令(using namespace)时,将进行名称解析。如果使用using编译指令导入一个已经在函数中声明的名称,则局部名称将隐藏名称空间名。
1 | namespace Jill{ |
可以在名称空间中使用using编译指令和using声明:
using Jill::fetch;
using namespace std;
}1 |
|
未命名的名称空间:
namespace {
int ice;
int bandycoot;
}
不能在未命名的名称空间所属文件之外的其他文件中,使用该未命名名称空间中的名称
。未命名的名称空间中的变量相当于声明了链接性为内部的静态变量。