您的位置:

深入剖析boost::optional

一、简介

boost::optional是boost库中的一个模板类,它是一个封装了一个可选对象(option value)的类。可选对象是一种可能存在,也可能不存在的值。

在实际编程中,我们常常会遇到需要对变量判定是否有值,如果有值才进行下一步操作的情况,这时候,我们常常会选择使用指针类型并检查是否为NULL或者采用某个特定值来表示缺少值的情况。这种处理方式相对非常繁琐,代码中充斥了大量的if-else语句,代码可读性大打折扣。而boost::optional则可以很方便地解决这个问题。

#include <boost/optional.hpp>
#include <iostream>

using namespace std;
using boost::optional;

int main()
{
    optional<int> i;
    cout << boolalpha << i.is_initialized() << endl; //输出false
    i = 5;
    if(i)
    {
        cout << *i << endl; //输出5
    }
    else
    {
        cout << "no value" << endl;
    }
    cout << i.value_or(0) << endl; //输出5,如果i没有值,则输出0
    return 0;
}

二、优点

相比于使用指针或者某个特殊的值来表示缺少值的方法,使用boost::optional有以下优点:

1. 自然易读

和用指针或者特殊值的方式相比,boost::optional的语义更加清晰明了,代码更容易阅读和理解。这是因为我们可以直接使用"."和"->"访问optional对象内部的值,而不必在代码中添加大量的if-else分支来判断对象的存在状态。

//使用指针
int *p;
if(p == NULL)
{
    cout << "no value" << endl;
}
else
{
    cout << *p << endl;
}

//使用特殊值
int value = -1;
if(value == -1)
{
    cout << "no value" << endl;
}
else
{
    cout << value << endl;
}

//boost::optional
optional<int> i;
if(i)
{
    cout << *i << endl;
}
else
{
    cout << "no value" << endl;
}

2. 更加安全

和使用指针的方式相比,boost::optional更加安全。当我们使用指针访问一个已经被释放的对象时,程序会发生内存错误。而如果我们使用boost::optional,只有当确实存在值的时候才会去访问其中的对象,这样就不会出现访问已经被释放的对象的情况。

//使用指针
int *p = new int(5);
delete p;
cout << *p << endl; //程序会崩溃

//boost::optional
optional<int> i(5);
i.reset();
cout << *i << endl; //不会访问,程序不会崩溃

三、API介绍

1. 构造函数

初始化boost::optional对象,一般有以下几种方式:

1. 使用默认构造函数

optional<int> i; //i没有值

2. 使用值(T类型的实例化对象)初始化

optional<int> i(5); //i有一个值为5

3. 使用boost::none初始化

optional<int> i(boost::none); //i没有值

2. 拷贝构造函数

boost::optional对象支持拷贝构造和拷贝赋值,具体实现和普通类型的对象一样。

optional<int> i(5);
optional<int> j(i); //j也有一个值为5

3. 重载运算符

boost::optional支持以下重载运算符:

1. bool类型重载:

可以通过使用bool()来判断是否有值。如果有值,则返回true,否则返回false。

optional<int> i;
if(i)
{
    //i有值
}
else
{
    //i没有值
}

if(bool(i))
{
    //i有值
}
else
{
    //i没有值
}

2. 相等(==)和不相等(!=)运算符重载:

可以将boost::optional对象与T类型(对象或值)进行比较。如果optional对象有值,且该值与T对象相等,则返回true;否则返回false。

optional<int> i(5);
int j = 5;
if(i == j)
{
    //i和j的值相等
}
else
{
    //i和j的值不相等
}

int k = 6;
if(i != k)
{
    //i和k的值不相等
}
else
{
    //i和k的值相等
}

3. 指针重载:

我们可以使用"->"来访问optional对象的值。如果optional对象没有值,则会抛出一个boost::bad_optional_access异常。

optional<string> p("hello world");
cout << p->size() << endl;

optional<string> q;
cout << q->size() << endl; //会抛出异常

4. 解引用(*)运算符重载:

可以使用"*"来访问optional对象内部存储的值。如果optional对象没有值,则会抛出一个boost::bad_optional_access异常。

optional<string> p("hello world");
cout << *p << endl;

optional<string> q;
cout << *q << endl; //会抛出异常

4. 函数成员

boost::optional提供了一系列函数成员来方便我们使用可选对象:

1. is_initialized():

用于检查对象是否被初始化,如果已经被初始化,则返回true,否则返回false。

optional<int> i;
cout << boolalpha << i.is_initialized() << endl; //false

i = 5;
cout << boolalpha << i.is_initialized() << endl; //true

2. reset():

用于清除对象的值(不同于将它赋值为一个没有值的optional对象)

optional<int> i(5);
i.reset(); //现在i没有值

3. value():

用于访问optional对象的值。如果对象不包含值,则会抛出一个boost::bad_optional_access异常。

optional<int> i(5);
cout << i.value() << endl; //输出5

optional<int> j;
cout << j.value() << endl; //会抛出异常

4. value_or():

用于返回optional对象的值,如果对象没有值,则返回一个默认值。

optional<int> i;
cout << i.value_or(0) << endl; //输出0,因为i没有值

i = 5;
cout << i.value_or(0) << endl; //输出5

四、使用技巧

1. 使用optional作为函数返回类型:

我们可以使用boost::optional作为函数的返回类型,这样就可以方便地处理函数返回值可能为空的情况。如果函数的调用方需要获取函数返回值,则可以使用optional对象的value()函数访问其内部存储的值。如果函数没有返回有效值,则可通过抛出异常的方式来通知调用方。如下面所示:

optional<int> getInt()
{
    int a = 5;
    bool exist = false; //假设a不一定有值

    if(exist)
    {
        return a;
    }
    else
    {
        return boost::none;
    }
}

int main()
{
    optional<int> i = getInt();
    if(i)
    {
        cout << i.value() << endl;
    }
    else
    {
        cout << "no value" << endl;
    }

    return 0;
}

2. 使用lambda表达式:

使用lambda表达式可以方便地处理optional对象的值。lambda表达式可以将我们的逻辑代码和optional对象的判断分支合并在一起,让代码更加简洁易读。如下面所示:

optional<int> i(5);
optional<int> j;
auto f = [](optional<int> a, optional<int> b)
{
    if(!a && !b)
    {
        cout << "no value" << endl;
    }
    else if(a && !b)
    {
        cout << *a << endl;
    }
    else if(!a && b)
    {
        cout << *b << endl;
    }
    else
    {
        cout << *a + *b << endl;
    }
};

f(i, j);  //输出5
f(j, i);  //输出5
f(i, optional<int>()); //输出5
f(optional<int>(), j); //输出no value
f(i, optional<int>(10)); //输出15

总结

boost::optional提供了一种简单、直观的方法来处理可选对象。与使用指针或者某个特殊值的方式相比,boost::optional具有更好的代码可读性和安全性。此外,使用boost::optional还可以带来一些比较方便的使用技巧。