《Hands-On System Programming with C++》读书笔记之四
A Brief Overview of C++17
Language Changes
if/switch语句中的变量初始化
下面的代码需要使用g++ -std=c++17 scratchpad.cpp编译。
#include <iostream>
int main(void)
{
if (auto i = 42; i > 0)
std::cout << "Hello World" << std::endl;
}
这种定义形式的好处是,只有当变量满足条件时,它才会存在。
同样适用于switch判断的例子。
#include <iostream>
int main(void)
{
switch(auto i = 42)
{
case 42:
std::cout << "Hello World" << std::endl;
break;
default:
break;
}
return 0;
}
新的编译时处理
if constexpr告诉编译器可以在编译时就选择好执行的分支,直接优化掉运行时的判断过程。
#include <iostream>
int main(void)
{
if constexpr (constexpr const auto i = 42; i > 0)
std::cout << "Hello World" << std::endl;
return 0;
}
查看这段代码的编译结果会发现分支指令已经不存在了。如果无法在编译时去掉分支,编译器会直接报错,这与C++11中的consexpr只是尝试去掉分支不同。
新的static_assert()函数不再要求提供描述文字。不过我在实验时发现两种都正常编译,不管是否加std=c++17参数。
#include <iostream>
int main(void)
{
static_assert(42 == 42); // C++17
static_assert(42 == 42, "Description"); // C++11
return 0;
}
Namespace
嵌套的命名空间可以在一行里定义:
// C++11
namespace X
{
namespace Y
{
namespace Z
{
int a = 0;
}
}
}
// C++17
namespace X::Y::Z
{
int a = 0;
}
Structured Bindings
Structure bindings为我们提供了一种提取结构体或std::pair中变量的很方便的方法。
#include <iostream>
struct mystruct
{
const char *msg;
int answer;
};
mystruct give_me_a_struct()
{
return {"The answer is: ", 42};
}
int main(void)
{
auto [msg, answer] = give_me_a_struct();
std::cout << msg << answer << std::endl;
return 0;
}
Inline variables
原本定义一个全局变量时采用的是在某个代码文件中定义,再在一个头文件中声明此外部变量,然后其他源文件引用此头文件。这个过程比较复杂,也不清楚此全局变量到底在哪里定义的。C++17通过引入Inline variables,在头文件中直接定义全局变量,使这一过程变得更加清晰。
#include <iostream>
inline auto msg = "Hello World\n";
int main()
{
std::cout << msg;
}
Changes in the library
String View
随着程序的复杂,直接对指针和数组的使用容易造成错误。一直有人推动在C++程序中减少对它们的直接使用。std::string_view类就是C++17在字符数组基础上开发的一个类,使对字符串的使用更加安全和方便。
#include <iostream>
#include <string_view>
using namespace std;
int main(void)
{
string_view str("Hello World");
cout << str.front() << endl; // H
cout << str.back() << endl; // d
cout << str.at(1) << endl; // e
cout << str.data() << endl; // Hello World
cout << str.size() << endl; // 11
cout << str.max_size() << endl; // 4611686018427387899
cout << str.empty() << endl; // 0
cout << str.substr(0, 5) << endl; // Hello
if (str.compare("Hello World") == 0)
cout << "The same" << endl; // The same
cout << str.compare("Hello") << endl; // 6
cout << str.compare("World") << endl; // -1
cout << str.find("ll") << endl; // 2
str.remove_prefix(1);
str.remove_suffix(1);
cout << str << endl; // ello Worl
}
std::any, std::Variant, and std::optional
- std::any可以用来存储任意类型的数据。
#include <iostream>
#include <any>
using namespace std;
struct mystruct
{
int data;
};
int main(void)
{
auto myany = make_any<int>(42);
cout << any_cast<int>(myany) << endl; // 42
myany = 4.2;
cout << any_cast<double>(myany) << endl; // 4.2
myany = mystruct{42};
cout << any_cast<mystruct>(myany).data << endl; // 42
return 0;
}
- std::variant is more like a type-safe union. 和传统的C语言union不同,它不允许用错误的类型去访问其中的数据时。
#include <iostream>
#include <variant>
using namespace std;
int main(void)
{
variant<int, double> v = 42;
cout << get<int>(v) << endl; // 42
v = 4.2;
cout << get<double>(v) << endl; // 4.2
return 0;
}
- std::optional是一种可以为空的数据类型,只有当它赋予具体值时它的构造函数才执行,内存空间才被分配。这里不太理解它和空指针有什么区别呢?
#include <iostream>
#include <optional>
class MyClass
{
public:
int val;
MyClass(int v): val{v}
{
std::cout << "constructed\n";
}
};
int main(void)
{
std::optional<MyClass> opt;
std::cout << "created, but not constructed\n";
if (opt)
{
std::cout << "Attempt #1: " << opt->val << std::endl; // Nothing
}
opt = MyClass{42}; // constructed
if (opt)
{
std::cout << "Attempt #2: " << opt->val << std::endl; // 42
}
}
Resource Acquisition Is Initialization(RAII)
RAII机制的含义:资源的占用伴随着一个对象的构造,这个对象的析构意味着对此资源的释放。这可以利用C++的构造函数和析构函数实现。
#include <iostream>
class myclass
{
int* ptr;
public:
myclass(): ptr{new int(42)} {}
~myclass()
{
delete ptr;
}
int get()
{
return *ptr;
}
};
int main(void)
{
myclass c;
std::cout << "The answer is: " << c.get() << '\n'; // 42
return 0;
}
上面的代码体现了这种机制,它的好处是:
- 只要myclass的实例存在,其指针就有效,对这个指针的使用是安全的。
- 不会发生内存泄漏。
类std::unique_ptr和std::shared_ptr同样利用了这种模式,而且提供了更完善的保护。
The Guideline Support Library(GSL)
GSL是一个用来帮助在使用C++开发时实现最优实践的库,主要包括:
- 明确指针的所有者
- 异常处理
- 避免指针运算
目前发现这部分的代码无法编译,提示找不到<gsl/gsl>库。后面实际用到时搭好了环境再补充吧。