存档

C++11/14 新特性 (多线程)

2017/03/28 6,648

在 C++11 之前 ,C++ 标准并没有提供统一的并发编程标准,也没有提供语言级别的支持。这导致我们在编写可移植的多线程程序时很不方便,往往需要面向不同的平台进行不同的实现,或者引入一些第三方平台,如Boost,pthread_win32 等。 从C++11开始 ,对并发编程进行了语言级别的支持,使用使用C++进行并发编程方便了很多。这里介绍C++11并发编程的相关特性。

1 线程

1.1 线程的创建

std::thread 的构造函数如下:

我们只需要提供线程函数或函数对象,即可以创建线程,并可以同时指定线程函数的参数。

join函数将会阻塞线程,直到线程函数执行完毕,主线程才会接着执行。如果线程函数有返回值,返回值将被忽略。 在使用线程对象的过程中,我们需要注意线程对象的生命周期。如果线程对象先于线程函数结束,那么将会出现不可预料的错误。可以通过线程阻塞的方式来等待线程函数执行完(join),或让线程在后台执行。 如果不希望线程被阻塞,可以调用线程的 detach() 函数,将线程与线程对象分离。但需要注意的是,detach 之后的线程无法再使用join来进行阻塞了,即detach之后的线程,我们无法控制了。当我们不确定一个线程是否可以join时,可以先使用 thead::joinable() 来进行判断。

另外,我们还可以通过 std::bind, lambda 来创建线程(其实就是使用函数对象创建线程)。

线程不可以被复制,但是可以被移动:
继续阅读

C++11/14 新特性(function/bind 可调用对象包装器与绑定器)

2017/02/28 6,082

1 可调用对象

在 C++ 中,可调用对象一般是指:

  • 一个函数指针
  • 一个重载 () 操作符的类对句(仿函数)
  • 一个可被转换为函数指针的类对象
  • 一个类成员函数指针

上例中的这些对象(func_ptr,foo,bar,mem_func_ptr,mem_obj_ptr) 均可称之为 “可调用对象”。相应的,其类型可被称作“可调用类型”。注意这里只有成员函数有成员函数指针而没有函数类型或函数引用类型,这是因为函数类型并不能直接用于定义对象,而函数引用或以看做一个 const 的函数指针。 可调用对象具有比较统一的调用形式,即使用括号操作(除成员函数指针),而定义的方法各不一样。这样我们在试图使用统一的方式保存,或传递一个可调用对象时,会十分烦琐。

2 std::function 可调用对象包装器

std::function 是一个类模板,它可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟调用它们。
继续阅读

C++ API 中的版本控制

2017/01/06 5,949

1 应该提供 API 版本号信息

这个类提供了单独返回当前版本号的主、次、补丁版本号的的访问函数。它们返回各 define 定义的值。GetVersion 方法向用户返回友好的字符串版本号信息。 当用户相比较版本号时,他们通常不关心版本号本身,而是想知道某些特性在该版本的API中是否存在。HasFeature() 方法 不是告诉用户哪个版本的 API 引入了哪些特性,而是让亿们直接测试某个特性是否可用。

2 软件分支策略

一般的大型项目通常会涉及到某种形式的分支策略,这需要同步开发、维护不同的软件版本发布。我们将讨论为项目选择分支策略和方针时需要考虑的一些事项。

2.1 分支策略

每个软件都需要一条 trunk (主干)代码路线,它是cppAPIversion软件项目源码
的持久库。对于对于每次版本的发布
,可以从主干进行。每次新版本的开发,可以从主干的代码添加分支,而使主干的代码不受开发的影响而保持稳定。右图是一种常见的分支策略。

2.2 API与并行分支

在API发布以后,对其所有的更改都应该表现为一个连续的过程,即不发布不兼容的非线性版本的API,一个版本的API应该是前一个版本功能的严格超集。在大型项目中,通常会有几条并行的代码分支在进行同时开发,这就会产生若干个并行维护的API版本。因此,工作在不同并行分支上的团队互不引入不兼容的特性是非常重要的。下列方法可以帮忙处理这种潜在的问题:
继续阅读

