一、简介
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还可以带来一些比较方便的使用技巧。