C++, RAII, and the GSL Refresher

《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;
}

上面的代码体现了这种机制,它的好处是:

  1. 只要myclass的实例存在,其指针就有效,对这个指针的使用是安全的。
  2. 不会发生内存泄漏。
    类std::unique_ptr和std::shared_ptr同样利用了这种模式,而且提供了更完善的保护。

The Guideline Support Library(GSL)

GSL是一个用来帮助在使用C++开发时实现最优实践的库,主要包括:

  • 明确指针的所有者
  • 异常处理
  • 避免指针运算

目前发现这部分的代码无法编译,提示找不到<gsl/gsl>库。后面实际用到时搭好了环境再补充吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值