C++11/14 新特性 (使用 chrono 进行时间处理)

2017/01/03 5,473

在c++11以前,我们获取当前时间的时间戳,需要借助于第三方库,如 boost ,或者针对不同的操作做不同的处理:

而在c++11中,这个问题得到解决。 c++11 标准库中提供 chrono库,用来处理时间相关的数据。

1 duration 记录时长

duration 表示一段时间间隔。其原型:


_Rep 为一个数值类型,如 int, long, 用来表示时钟数的类型。 _Period 为一个默认的模板参数 std::ratio,表示时钟周期。 ratio的原型:

它表示一个时钟周期的秒数。_Num 代表分子,_Den代表分母,它们的比值就是一个时钟周期,可以通过调整分子与分母来表示任意一个时钟周期。如 ratio<1> 表示一个时钟周期为1秒,ratio<3600>表示一个时钟周期为1小时,ratio<1,1000>表示一个时钟周期为1毫秒,ratio<1,2>表示一个时钟周期为0.2秒,ratio<9/7>表示一个时钟周期为9/7秒。 标准库将一些常用的时钟周期做了定义:

在不同的时钟周期中,我们可以使用 chrono_cast 来进行转换。两个 duration还可以进行加减运算:

继续阅读

C++11/14 新特性(for循环)

2016/12/05 6,121

在C++ 中,遍历一个容器的方法一般是这样的:

对STL比较熟悉的程序员肯定还知道在 <algorithm> 中有一个 for_each 算法,可以用来完成上述功能:

这里借助了 auto 关键字和 lambda 表达式简化了操作。 std::for_each 比起前面 for 循环,最大的好处是不再需要关注迭代器的概念,而只需要关心容器中的元素类型即可。 但这两种方法,都必须显式的给出容器有开头和结尾(begin,end).这是因为上面的两种方法都不是基于范围(range)来设计的. 范围的概念在很多高级语言中都有涉及。例如下面这段 python 代码:

在这种循环中,不再需要关心容器的两端,循环会自动以容器的范围进行展开。而且这种语法可以清楚地表明它的意义。使用这种方式进行循环无疑会使编码和维护更加简便。 现在,c++11 可有了基于范围的 for 循环

继续阅读

C++11/14 新特性(类型/初始化/智能指针)

2016/12/01 5,908

数据类型cpplogo

1.long long

long long 数据类型,提供至少 64 位的整型数据。

2.nullptr

C++11 推荐使用 nullptr 表示空指针,不再推荐使用 NULL 或 0 表示空指针。 NULL为从 C 语言中引进的,在 C 语言中一般被定义为宏:

在 C 语言中,可以将 void* 隐式转换为任意指针类型,而在 C++ 中,可以为任意类型的指针赋 0 值。所以 c++ 将 NULL 定义为 0. nullptr 是指针类型(nullptr_t),该类型的对象不允许转换到非指针类型。

3.contexpr

1).const expression  常量表达式

是指值不会改变且在编译过程中就能得到计算结果的表达式。字面量属于常量表达式,使用常量表达式初始化的 const 对象也是常量表达式。一个对象或表达式是不是常量表达式要由它的数据类型和初始值共同决定:

尽管 size 变量的初始化值是字面常量 ,但是它的定义数据类型是 int 而不是 const int 。 尽管 sz 定义为 const int ,但其值需要运行时才能得到,所以它也不是常量表达式。

2).constexpr 变量

C++11 中,允许将变量声明为 constexpr 类型,以便由编译器检查变量是否为一个常量表达式:声明为 constexpr 的变量一定是一个常量 ,且必须使用常量表达式初始化:

3).constexpr 函数

constexpr 可用于指示函数是一个常量表达式。这需要满足:

  • 返回值类型是字面值类型
  • 形参类型是字面值类型
  • 函数体中必须有且仅有一条 return 语句

4).constexpr 与 const

这里的 p 和 q 本质是不一样的。指针 p 指向的整数是常量,而指针 q 本身是常量,其指向的整数则可以不是常量:

其中的关键在于constexpr把它所定义的对象置为了顶层const.

4.noexcept

