实用工具库(上)-Boost.optional

一起学习Boost标准库

Posted by iceman on May 16, 2018

在Boost库中提供了许多非常有用的小工具类库。有大有小,主要就是解决实际实用过程中提高生产力的作用,提高开发效率

由于提供的工具库较多,该篇分为两个上、下两次推送。本文主要和大家一起学习如下三个库:

  • Boost.noncopyable 轻松实现禁止拷贝类
  • Boost.ignone_unused 忽略编译器对暂时用不到又必须保留的变量的报警(‘未引用的局部变量’)
  • Boost.optional 可选型变量

Boost.noncopyable库

前期准备

noncopyable库位于boost命名空间下,为了使用该库需要引入头文件<boost/noncopyable.hpp>同时该库也收录在<boost/utility.hpp>头文件中,该文件包含了多个小工具的实现

#include <boost/noncopyable.hpp>
//or #include <boost/utility.hpp>
using namespace std;

类声明

noncopyable库的实现很简单,代码很少

class noncopyable
  {
  protected:
      noncopyable() {}
      ~noncopyable() {}
  private:  
      noncopyable( const noncopyable& );
      noncopyable& operator=( const noncopyable& );
  };

当我们自己定义一个子类的时,如果不定义构造函数,编译器就会自动为我们生成默认构造函数,比如我们实现类

class my_class
{
public:
	int a_;    
};

my_class my_;

此时,编译器为我们默认生成了四个函数

my_class();	//构造函数
~my_class();	//析构函数
my_class(const my_class&);	// 拷贝构造函数
my_class& operator=(const my_class&) //拷贝赋值函数

这样做的好处是,类可以进行定义、拷贝、对象间赋值、放入标准容器。但是有些时候其实我们并不需要对象被拷贝,此时我们就需要将拷贝构造函数和拷贝赋值构造函数设置为private 这样外面就不可用调用了。也就起到了禁止拷贝实例了

class my_class
{
public:
	int a_;
private:
	my_class(const my_class&);
	my_class& operator=(const my_class&);
};

如果中大量使用这样的类,就会不断的重复实现这样的代码,大家都知道,程序员是比较懒的物种,所以就慢慢的抽象出来,整个辅助类,不用重复编码

使用

class my_class : boost::noncopyable
{
public:
	int a_;
};

my_class my1;		//实例化对象
my_class my2(my1);	//拷贝构造对象 失败!
my_class my3;		//实例化对象
my3 = my1;			//拷贝赋值 失败!

当我们定义my_class 时,会自动继承noncopyable的拷贝构造函数,从而禁止用户从外部访问拷贝构造函数和拷贝赋值函数。因为对象的拷贝主要是通过调用者两个函数进行成员赋值,通过私有化来禁止拷贝。

在vs编译器上,编译代码会收到error C2280 错误。

Boost.ignore_unused库

编写代码的时,会有些暂时用不到的但是又必须保留,因为后面可能需要用到,就需要把这个‘坑’占着,但是编译器一般都会发出警告,比如在vs编译器上回收到到这样的 “Warning C4101: “my1”: 未引用的局部变量“ ,虽然也能通过一些其他方法屏蔽掉(就是不管咋的,假装用一用:(void)var) ,但是这就不方便维护,更重要的的是还容易让人误解

Boost库基于这个需求通过过组件ignore_unused完美解决了它

前期准备

ignore_unused库包含在boost命名空间下,为了使用它,需包含头文件ignore_unused.hpp头文件

#include <boost/core/ignore_unused.hpp>
using namespace boost;

类声明

ignore_unused的实现非常地简单,几乎是什么都没做

template <typename... Ts>
inline void ignore_unused(Ts const& ...)
{}

template <typename... Ts>
inline void ignore_unused()
{}

通过使用可变参数,支持任意数量、任意类型的变量,让他们作为函数的参数,骗了下编译器。作为内联函数,没有运行时的效率损失。

用法

我们通过定义一个函数进行说明

void test_ignore_unused(int a_, int b_)
{
	TEST_FUNCTION_NAME;
	int x, y;
	//boost::ignore_unused(x,y);
}

在vs中会收到如下警告

ignore_unsued

Boost.optional库

