书籍是非常主观和私人的财产，编程书籍也不例外。但是不管 C++ 编程书籍的风格、关注点或者节奏如何，好书总可以带领读者走过一段引人入胜的旅程，揭示编程语言的能力，还能向读者描绘如何使用编程语言来实现各种事物。分享是一种美德，我精心挑选了九本值得一读的优质 C++ 书籍，这些书籍均基于开源协议发布。在这之前，我会给出一份 C++ 的简短介绍。C++ 是由 Bjarne Stroustrup 设计，初次发布于 1983 年。它是一种静态类型、格式自由、多重范式、可移植、编译式的通用编程语言。它被认为是中级语言，同时包含有高级与初级编程语言的特性。C++ 设计用于实现系统级与应用的编程，它拓展了 C 编程语言，因此其名字中的使用了自增运算符 ++。C++ 仍是一种流行的编程语言。例如，它被广泛应用于嵌入式系统、银行以及通讯业。它作为 C 的超集保留了 C 标志性的简洁与高效，同时提供强类型检查、多重继承、数据抽象、异常处理操作符重载、泛型编程，以及面向对象编程等功能。C++ 不仅影响了 C 语言的发展，同时也影响到了包括 C#、Java 在内的许多其他编程语言。《 Boost C++ 类库 （ The Boost C++ Libraries ） 》作者 Boris Schäling (570页)《 Boost C++ 类库 （ The Boost C++ Libraries ） 》被认为在 C++ 世界中极其重要并有深远影响。书中这些可移植的库提供对多种任务和结构体的支持，包括多线程、容器、字符串和文本处理、迭代器、线性代数、伪随机数产生、元程序设计模板、并发程序设计、数据结构、图像处理、正则表达式和单元测试。Boost 可以在几乎所有现代操作系统上工作，包括 Linux 和 Windows 及其衍生，并且支持绝大多数现代编译器。这本书介绍了 72 个 Boost 库，提供了广泛并且实用的功能。它们能够帮助程序员更轻松的管理内存和处理字符串。这些库提供多种容器以及其它数据结构来拓展标准库。使用这些库可以轻松实现平台无关的网络应用程序。本书是一颗值得添加到各类收藏中的明珠。430 份源代码例子生动形象地阐释了这些库的功能。本书前面的章节审视了内存管理、字符串处理、容器、数据结构、算法、通信、文件与流以及时间。后续章节进一步探索了函数式编程、并行编程和泛型编程。以对语言拓展、错误与数字处理、应用程序库、设计模式以及其他库的大部分内容。本书采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。如果你喜欢实体书，可以在亚马逊上购买纸质书，也可选择如 kindle、E-book 和 PD F格式的电子书。《 C++ 注释 （ C++ Annotations ） 》作者 Frank B. Brokken (1029页)《 C++ 注释 （ C++ Annotations ） 》提供了关于 C++ 编程语言的一份全面的教程。它可以被当做一门 C++ 课程的教材。本书面向已掌握 C 语言或其他类 C 语法知识的使用者。本书主要章节有：命名空间字符串——C 提供的是最基础的字符串支持I/O 流库——提供了一个基于类概念的输入/输出库类——C 提供了两种结构化不同类型数据的方法。C 语言的结构体包含多种类型的数据成员，而 C 语言的 共用体 （ union ） 同样可以定义不同类型的数据成员。本章介绍的类，也是一种结构体但是它的内容对外部世界来说是默认不可访问的。静态数据和函数内存管理——审视 C++ 中能够操作内存分配的操作符异常——允许 C++ 程序执行受控的非本地返回命令，避免了使用 longjmp（非局部跳转）和 setjmp（激活非局部跳转）函数的缺陷。操作符重载——介绍通用的操作符重载抽象容器继承——派生的另外一个特性。 本章演示基类指针可能被用来指向派生类的对象。多态——继承的一种特殊形态友元机制——介绍 friend 关键词以及它的使用原则成员指针——定义成员指针及其用法，静态成员指针，指针长度嵌套类——当嵌套类与其外围类有概念上的紧密联系时会被使用标准模板库（STL）——包含容器、通用算法、迭代器、函数对象、分配器、适配器和数据结构的通用库。这些算法使用的数据结构都是抽象意义的，因此算法实际上可以使用任何数据类型。泛型算法——涵盖标准模板库中的泛型算法函数模板——探索模板独特的句法。介绍模板类型参数与模板非类型参数和函数模板等概念并且提供模板的多个案例。类模板——讨论构建与使用类模板进阶模板用法——一份简短的模板元编程主要特性与模板的微妙关系的介绍本书有HTML、PDF、PostScript 和 txt 版本。它可以自由分发，基于 GNU GPL 协议发布。《 通过 C++ 和 Qt4 了解设计模式 （ An Introduction to Design Patterns in C++ with Qt 4 ） 》作者 Alan Ezust, Paul Ezust (656页)《 通过 C++ 和 Qt4 了解设计模式 （ An Introduction to Design Patterns in C++ with Qt 4 ） 》从介绍 C++ 基础知识、面向对象概念、UML（统一建模语言）和核心 Qt 类开始。进而转向更高级的编程概念如 Qt modules 和设计模式。最后严密地印证了重要的 C++ 特性。其中涵盖了非常优秀的对函数、继承和多态的讲解。本书不需要具备任何 C 或者 C++ 编程经验前提，并且被设计为普适用途。它也为教学工作者准备了 Qt 案例、练习、答案以及课程 PPT。本书是 Bruce Perens 开源系列中的一部分。所有在此系列中的书籍都是基于 Open Publication License V1.0 及其后版本发布的。《 像计算机科学家一样思考：C++ （ How to Think Like a Computer Scientist: C++ ） 》作者 Allen B. Downey (191页)《 像计算机科学家一样思考：C++ （ How to Think Like a Computer Scientist: C++ ） 》是使用 C++ 编程语言进行软件设计的一本简洁友好的介绍性书籍。本书的目的是通过讲解最基础的概念并且以读者容易接受的节奏逐步增加新内容来把没有编程经验的读者培养成为未来的开发者。本书提供的有价值的内容包括：变量、表达式和语句函数条件语句与递归丰富的函数迭代字符串向量成员函数对象的向量向量的对象类与不变量文件输入输出和 apmatrixes本书是一本在知识共享署名-非商业性使用-3.0 未本地化版本协议下发布的免费书籍。《 C++ Qt3 图形界面编程 （ C++ GUI Programming with Qt 3 ） 》作者 Jasmin Blanchette, Mark Summerfield (464 页)最新发布的 Qt 稳定版本是 5.8，而《 C++ Qt3 图形界面编程 （ C++ GUI Programming with Qt 3 ） 》指导读者如何使用 Qt3 编写 GUI 程序，Qt3 最近一次更新是 2004 年，但是本书仍然有大部分内容对 Qt4 和 Qt5 有效。本书不是一本面向初学者的书，需要读者对 C++ 有基本的理解。本书向读者介绍了使用 Qt 进行 GUI 应用编程所有的概念和需要的练习。本书不仅详尽的讲述了核心内容，而且也提供了一些特别的和高阶的学习材料。本书是 Bruce Perens 开源系列中的一部分。所有在此系列中的书籍都是基于 Open Publication License V1.0及之后版本发布的。《 开放数据结构（C++ 版） （ Open Data Structures (in C++) ） 》作者 Pat Morin (336页)《 开放数据结构（C++ 版） （ Open Data Structures (in C++) ） 》教导读者设计和分析基础数据结构以及如何使用 C++ 实现。 它涵盖了对如下数据结构的分析和实现：序列（列表）、队列、优先队列、无序字典、有序字典以及图。作者的意图在于向大学计算机科学的学生提供一条学习数据结构的免费的途径，但是并不打算成为介绍 C++ 编程语言或者 C++ 标准模板库的一本书。不过它可以帮助程序员理解标准模板库的数据结构是如何实现的以及这种实现为何如此高效。章节内容覆盖了基于数组的列表、链表、分级链表、哈希表、二叉树（又包含了随机二叉搜索树、替罪羊树、红黑树）。之后的章节还包括堆、排序算法（比较、计数和基数排序）、图、整数的数据结构和外部存储器搜索。本书基于知识共享署名 协议发布。免费的 HTML、PDF 均已发布，本书的 LaTeX 版本，Java/C++/Python 源代码都可以从 GitHub 下载。也有纸质书版本可供购买。本书已被翻译为斯罗维尼亚语和土耳其语。《 使用 wxWidgets 进行跨平台 GUI 编程 （ Cross-Platform GUI Programming with wxWidgets ） 》作者 Julian Smart，Kevin Hock和Stefan CsomorBrifll (744 页)wxWidgets 是一个流行的 C++ 库，可供开发者使用单一的代码基础为 Windosw、Mac OS、Linux 和其他平台创建应用。它支持非常广泛的图形处理库。这本书《 使用 wxWidgets 进行跨平台 GUI 编程 （ Cross-Platform GUI Programming with wxWidgets ） 》从简单的介绍和起步章节开始，主要内容包括：事件处理窗口基础画图输入事件处理使用 sizers 进行窗口布局使用标准对话框创建自定义对话框图像编程剪贴板与拖放动作高阶窗口类文件和数据流内存管理，调试和错误检查编写国际化应用编写多线程应用wxSocket 编程处理文档和视图日臻完美你的应用本书是 Bruce Perens 开源系列中的一部分。所有在此系列中的书籍都是基于 Open Publication License V1.0及其后版本发布的。《 Rook 的 C++ 指南 （ The Rook’s Guide to C++ ） 》作者 Jeremy Hansen (160页)《 Rook 的 C++ 指南 （ The Rook’s Guide to C++ ） 》的章节中包含变量、常量、输入输出、数据类型和转换、条件判断语句（if、else 和 else if、switch 语句）、字符串、循环、数组、块、函数和作用域。之后的章节主要集中解决问题和定位问题，包括预处理器、高等代数、文件输入输出、指针、动态数据、类和抽象、分离编译和标准模板库。本书大部分都是由 25 位 Norwich 大学的学生于一个黑客马拉松周末写成。当然不敢说毫无缺点，但还是一本相当不错的书。它被基于知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本协议发布。同时在亚马逊也有本书的纸质版可供购买。《 GCC 简介 （ An Introduction to GCC ） 》作者 Brian Gough (144页)《 GCC 简介 （ An Introduction to GCC ） 》介绍了 GCC，同时介绍了 GNU C 和 C++ 编译器：gcc 和 g++，均属于 GNU 编译器集合（GCC）。本书解释了如何单独使用编译器。作者通过数年对邮件列表中发表的问题的观察，撰写本书带领读者直接接触 GCC 最重要的部分。章节简介：介绍编译一个 C 程序——描述了如何使用 gcc 编译 C 程序。程序可能是由单独文件或者多个文件编译而成，也可能使用系统库和头文件。编译选项——描述gcc中可以使用的编译器中常用的选项。这些选项可以修改本地库和包含文件的搜索路径，配置额外的 warning 和调试信息，预处理器宏指令和 C 方言。使用预处理——描述了属于 GCC 包内的 GNU C 预处理 cpp 程序的用途。预处理将宏定义在源代码被编译前展开。预处理会在 GCC 编译 C 或者 C++ 程序时自动被调用。以调试为目的编译——提供 -g 选项使目标文件和可执行文件中存储额外的调试信息。当出现错误时，这些调试信息允许从特定的机器指令回溯到源代码中相应的行。优化编译——GCC 是一个优化编译器。它提供了大量的选项来使其生成的可执行文件的速度得到提升，并尽量减小可执行文件的大小。编译一个 C++ 程序——描述了如何使用 GCC 来编译以 C++ 编写的程序，以及针对这门语言特定的命令行选项。平台相关的选项——描述了一些通用平台上可用的选项，如 Intel 和 AMD x86 选项、x86 拓展选项、x86 64 位处理器选项、DEC Alpha 选项、SPARC 选项、POWER/powerPC 选项、复合架构支持以及浮点相关问题。问题定位——GCC 提供了几项帮助和诊断选项来帮助用户在编译过程中定位问题。编译器相关工具——介绍了大量能够用于和 GCC 组合使用的工具。包括：用来创建库的 GNU 压缩器 ar，以及 GNU 性能和覆盖测试工具：gprof 和 gcov。编译器如何工作——描述了关于 GCC 如何将源代码转换为可执行程序的更多细节。编译是一个涉及到多种工具的多级过程，包括 GNU 编译器自身（通过 gcc 或者 g++ 前端）、GNU 汇编器 as 以及 GNU 链接器 ld。编译过程中一个完整的工具集合被称之为工具链。检查编译后的文件——描述了多种有用的工具来检查可执行文件和目标文件的内容。常见错误消息——描述了 gcc 和 g++ 产生的最常见的错误和警告信息。每一个案例都伴随着错误和警告产生的原因，相应的例子和可能解决问题的建议。获得帮助——如果读者碰到了本书中未能涵盖的问题，还有多种参考手册更详细的描述了 GCC 和编程语言相关的主题。本书是基于 GNU Free Documentation 协议出版的。此外还有一些 C++ 书籍可以免费下载，但是那些并不是基于开源协议发布的，或者作者没有指定一个协议。这类书籍包括：《Thinking in C++，第二版，卷一、卷二》 – 作者 Bruce Eckel (卷二作者增加了 Chuck Allison)《C++ In Action:Industrial Strength Programming》– 作者 Bartosz Milewski最后，我对刚入门的 C++ 新手的建议一定是《 编程原理与体验（C++ 版） （ Programming — Principles and Practice Using C++ ） (第二版)》。普遍认为这是最合适的入门书籍，由 C++ 的创始人书写，物超所值。via: https://www.ossblog.org/master-c-programming-with-open-source-books/作者：Steve Emms 译者：xiaow6 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这篇教程提供了一个面向 C++ 程序员关于 protocol buffers 的基础介绍。通过创建一个简单的示例应用程序，它将向我们展示：在 .proto 文件中定义消息格式使用 protocol buffer 编译器使用 C++ protocol buffer API 读写消息这不是一个关于在 C++ 中使用 protocol buffers 的全面指南。要获取更详细的信息，请参考 Protocol Buffer Language Guide 和 Encoding Reference。为什么使用 Protocol Buffers我们接下来要使用的例子是一个非常简单的"地址簿"应用程序，它能从文件中读取联系人详细信息。地址簿中的每一个人都有一个名字、ID、邮件地址和联系电话。如何序列化和获取结构化的数据？这里有几种解决方案：以二进制形式发送/接收原生的内存数据结构。通常，这是一种脆弱的方法，因为接收/读取代码必须基于完全相同的内存布局、大小端等环境进行编译。同时，当文件增加时，原始格式数据会随着与该格式相关的软件而迅速扩散，这将导致很难扩展文件格式。你可以创造一种 ad-hoc 方法，将数据项编码为一个字符串——比如将 4 个整数编码为 12:3:-23:67。虽然它需要编写一次性的编码和解码代码且解码需要耗费一点运行时成本，但这是一种简单灵活的方法。这最适合编码非常简单的数据。序列化数据为 XML。这种方法是非常吸引人的，因为 XML 是一种适合人阅读的格式，并且有为许多语言开发的库。如果你想与其他程序和项目共享数据，这可能是一种不错的选择。然而，众所周知，XML 是空间密集型的，且在编码和解码时，它对程序会造成巨大的性能损失。同时，使用 XML DOM 树被认为比操作一个类的简单字段更加复杂。Protocol buffers 是针对这个问题的一种灵活、高效、自动化的解决方案。使用 Protocol buffers，你需要写一个 .proto 说明，用于描述你所希望存储的数据结构。利用 .proto 文件，protocol buffer 编译器可以创建一个类，用于实现对高效的二进制格式的 protocol buffer 数据的自动化编码和解码。产生的类提供了构造 protocol buffer 的字段的 getters 和 setters，并且作为一个单元来处理读写 protocol buffer 的细节。重要的是，protocol buffer 格式支持格式的扩展，代码仍然可以读取以旧格式编码的数据。在哪可以找到示例代码示例代码被包含于源代码包，位于“examples”文件夹。可在这里下载代码。定义你的协议格式为了创建自己的地址簿应用程序，你需要从 .proto 开始。.proto 文件中的定义很简单：为你所需要序列化的每个数据结构添加一个 消息 （ message ） ，然后为消息中的每一个字段指定一个名字和类型。这里是定义你消息的 .proto 文件 addressbook.proto。package tutorial; message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; } 如你所见，其语法类似于 C++ 或 Java。我们开始看看文件的每一部分内容做了什么。.proto 文件以一个 package 声明开始，这可以避免不同项目的命名冲突。在 C++，你生成的类会被置于与 package 名字一样的命名空间。下一步，你需要定义 消息 （ message ） 。消息只是一个包含一系列类型字段的集合。大多标准的简单数据类型是可以作为字段类型的，包括 bool、int32、float、double 和 string。你也可以通过使用其他消息类型作为字段类型，将更多的数据结构添加到你的消息中——在以上的示例，Person 消息包含了 PhoneNumber 消息，同时 AddressBook 消息包含 Person 消息。你甚至可以定义嵌套在其他消息内的消息类型——如你所见，PhoneNumber 类型定义于 Person 内部。如果你想要其中某一个字段的值是预定义值列表中的某个值，你也可以定义 enum 类型——这儿你可以指定一个电话号码是 MOBILE、HOME 或 WORK 中的某一个。每一个元素上的 = 1、= 2 标记确定了用于二进制编码的唯一 “标签” （ tag ） 。标签数字 1-15 的编码比更大的数字少需要一个字节，因此作为一种优化，你可以将这些标签用于经常使用的元素或 repeated 元素，剩下 16 以及更高的标签用于非经常使用的元素或 optional 元素。每一个 repeated 字段的元素需要重新编码标签数字，因此 repeated 字段适合于使用这种优化手段。每一个字段必须使用下面的修饰符加以标注：required：必须提供该字段的值，否则消息会被认为是 “未初始化的” （ uninitialized ） 。如果 libprotobuf 以调试模式编译，序列化未初始化的消息将引起一个断言失败。以优化形式构建，将会跳过检查，并且无论如何都会写入该消息。然而，解析未初始化的消息总是会失败（通过 parse 方法返回 false）。除此之外，一个 required 字段的表现与 optional 字段完全一样。optional：字段可能会被设置，也可能不会。如果一个 optional 字段没被设置，它将使用默认值。对于简单类型，你可以指定你自己的默认值，正如例子中我们对电话号码的 type 一样，否则使用系统默认值：数字类型为 0、字符串为空字符串、布尔值为 false。对于嵌套消息，默认值总为消息的“默认实例”或“原型”，它的所有字段都没被设置。调用 accessor 来获取一个没有显式设置的 optional（或 required） 字段的值总是返回字段的默认值。repeated：字段可以重复任意次数（包括 0 次）。repeated 值的顺序会被保存于 protocol buffer。可以将 repeated 字段想象为动态大小的数组。你可以查找关于编写 .proto 文件的完整指导——包括所有可能的字段类型——在 Protocol Buffer Language Guide 里面。不要在这里面查找与类继承相似的特性，因为 protocol buffers 不会做这些。required 是永久性的在把一个字段标识为 required 的时候，你应该特别小心。如果在某些情况下你不想写入或者发送一个 required 的字段，那么将该字段更改为 optional 可能会遇到问题——旧版本的读者（LCTT 译注：即读取、解析旧版本 Protocol Buffer 消息的一方）会认为不含该字段的消息是不完整的，从而有可能会拒绝解析。在这种情况下，你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google 的一些工程师得出了一个结论：使用 required 弊多于利；他们更愿意使用 optional 和 repeated 而不是 required。当然，这个观点并不具有普遍性。编译你的 Protocol Buffers既然你有了一个 .proto，那你需要做的下一件事就是生成一个将用于读写 AddressBook 消息的类（从而包括 Person 和 PhoneNumber）。为了做到这样，你需要在你的 .proto 上运行 protocol buffer 编译器 protoc：如果你没有安装编译器，请下载这个包，并按照 README 中的指令进行安装。现在运行编译器，指定源目录（你的应用程序源代码位于哪里——如果你没有提供任何值，将使用当前目录）、目标目录（你想要生成的代码放在哪里；常与 $SRC_DIR 相同），以及你的 .proto 路径。在此示例中：protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto 因为你想要 C++ 的类，所以你使用了 --cpp_out 选项——也为其他支持的语言提供了类似选项。在你指定的目标文件夹，将生成以下的文件：addressbook.pb.h，声明你生成类的头文件。addressbook.pb.cc，包含你的类的实现。Protocol Buffer API让我们看看生成的一些代码，了解一下编译器为你创建了什么类和函数。如果你查看 addressbook.pb.h，你可以看到有一个在 addressbook.proto 中指定所有消息的类。关注 Person 类，可以看到编译器为每个字段生成了 读写函数 （ accessors ） 。例如，对于 name、id、email 和 phone 字段，有下面这些方法：（LCTT 译注：此处原文所指文件名有误，径该之。）// name inline bool has_name() const; inline void clear_name(); inline const ::std::string& name() const; inline void set_name(const ::std::string& value); inline void set_name(const char* value); inline ::std::string* mutable_name(); // id inline bool has_id() const; inline void clear_id(); inline int32_t id() const; inline void set_id(int32_t value); // email inline bool has_email() const; inline void clear_email(); inline const ::std::string& email() const; inline void set_email(const ::std::string& value); inline void set_email(const char* value); inline ::std::string* mutable_email(); // phone inline int phone_size() const; inline void clear_phone(); inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const; inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone(); inline const ::tutorial::Person_PhoneNumber& phone(int index) const; inline ::tutorial::Person_PhoneNumber* mutable_phone(int index); inline ::tutorial::Person_PhoneNumber* add_phone(); 正如你所见到，getters 的名字与字段的小写名字完全一样，并且 setter 方法以 set_ 开头。同时每个 单一 （ singular ） （required 或 optional）字段都有 has_ 方法，该方法在字段被设置了值的情况下返回 true。最后，所有字段都有一个 clear_ 方法，用以清除字段到 空 （ empty ） 状态。数字型的 id 字段仅有上述的基本 读写函数 （ accessors ） 集合，而 name 和 email 字段有两个额外的方法，因为它们是字符串——一个是可以获得字符串直接指针的mutable_ 的 getter ，另一个为额外的 setter。注意，尽管 email 还没被 设置 （ set ） ，你也可以调用 mutable_email；因为 email 会被自动地初始化为空字符串。在本例中，如果你有一个单一的（required 或 optional）消息字段，它会有一个 mutable_ 方法，而没有 set_ 方法。repeated 字段也有一些特殊的方法——如果你看看 repeated 的 phone 字段的方法，你可以看到：检查 repeated 字段的 _size（也就是说，与 Person 相关的电话号码的个数）使用下标取得特定的电话号码更新特定下标的电话号码添加新的电话号码到消息中，之后你便可以编辑。（repeated 标量类型有一个 add_ 方法，用于传入新的值）为了获取 protocol 编译器为所有字段定义生成的方法的信息，可以查看 C++ generated code reference。枚举和嵌套类与 .proto 的枚举相对应，生成的代码包含了一个 PhoneType 枚举。你可以通过 Person::PhoneType 引用这个类型，通过 Person::MOBILE、Person::HOME 和 Person::WORK 引用它的值。（实现细节有点复杂，但是你无须了解它们而可以直接使用）编译器也生成了一个 Person::PhoneNumber 的嵌套类。如果你查看代码，你可以发现真正的类型为 Person_PhoneNumber，但它通过在 Person 内部使用 typedef 定义，使你可以把 Person_PhoneNumber 当成嵌套类。唯一产生影响的一个例子是，如果你想要在其他文件前置声明该类——在 C++ 中你不能前置声明嵌套类，但是你可以前置声明 Person_PhoneNumber。标准的消息方法所有的消息方法都包含了许多别的方法，用于检查和操作整个消息，包括：bool IsInitialized() const; ：检查是否所有 required 字段已经被设置。string DebugString() const; ：返回人类可读的消息表示，对调试特别有用。void CopyFrom(const Person& from);：使用给定的值重写消息。void Clear();：清除所有元素为空的状态。上面这些方法以及下一节要讲的 I/O 方法实现了被所有 C++ protocol buffer 类共享的 消息 （ Message ） 接口。为了获取更多信息，请查看 complete API documentation for Message。解析和序列化最后，所有 protocol buffer 类都有读写你选定类型消息的方法，这些方法使用了特定的 protocol buffer 二进制格式。这些方法包括：bool SerializeToString(string* output) const;：序列化消息并将消息字节数据存储在给定的字符串中。注意，字节数据是二进制格式的，而不是文本格式；我们只使用 string 类作为合适的容器。bool ParseFromString(const string& data);：从给定的字符创解析消息。bool SerializeToOstream(ostream* output) const;：将消息写到给定的 C++ ostream。bool ParseFromIstream(istream* input);：从给定的 C++ istream 解析消息。这些只是两个用于解析和序列化的选择。再次说明，可以查看 Message API reference 完整的列表。Protocol Buffers 和面向对象设计Protocol buffer 类通常只是纯粹的数据存储器（像 C++ 中的结构体）；它们在对象模型中并不是一等公民。如果你想向生成的 protocol buffer 类中添加更丰富的行为，最好的方法就是在应用程序中对它进行封装。如果你无权控制 .proto 文件的设计的话，封装 protocol buffers 也是一个好主意（例如，你从另一个项目中重用一个 .proto 文件）。在那种情况下，你可以用封装类来设计接口，以更好地适应你的应用程序的特定环境：隐藏一些数据和方法，暴露一些便于使用的函数，等等。但是你绝对不要通过继承生成的类来添加行为。这样做的话，会破坏其内部机制，并且不是一个好的面向对象的实践。写消息现在我们尝试使用 protocol buffer 类。你的地址簿程序想要做的第一件事是将个人详细信息写入到地址簿文件。为了做到这一点，你需要创建、填充 protocol buffer 类实例，并且将它们写入到一个 输出流 （ output stream ） 。这里的程序可以从文件读取 AddressBook，根据用户输入，将新 Person 添加到 AddressBook，并且再次将新的 AddressBook 写回文件。这部分直接调用或引用 protocol buffer 类的代码会以“// pb”标出。#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" // pb using namespace std; // This function fills in a Person message based on user input. void PromptForAddress(tutorial::Person* person) { cout << "Enter person ID number: "; int id; cin >> id; person->set_id(id); // pb cin.ignore(256, '\n'); cout << "Enter name: "; getline(cin, *person->mutable_name()); // pb cout << "Enter email address (blank for none): "; string email; getline(cin, email); if (!email.empty()) { // pb person->set_email(email); // pb } while (true) { cout << "Enter a phone number (or leave blank to finish): "; string number; getline(cin, number); if (number.empty()) { break; } tutorial::Person::PhoneNumber* phone_number = person->add_phone(); //pb phone_number->set_number(number); // pb cout << "Is this a mobile, home, or work phone? "; string type; getline(cin, type); if (type == "mobile") { phone_number->set_type(tutorial::Person::MOBILE); // pb } else if (type == "home") { phone_number->set_type(tutorial::Person::HOME); // pb } else if (type == "work") { phone_number->set_type(tutorial::Person::WORK); // pb } else { cout << "Unknown phone type. Using default." << endl; } } } // Main function: Reads the entire address book from a file, // adds one person based on user input, then writes it back out to the same // file. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; // pb if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; return -1; } tutorial::AddressBook address_book; // pb { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!input) { cout << argv[1] << ": File not found. Creating a new file." << endl; } else if (!address_book.ParseFromIstream(&input)) { // pb cerr << "Failed to parse address book." << endl; return -1; } } // Add an address. PromptForAddress(address_book.add_person()); // pb { // Write the new address book back to disk. fstream output(argv[1], ios::out | ios::trunc | ios::binary); if (!address_book.SerializeToOstream(&output)) { // pb cerr << "Failed to write address book." << endl; return -1; } } // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); // pb return 0; } 注意 GOOGLE_PROTOBUF_VERIFY_VERSION 宏。它是一种好的实践——虽然不是严格必须的——在使用 C++ Protocol Buffer 库之前执行该宏。它可以保证避免不小心链接到一个与编译的头文件版本不兼容的库版本。如果被检查出来版本不匹配，程序将会终止。注意，每个 .pb.cc 文件在初始化时会自动调用这个宏。同时注意在程序最后调用 ShutdownProtobufLibrary()。它用于释放 Protocol Buffer 库申请的所有全局对象。对大部分程序，这不是必须的，因为虽然程序只是简单退出，但是 OS 会处理释放程序的所有内存。然而，如果你使用了内存泄漏检测工具，工具要求全部对象都要释放，或者你正在写一个 Protocol Buffer 库，该库可能会被一个进程多次加载和卸载，那么你可能需要强制 Protocol Buffer 清除所有东西。读取消息当然，如果你无法从它获取任何信息，那么这个地址簿没多大用处！这个示例读取上面例子创建的文件，并打印文件里的所有内容。#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" // pb using namespace std; // Iterates though all people in the AddressBook and prints info about them. void ListPeople(const tutorial::AddressBook& address_book) { // pb for (int i = 0; i < address_book.person_size(); i++) { // pb const tutorial::Person& person = address_book.person(i); // pb cout << "Person ID: " << person.id() << endl; // pb cout << " Name: " << person.name() << endl; // pb if (person.has_email()) { // pb cout << " E-mail address: " << person.email() << endl; // pb } for (int j = 0; j < person.phone_size(); j++) { // pb const tutorial::Person::PhoneNumber& phone_number = person.phone(j); // pb switch (phone_number.type()) { // pb case tutorial::Person::MOBILE: // pb cout << " Mobile phone #: "; break; case tutorial::Person::HOME: // pb cout << " Home phone #: "; break; case tutorial::Person::WORK: // pb cout << " Work phone #: "; break; } cout << phone_number.number() << endl; // ob } } } // Main function: Reads the entire address book from a file and prints all // the information inside. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; // pb if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; return -1; } tutorial::AddressBook address_book; // pb { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!address_book.ParseFromIstream(&input)) { // pb cerr << "Failed to parse address book." << endl; return -1; } } ListPeople(address_book); // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); // pb return 0; } 扩展 Protocol Buffer或早或晚在你发布了使用 protocol buffer 的代码之后，毫无疑问，你会想要 "改善" protocol buffer 的定义。如果你想要新的 buffers 向后兼容，并且老的 buffers 向前兼容——几乎可以肯定你很渴望这个——这里有一些规则，你需要遵守。在新的 protocol buffer 版本：你绝不可以修改任何已存在字段的标签数字你绝不可以添加或删除任何 required 字段你可以删除 optional 或 repeated 字段你可以添加新的 optional 或 repeated 字段，但是你必须使用新的标签数字（也就是说，标签数字在 protocol buffer 中从未使用过，甚至不能是已删除字段的标签数字）。（对于上面规则有一些例外情况，但它们很少用到。）如果你能遵守这些规则，旧代码则可以欢快地读取新的消息，并且简单地忽略所有新的字段。对于旧代码来说，被删除的 optional 字段将会简单地赋予默认值，被删除的 repeated 字段会为空。新代码显然可以读取旧消息。然而，请记住新的 optional 字段不会呈现在旧消息中，因此你需要显式地使用 has_ 检查它们是否被设置或者在 .proto 文件在标签数字后使用 [default = value] 提供一个合理的默认值。如果一个 optional 元素没有指定默认值，它将会使用类型特定的默认值：对于字符串，默认值为空字符串；对于布尔值，默认值为 false；对于数字类型，默认类型为 0。注意，如果你添加一个新的 repeated 字段，新代码将无法辨别它被留空（被新代码）或者从没被设置（被旧代码），因为 repeated 字段没有 has_ 标志。优化技巧C++ Protocol Buffer 库已极度优化过了。但是，恰当的用法能够更多地提高性能。这里是一些技巧，可以帮你从库中挤压出最后一点速度：尽可能复用消息对象。即使它们被清除掉，消息也会尽量保存所有被分配来重用的内存。因此，如果我们正在处理许多相同类型或一系列相似结构的消息，一个好的办法是重用相同的消息对象，从而减少内存分配的负担。但是，随着时间的流逝，对象可能会膨胀变大，尤其是当你的消息尺寸（LCTT 译注：各消息内容不同，有些消息内容多一些，有些消息内容少一些）不同的时候，或者你偶尔创建了一个比平常大很多的消息的时候。你应该自己通过调用 SpaceUsed 方法监测消息对象的大小，并在它太大的时候删除它。对于在多线程中分配大量小对象的情况，你的操作系统内存分配器可能优化得不够好。你可以尝试使用 google 的 tcmalloc。高级用法Protocol Buffers 绝不仅用于简单的数据存取以及序列化。请阅读 C++ API reference 来看看你还能用它来做什么。protocol 消息类所提供的一个关键特性就是 反射 （ reflection ） 。你不需要针对一个特殊的消息类型编写代码，就可以遍历一个消息的字段并操作它们的值。一个使用反射的有用方法是 protocol 消息与其他编码互相转换，比如 XML 或 JSON。反射的一个更高级的用法可能就是可以找出两个相同类型的消息之间的区别，或者开发某种“协议消息的正则表达式”，利用正则表达式，你可以对某种消息内容进行匹配。只要你发挥你的想像力，就有可能将 Protocol Buffers 应用到一个更广泛的、你可能一开始就期望解决的问题范围上。反射是由 Message::Reflection interface 提供的。via: https://developers.google.com/protocol-buffers/docs/cpptutorial作者：Google 译者：cposture 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
C++，一个众所周知的 C 语言的扩展，是一个优秀的、强大的、通用编程语言，它能够提供现代化的、通用的编程功能，可以用于开发包括视频游戏、搜索引擎、其他计算机软件乃至操作系统等在内的各种大型应用。C++，提供高度可靠性的同时还能够允许操作底层内存来满足更高级的编程要求。虽然已经有了一些供程序员用来写 C/C++ 代码的文本编辑器，但 IDE 可以为轻松、完美的编程提供综合的环境和组件。在这篇文章里，我们会向你展示一些可以在 Linux 平台上找到的用于 C++ 或者其他编程语言编程的最好的 IDE。 1. 用于 C/C++ 开发的 NetbeansNetbeans 是一个自由而开源的、流行的跨平台 IDE ，可用于 C/C++ 以及其他编程语言，可以使用由社区开发的插件展现了其完全的扩展性。它包含了用于 C/C++ 开发的项目类型和模版，并且你可以使用静态和动态函数库来构建应用程序。此外，你可以利用现有的代码去创造你的工程，并且也可以通过拖放的方式导入二进制文件来从头构建应用。让我们来看看关于它的特性：C/C++ 编辑器很好的整合了多线程的 GNU GDB 调试工具支持代码协助支持 C++11 标准在里面创建和运行 C/C++ 测试程序支持 QT 工具包支持将已编译的应用程序自动打包到 .tar，.zip 等归档文件支持多个编译器，例如： GNU、Clang/LLVM、Cygwin、Oracle Solaris Studio 和 MinGW支持远程开发文件导航源代码检查主页：https://netbeans.org/features/cpp/index.html2. Code::BlocksCode::Blocks 是一个免费的、具有高度扩展性的、并且可以配置的跨平台 C++ IDE，它为用户提供了必备而典范的功能。它具有一致的界面和体验。最重要的是，你可以通过用户开发的插件扩展它的功能，一些插件是随同 Code::Blocks 发布的，而另外一些则不是，它们由 Code::Block 开发团队之外的个人用户所编写的。其功能分为编译器、调试器、界面功能，它们包括：支持多种编译器如 GCC、clang、Borland C++ 5.5、digital mars 等等非常快，不需要 makefile支持多个目标平台的项目支持将项目组合起来的工作空间GNU GDB 接口支持完整的断点功能，包括代码断点，数据断点，断点条件等等显示本地函数的符号和参数用户内存导出和语法高亮显示可自定义、可扩展的界面以及许多其他的的功能，包括那些用户开发的插件添加功能主页： http://www.codeblocks.org3. Eclipse CDT (C/C++ Development Tooling)Eclipse 在编程界是一款著名的、开源的、跨平台的 IDE。它给用户提供了一个很棒的界面，并支持拖拽功能以方便界面元素的布置。Eclipse CDT 是一个基于 Eclipse 主平台的项目，它提供了一个完整功能的 C/C++ IDE，并具有以下功能：支持项目创建管理各种工具链的构建标准的 make 构建源代码导航一些知识工具，如调用图、类型分级结构，内置浏览器，宏定义浏览器支持语法高亮的代码编辑器支持代码折叠和超链接导航代码重构与代码生成可视化调试存储器、寄存器的工具反汇编查看器以及更多功能主页： http://www.eclipse.org/cdt/4. CodeLite IDECodeLite 也是一款为 C/C++、JavaScript（Node.js）和 PHP 编程专门设计打造的自由而开源的、跨平台的 IDE。它的一些主要特点包括：代码补完，提供了两个代码补完引擎支持多种编译器，包括 GCC、clang/VC++以代码词汇的方式显示错误构建选项卡中的错误消息可点击支持下一代 LLDB 调试器支持 GDB支持重构代码导航使用内置的 SFTP 进行远程开发源代码控制插件开发基于 wxWidgets 应用的 RAD（快速应用程序开发）工具，以及更多的特性主页： http://codelite.org/5. Bluefish 编辑器Bluefish 不仅仅是一个一般的编辑器，它是一个轻量级的、快捷的编辑器，为程序员提供了如开发网站、编写脚本和软件代码的 IDE 特性。它支持多平台，可以在 Linux、Mac OSX、FreeBSD、OpenBSD、Solaris 和 Windows 上运行，同时支持包括 C/C++ 在内的众多编程语言。下面列出的是它众多功能的一部分：多文档界面支持递归打开文件，基于文件名通配模式或者内容模式提供一个非常强大的搜索和替换功能代码片段边栏支持整合个人的外部过滤器，可使用命令如 awk，sed，sort 以及自定义构建脚本组成（过滤器的）管道文件支持全屏编辑网站上传和下载支持多种编码等许多其他功能主页： http://bluefish.openoffice.nl6. Brackets 代码编辑器Brackets 是一个现代化风格的、开源的文本编辑器，专为 Web 设计与开发打造。它可以通过插件进行高度扩展，因此 C/C++ 程序员通过安装 C/C++/Objective-C 包来使用它来开发，这个包用来在辅助 C/C++ 代码编写的同时提供了 IDE 之类的特性。主页： http://brackets.io/7. Atom 代码编辑器Atom 也是一个现代化风格、开源的多平台文本编辑器，它能运行在 Linux、Windows 或是 Mac OS X 平台。它的定制可深入底层，用户可以自定义它，以便满足各种编写代码的需求。它功能完整，主要的功能包括：内置了包管理器智能的自动补完内置文件浏览器查找、替换以及其他更多的功能主页： https://atom.io/安装指南： http://www.tecmint.com/atom-text-and-source-code-editor-for-linux/8. Sublime Text 编辑器Sublime Text 是一个完善的、跨平台的文本编辑器，可用于代码、标记语言和一般文字。它可以用来编写 C/C++ 代码，并且提供了非常棒的用户界面。它的功能列表包括：多重选择按模式搜索命令抵达任何一处的功能免打扰模式窗口分割支持项目之间快速的切换高度可定制支持基于 Python 的 API 插件以及其他特性主页： https://www.sublimetext.com安装指南： http://www.tecmint.com/install-sublime-text-editor-in-linux/9. JetBrains CLionJetBrains CLion 是一个收费的、强大的跨平台 C/C++ IDE。它是一个完全整合的 C/C++ 程序开发环境，并提供 Cmake 项目模型、一个嵌入式终端窗口和一个主要以键盘操作的编码环境。它还提供了一个智能而现代化的编辑器，具有许多令人激动的功能，提供了理想的编码环境，这些功能包括：除了 C/C++ 还支持其他多种语言在符号声明和上下文中轻松导航代码生成和重构可定制的编辑器即时代码分析集成的代码调试器支持 Git、Subversion、Mercurial、CVS、Perforcevia（通过插件）和 TFS无缝集成了 Google 测试框架通过 Vim 仿真插件支持 Vim 编辑体验主页： https://www.jetbrains.com/clion/10. 微软的 Visual Studio Code 编辑器Visual Studio 是一个功能丰富的、完全整合的、跨平台开发环境，运行在 Linux、Windows 和 Mac OS X 上。 最近它向 Linux 用户开源了，它重新定义了代码编辑这件事，为用户提供了在 Windows、Android、iOS 和 Web 等多个平台开发不同应用所需的一切工具。它功能完备，功能分类为应用程序开发、应用生命周期管理、扩展和集成特性。你可以从 Visual Studio 官网阅读全面的功能列表。主页： https://www.visualstudio.com11. KDevelopKDevelop 是另一个自由而开源的跨平台 IDE，能够运行在 Linux、Solaris、FreeBSD、Windows、Mac OS X 和其他类 Unix 操作系统上。它基于 KDevPlatform、KDE 和 Qt 库。KDevelop 可以通过插件高度扩展，功能丰富且具有以下显著特色：支持基于 Clang 的 C/C++ 插件支持 KDE 4 配置迁移支持调用二进制编辑器 Oketa支持众多视图插件下的差异行编辑支持 Grep 视图，使用窗口小部件节省垂直空间等主页： https://www.kdevelop.org12. Geany IDEGeany 是一个免费的、快速的、轻量级跨平台 IDE，只需要很少的依赖包就可以工作，独立于流行的 Linux 桌面环境下，比如 GNOME 和 KDE。它需要 GTK2 库实现功能。它的特性包括以下列出的内容：支持语法高亮显示代码折叠调用提示符号名自动补完符号列表代码导航一个简单的项目管理工具可以编译并运行用户代码的内置系统可以通过插件扩展主页： http://www.geany.org/13. Ajunta DeveStudioAjunta DevStudio 是一个简单，强大的 GNOME 界面的软件开发工作室，支持包括 C/C++ 在内的几种编程语言。它提供了先进的编程工具，比如项目管理、GUI 设计、交互式调试器、应用程序向导、源代码编辑器、版本控制等。此外，除了以上特点，Ajunta DeveStudio 也有其他很多不错的 IDE 功能，包括：简单的用户界面可通过插件扩展整合了 Glade 用于所见即所得的 UI 开发项目向导和模板整合了 GDB 调试器内置文件管理器使用 DevHelp 提供上下文敏感的编程辅助源代码编辑器支持语法高亮显示、智能缩进、自动缩进、代码折叠/隐藏、文本缩放等主页： http://anjuta.org/14. GNAT Programming StudioGNAT Programming Studio 是一个免费的、易于使用的 IDE，设计的目的用于统一开发人员与他/她的代码和软件之间的交互。它通过高亮程序的重要部分和逻辑从而提升源代码导航体验，打造了一个理想的编程环境。它的设计目标是为你带来更舒适的编程体验，使用户能够从头开始开发全面的系统。它丰富的特性包括以下这些：直观的用户界面对开发者的友好性支持多种编程语言，跨平台灵活的 MDI（多文档界面）高度可定制使用喜欢的工具获得全面的可扩展性主页： http://libre.adacore.com/tools/gps/15. Qt Creator这是一款收费的、跨平台的 IDE，用于创建连接设备、用户界面和应用程序。Qt Creator 可以让用户比应用的编码做到更多的创新。它可以用来创建移动和桌面应用程序，也可以连接到嵌入式设备。它的优点包含以下几点：复杂的代码编辑器支持版本控制项目和构建管理工具支持多屏幕和多平台，易于构建目标之间的切换等等主页： https://www.qt.io/ide/16. Emacs 编辑器Emacs 是一个自由的、强大的、可高度扩展的、可定制的、跨平台文本编辑器，你可以在 Linux、Solaris、FreeBSD、NetBSD、OpenBSD、Windows 和 Mac OS X 这些系统中使用该编辑器。Emacs 的核心也是一个 Emacs Lisp 的解释器，Emacs Lisp 是一种基于 Lisp 的编程语言。在撰写本文时，GNU Emacs 的最新版本是 24.5，Emacs 的基本功能包括：内容识别编辑模式Unicode 的完全支持可使用 GUI 或 Emacs Lisp 代码高度定制下载和安装扩展的打包系统超出了正常文本编辑的功能生态系统，包括项目策划、邮件、日历和新闻阅读器等完整的内置文档，以及用户指南等等主页： https://www.gnu.org/software/emacs/17. VI/VIM 编辑器Vim，一款 VI 编辑器的改进版本，是一款自由的、强大的、流行的并且高度可配置的文本编辑器。它为有效率地文本编辑而生，并且为 Unix/Linux 使用者提供了激动人心的编辑器特性，因此，它对于撰写和编辑 C/C++ 代码也是一个好的选择。总的来说，与传统的文本编辑器相比，IDE 为编程提供了更多的便利，因此使用它们是一个很好的选择。它们带有激动人心的特征并且提供了一个综合性的开发环境，有时候程序员不得不陷入对最好的 C/C++ IDE 的选择。在互联网上你还可以找到许多 IDE 来下载，但不妨试试我们推荐的这几款，可以帮助你尽快找到哪一款是你需要的。via: http://www.tecmint.com/best-linux-ide-editors-source-code-editors/作者：Aaron Kili 译者：ZenMoore ，LiBrad ，WangYueScream ，LemonDemo 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出