noexcept 指示一个函数不会抛出异常。如果一个使用 noexcept 修饰的函数内部抛出了异常,编译器并不会报错,但

继续阅读

网络协议详解之 HTTP 协议

2016/11/20 3,036

概述

HTTP(HyperText Transfer Protocal)超文本传输协议, 是一个基于请求与响应模式的、无状态的应用层协议。它是 WEB 上应用最广泛的协议。它一般基于 TCP 的连接方式。其主要特点有:

  • 支持 C/S 通信模式
  • 简单快速。HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信快。
  • 灵活。HTTP 协议允许客户端和服务端传输任意类型任意格式的数据。不同的类型由 Content-Tyoe 标记
  • 面向无连接。无连接是指每次建立的连接只处理一个文请求。
  • 无状态。无状态是指协议对于事务处理没有记忆能力。如果后续处理需要前面的信息,则它必须重传。这样可有导致每次连接传送的数据量增大。

HTTP URL

URL(Uniform Resource Locator) 统一资源定位符,它包含了查找某个资源的信息。其格式如下:

http 指定协议的名称,表示要通过 HTTP 协议来定位网络资源。 host 是一个合法的网络域名 或 IP 地址。 port 指定使用的网络端口,缺省值为 80 。 abs_path 指定请求的资源的 URI .如果 URL 中没有给出 URI, 则必须以 "/" 符号结束(这个工作通常由浏览器完成)。

连接

浏览器与服务器联系的最常用方法是与服务器的 80 端口建立 TCP 连接。使用 TCP 的意义在于,浏览器和服务器都不需要担心如何处理长消息、可靠性与拥塞控制,这些事将由 TCP 来处理。 在早期的 HTTP1.0 中,连接建立起来后会在一个请求和一个响应后立即释放。因为那时的 HTML 很简单,基本只有文本,使用这种模式就够了。但是随着时代的发展,HTML里包含了太多的东西,使用单独的 TCP 来传递每个资源代价太大。于是 HTTP1.1诞生了,它支持持续连接(persistent connection),它可以在一个 TCP 连接上进行多次请求响应,还可以发送流水线请求。这种做法减少建立多个 TCP 连接所用的时间,减少服务器的空闲时间,提高了性能。

HTTP 协议的请求

每个 HTTP Request (请求) 由一行或多行 ASCII 文本组成。其中第一行的第一个词为请求方法的名称,然后是请求资源的 URI,再后是协议的版本。这几个部分使用空格分开:

Method 为请求方法,必须为大写。 Request-URI 是一个统一资源标识符。  HTTP-Version 表示请求的 HTTP 协议版本。 CRLF 表示换行符。 例如:

常用的请求方法如下: 继续阅读

lambda 表达式 (in Python)

2016/11/16 5,562

概述lambda

python 中的 lambda 比较简单,只是作为创建匿名函数来使用

  • lambda 只是一个表达式,而不是一个代码块。类似于 C++ 中的 define ,但比 define 要简单.Python 中的 lambda 只能封装有限的逻辑进去。
  • lambda 拥有自己的全名空间,且不能访问自有参数列表之外及全局的参数。

语法

labmda 函数的语法只包含一个语句:

lambda 可以传入多个参数,使用逗号 "," 分隔。 exception 使用这些参数进行运算并将结果返回。这里的 exception 隐藏了 return 关键词。 示例:

以上输出:

在 python 中的使用

lambda 的使用比较灵活。例如可以在函数内部定义匿名函数,实现简单逻辑并在函数内部使用。另一个常见的应用场景则是作为匿名函数对象在迭代器中使用。比如:

  • map : 对迭代器招待 function 操作,并将操作后的 list 返回
  • filter : function 应该返回一个布尔值,对迭代器操作为 False 的,将从 List 里排除
  • reduce : 对迭代器进行累计操作,即将上一次操作的结果作为function 的第一个参数进行下一次操作。reduce 最后一个参数作为可选参数,将在首先参与运算。

这几个函数的一般用法如下:

可以看到,我们需要为每个函数再定义一个函数,作为参数传入。 如果我们使用 lambda, 可以简化这些工作:

上面两种写法都输出:

但是,显然使用lambda的写法更加优雅。