在实际开发总通常会遇到这种情况,函数并不能总是返回一个有效值,有可能是输入参数不符合规范,我们需要输出一个值来提示用户,该次请求违规了,存在一些问题,请按照正确方法调用。通常的做法就是通过返回一个约定的值来表示出现了问题,比如:

  • -1 :这个值绝对是很多函数喜欢返回的一个用于说明该次调用存在问题的一个标志
  • EOF :一般文件访问通过EOF来说明操作到了文件尾部
  • string::npos :字符串为查找到
  • vector::end :容器最后一个元素的下一个位置,说明到此处已经对元素访问完毕了

同时也存在通过返回一个pair 对象来返回该次操作是否有效

pair<T, bool>

optional通过包装可能产生无效值的对象,实现未初始化的概念,为上文提到的无效值提供了一个解决方案

前期准备

optinal位于boost命名空间,如使用它需要包含<boost/optional.hpp>头文件

#include <boost/optional.hpp>
using namespace std;

类声明

template< class T>
class optional
{
public:
	optional();
	optional(none_t);
	optional(T const& v);
	optional(bool condition, T v);

	optional& operator= (T const& rhs);
	template<typename... Args>
	void emplace(Args...& args);

	T* operator ->();
	T& operator *();

	T& get();
	T* get_ptr();
	T& value();
	T const& value_or(T const& default)const;
	template<typename F>
	T value_or_eval(F f)const;
    
	explicit operator bool() const;
	bool operator !() const;
};

用法

以下通过一个简单的函数来一步一步说明使用optional库

int get_value(const string& key)
{
	std::map<string, int> kv{
		{ "one", 1 },
		{ "two", 2 },
		{ "three", 3 },
		{ "four", 4 }
	};

	return kv.count(key) > 0 ? kv[key] : -1;
}

int main()
{
 	//int ret = get_value("one");
 	int ret = get_value("five");
    if(ret != -1)
        cout<< "find value: " << ret <<endl;
    return 0;
}

该样例中,-1 意味着没有找到匹配值,此时返回值就是“无效的”,我们通过optional来改写下程序

boost::optional<int> get_value(const string& key)
{
	std::map<string, int> kv{
		{ "one", 1 },
		{ "two", 2 },
		{ "three", 3 },
		{ "four", 4 }
	};

	return kv.count(key) > 0 ? kv[key] : boost::optional<int>{};
}

int main()
{
 	//optional<int> ret = get_value("one");
 	optional<int> ret = get_value("five");
    if(ret)
        cout<< "find value: " << *ret <<endl;
    return 0;
}

当方法get_value查询到值时,就自动构建对象*optional* 直接返回;当为查询到就调用默认构造函数返回一个缺省值。调用者通过判断返回值是否为空,来确认调用是否有效。如果不为空,通过**operator*** 操作符进行访问,optional封装返回值像指针一样

同时optional提供了一些有用的方法

boost::optional<int> get_value2(const string& key)
{
	std::map<string, int> kv{
		{ "one", 1 },
		{ "two", 2 },
		{ "three", 3 },
		{ "four", 4 }
	};

	return boost::optional<int>{kv.count(key) > 0 , kv[key]};
}

int main()
{
 	//optional<int> ret = get_value("one");
 	optional<int> ret = get_value("five");
    if(ret.is_initialized())
        cout<< "find value: " << ret.get() <<endl;
    return 0;
}

因为optional提供了一个特殊的构造函数,第一个参数通过设置一个条件值,来说明第二个参数是否初始化。可通过成员函数is_initialized() 来检看返回值是否为空。同时成员方法get()operator* 等效。

optional (bool condition, T v);

optional提提供了一些辅助方法。通过make_optional() 创建对象,通过方法get_optional_value_or() 获取返回值,该方法提供一个参数用于设定一个默认值,当返回为空时

optional<T> make_optional(T const& v);
optional<T> make_optional(bool condition, T const& v);
optional<T>::reference_type get ( optional<T>& opt );
optional<T>::reference_const_type
get_optional_value_or ( optional<T> const& opt,  optional<T>::reference_const_type v );

此时示例可以改写如下:

boost::optional<int> get_value2(const string& key)
{
	std::map<string, int> kv{
		{ "one", 1 },
		{ "two", 2 },
		{ "three", 3 },
		{ "four", 4 }
	};

	return make_optional(kv.count(key) > 0, kv[key]);
}

int main()
{
 	//optional<int> ret = get_value("one");
 	optional<int> ret = get_value("five");
    cout<< "find value: " << get_optinal_value_or(ret, -1) <<endl; // find value: -1
    return 0;
}

get_optional_value_or 方法内部通过调用optional::get_value_or()返回。