Boost开发指南-4.4assign

news2024/12/28 2:05:34

assign

许多情况下我们都需要为容器初始化或者赋值,填入大量的数据,比如初始错误代码和错误信息,或者是一些测试用的数据。在C++98中标准容器仅提供了容纳这些数据的方法,但填充的步骤却是相当地麻烦,必须重复调用insert()或者push_back()等成员函数,这正是boost.assign出现的理由。

assign库重载了赋值操作符operator+=、逗号操作符operator,和括号操作符operator(),可以用难以想象的简洁语法非常方便地对标准容器赋值或者初始化。在需要填入大量初值的地方很有用。C++11标准也提供类似功能的初始化列表,但功能没有assign库那么完备。

assign库位于名字空间 boost::assign,为了使用assign库,需要包含头文件<boost/assign.hpp>,它包含了大部分assign库的工具,即:

#include <boost/assign.hpp>
using namespace boost::tassign;

list_inserter

list_inserter是assign库中用来操作容器的工具类,它类似std::back_inserter,但增加了很多操作符重载和助手类来简化代码。

template< class Eunction >class list_inserter
{
public:
   list_inserter& operator, (const T& r); //重载operator,
   list_inserter& operator()(); //重载operator()
   list_inserter& operator()(const T& r);
public:  //重复输入数据操作
   list_inserter& repeat(std::size_t sz, T r );
   list_inserter& repeat_fun(std::size_t sz, Nullary_function fun);
   list_inserter& range(SinglePassIterator first, SinglePassIterator last);
   list_inserter& range(const singlePassRange& r);
private:
   Function insert_;
};

list_inserter内部存储一个函数对象insert_用来操作容器,这个函数对象包装了容器的push_back、push_front等操作,例如:

list_inserter& operator,(const T& r) //重载operator,
{
     insert_(r); //向容器添加元素
     return *this; //返回自身的引用
}

list_inserter成员函数的另一个特点是返回*this,这使得它可以像标准流操作一样串联操作,达到简化代码的目的。

list_inserter还提供repeat/range函数来简化输入重复数据的工作。

使用operator+=

使用assign库时必须使用using 指示符,只有这样才能让重载的+=,等操作符在作用域内生效。例如:

#include <boost/assign.hpp>
int main()
{
	using namespace boost::assign; //很重要,启用assign库的功能
	vector<int> v; //标准向量容器
	v += 1, 2, 3, 4, 5, 6 * 6; //用operator+=和,填入数据

	set<string> s; //标准集合容器
	s += "c", "cpp", "lua", "swift";

	map<int, string> m; //标准映射容器
	m += make_pair(1, "one"), make_pair(2, "two");
}

上面的代码示范了assign库操作标准容器的能力。+=操作符后可以接若干个可被容器容纳的元素,元素之间使用逗号分隔。元素不一定是常量,表达式或者函数调用也是可以接受的,只要其结果能够转换成容器可容纳的类型。比较特别的是 map容器,必须使用辅助函数make_pair()来生成容器元素,单纯地用括号把pair的两个成员括起来是无效的。

assign 库提供三个工厂函数push_back()、push_front()和insert()。这些函数可作用于拥有同名成员函数的容器,接受容器实例作为参数,创建对应的 list_inserter对象。
在名字空间 boost::assign里 assign库为标准容器重载了operator+=,调用push_back()或insert()函数,形式是:

inline list_inserter operator+=(C& c, V v)
{
     return push_back(c)(v); //对于关联容器则是insert函数
}

operator+=作用于容器时调用工厂函数push_back(),产生一个list_inserter对象,以这个对象作为起点,随后的operator()和operator,就会依次执行,使用push_back()或insert()向容器插入数据。由于list_inserter重载了很少使用的逗号操作符,所以函数的调用得到了极大的简化。

operator+=很好用,但有一点遗憾,assign库仅提供了对C++98标准容器(vector、list、set等)的重载,要操作其他类型的容器(如Boost容器)只能依据其原理自行实现。

使用operator()

operator+=仅作用于标准容器,而且在处理map容器时也显得有些麻烦,所以我们可以直接使用工厂函数insert() /push_front()/push_back(),直接利用他们返回的list_inserter对象来填入数据。

int main()
{
	using namespace boost::assign;

	vector<int> v; 
	push_back(v)(1)(2)(3)(4)(5); //使用puch_back工厂函数

	list<string> l;
	push_front(l)("c")("cpp")("lua")("swift"); //使用push_front工厂函数

	forward_list<string> fl; //C++11的单向链表
	push_front(l)("matrix")("reload"); //使用push__front工厂函数

	set<double> s;
	insert(s)(3.14)(0.618)(1.732); //使用insert工厂函数

	map<int, string> m;
	insert(m)(1, "one")(2, "two"); //使用insert工厂函数
}

这段代码与使用operator+=没有太多的不同。对于拥有push_back()或push_front ()成员函数的容器(vector、list、deque),可以调用assign::push_back()或assign::push_front(),而对于set和map,则只能使用assign::insert()。

operator()的好处是可以在括号中使用多个参数,这对于 map这样的元素是由多个值组成的类型非常方便,避免了make_pair()函数的使用。而且如果括号中没有参数,那么list_inserter 将调用容器元素的缺省构造函数填入一个缺省值,逗号操作符则不能这样做。

括号操作符也可以与逗号等操作符配合使用,写法更简单,有时甚至看起来不像是合法的C++代码(但的确是完全正确的C++代码),例如:

using namespace boost::assign;

vector<int> v;
push_back(v), 1, 2, 3, 4, 5;
push_back(v)(6), 7, 64 / 8, (9), 10; //v=[1 2 3 4 5 6 7 8 9 10]

for (auto& x : v)
	cout << x << ",";
cout << endl;

deque<string> d;
push_front(d)() = "breath", "of", "the", "wild";
assert(d.size() == 5); //d=['wild', 'the', 'of', 'breath', '']

generic_list

list_inserter解决了对容器的赋值问题,但有的时候我们需要在容器构造的时候就完成数据的填充,这种方式较赋值更为高效。

C++11标准引入了初始化列表std::initializer_list,而 boost.assign库则提供功能类似的generic_list,它的类摘要如下:

template<class T>
class generic_list
{
public:
  iterator begin() const; //类似容器的接口
  iterator end() const;
  bool empty() const;
  size_type size() const;
  generic_list& operator,(const Ty& u); //重载operator,
  generic_list&operator()(); //重载operator()
  generic_list&operator()(const Ty& u);
public: //重复输入数据操作
  generic_list& repeat(std::size_t sz, U u);
  generic_list& repeat_fun(std::size_t sz, Nullary_function fun);
  generic_list& range(singlePassIterator first, singlePassIterator last);
  generic_list& range(const singlePassRange& r);
public: //容器转换
  operator container(const;
  Container to_container(Container& c) const;
  adapter_converter to_adapter() const;
  Adapter to_adapter(Adapter& a) const;
  Array to_array(Array& a) const;
};

与list_inserter类似,generic_list也重载了逗号和括号操作符,因为要在初始化时与容器互操作,它还增加了一些容器操作函数。

generic_list内部使用std::deque 存储元素,大多数操作都转换为 deque 的push_back(),例如:

generic_list& operator,(const Ty& u) //重载operator,
{
     this->push_back(u); //push_back添加元素
     return *this; //返回自身的引用
}

初始化容器

assign库提供三个工厂函数list_of()、map_list_of()/pair_list_of()和tuple_list_of(),它们能够产生 generic_list 对象,然后我们就可以像list_inserter一样使用operator()和 operator,来填充数据。

因为generic_list提供到容器类型的隐式转型操作,所以它可以赋值给任意容器,当然我们也可以显式调用容器转换函数。

list_of()函数的声明是:

inline generic_list<T> list_of();
inline generic_list<T> list_of(const T&t);

它的用法与之前的insert()、push_back()等函数相似:

int main()
{
	using namespace boost::assign;

	vector<int> v = list_of(1)(2)(3)(4)(5);
	// v = [1, 2, 3, 4, 5]

	deque<string> d =
		(list_of("power")("bomb"), "phazon", "suit"); //注意括号和逗号的使用
	// d = [power bomb phazon suit]

	set<int> s = (list_of(10), 20, 30, 40, 50); //注意括号和逗号的使用
	// s = {10 20 30 40 50}

	map<int, string> m = list_of(make_pair(1, "one"))(make_pair(2, "two"));
	// m = [(1, “one”) (2, “two”)]
}

list_of()函数可以全部使用括号操作符,也可以把括号与逗号结合起来,但使用后者时需要将整个list_of表达式用括号括起来,否则会使编译器无法推导出list_of的类型而无法赋值。

上面的代码对应的C++11标准代码是

vector<int> v = {1,2,3,4,5};
deque<string> d = {"power", "bomb", "phazon", "suit"};
set<int> s = {10, 20, 30, 40, 50};
map<int, string> m = {{1, "one"},{2, "two"}};

两段代码比较起来,虽然assign库的略显麻烦,但胜在对C++98和C++11的兼容性。

使用list_of()处理map容器不是很方便,于是map_list_of()/pair_list_of()应运而生,map_list_of()可以接受两个参数,然后自动构造std::pair对象插入map 容器,pair_list_of()则纯粹是map_list_of的同义词,两者的用法功能完全相同。

map_list_of()和pair_list_of()的基本形式如下:

template<class Key, class T>
map_lists_of(const Key& k, const T& t); //key, value

template<class F, class S>
pair_list_of(const F&f, const S&s)
{
    return map_list_of(f, s);
}

下面的代码演示了map_list_of的用法:

map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6);
//m1 = [(1, 2)(3, 4)(5, 6)]

map<int, string> m2 = map_list_of(1, "one")(2, "two");
//m2 = [(1, "one")(2, "two")]

减少重复输入

在填充数据时会遇到输入重复数据的问题,如果用之前的方法要写大量的重复代码,很麻烦,也容易造成多写或少写的错误。list_inserter 和 generic_list 都提供成员函数repeat()、repeat_fun()和range()来减轻工作量。

这三个函数的简要声明如下:

list& repeat(std::size_t sz, U u);
list& repeat_fun(std::size_t sz, Nullary_function fun);
list& range(SinglePassIterator first, SinglePassIterator last);
list& range(const SinglePassRange& r);

repeat()函数把第二个参数作为要填入的值,重复第一个参数指定的次数,与vectorvdeque等容器的构造函数很相似;repeat_fun()函数同样重复第一个参数的次数,但第二个参数是个无参的函数或函数对象,它返回填入的数值;range()函数则可以把一个序列里的全部或者部分元素插入到另一个序列里。

它们同样也返回列表list_inserter或generic_list,所以可以串联进operator()和operator,操作序列里。

示范它们用法的代码如下:

#include <boost/assign.hpp>
#include <cstdlib>                          //for rand()

void case5()
{
	using namespace boost::assign;

	vector<int> v = list_of(1).repeat(3, 2)(3)(4)(5);
	//v = 1,2,2,2,3,4,5
	for (auto& x : v)
		cout << x << ",";
	cout << endl;


	multiset<int> ms;
	insert(ms).repeat_fun(5, &rand).repeat(2, 1), 10;
	//ms = x,x,x,x,x,1,1,10
	for (auto& x : ms)
		cout << x << ",";
	cout << endl;


	deque<int> d;
	push_front(d).range(v.begin(), v.begin() + 5);
	//d = 3,2,2,2,1
	for (auto& x : d)
		cout << x << ",";
	cout << endl;
}

操作非标准容器

assign库不仅支持全部8个标准容器(vector、string、deque、 list、set、multiset、map、multimap),也对容器适配器提供了适当的支持,包括 stack、queue和priority_queue。

因为这三个容器适配器没有push_back/push_front函数,所以assign库提供push()函数来赋值,但stack 可以使用 operator+=。初始化的list_of表达式最后则使用to_adapter()成员函数来适配到非标准容器。如果使用逗号操作符还需要把整个表达式用括号括起来,才能使用点号操作符调用to_adapter()。

示范assign库应用于容器适配器的代码如下:

#include <stack>
#include <queue>

int main()
{
	using namespace boost::assign;

	stack<int> stk = (list_of(1), 2, 3).to_adapter();
	stk += 4, 5, 6;
	for (; !stk.empty();) //输出stack的内容
	{
		cout << stk.top() << " ";
		stk.pop();
	}
	cout << endl;

	queue<string> q = (list_of("china")("us")("uk")).
		repeat(2, "russia").to_adapter();
	push(q)("germany");
	for (; !q.empty();) //输出queue的内容
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;

	priority_queue<double> pq = (list_of(1.414), 1.732, 2.236).to_adapter();
	push(pq), 3.414, 2.71828;
	for (; !pq.empty();) //输出优先队列的内容
	{
		cout << pq.top() << " ";
		pq.pop();
	}

}

其他议题

assign 库还有两个类似功能的函数ref_list_of()和 cref_list_of(),这两个函数接受变量的引用作为参数来创建初始化列表,较list_of()的内部 deque效率更高,但用法略微麻烦,例如:

using namespace boost::assign;

int a = 1, b = 2, c = 3;

vector<int> v = ref_list_of<3>(a)(b)(c);
assert(v.size() == 3);

代码示例

#include <iostream>
using namespace std;

#include <boost/assign.hpp>
//using namespace boost::assign;

//
void case1()
{
	using namespace boost::assign;
	vector<int> v;
	v += 1, 2, 3, 4, 5, 6 * 6;

	for (auto& x : v)
		cout << x << ",";
	cout << endl;

	set<string> s;
	s += "c", "cpp", "lua", "swift";

	for (auto& x : s)
		cout << x << ",";
	cout << endl;

	map<int, string> m;
	m += make_pair(1, "one"), make_pair(2, "two");

}

//
#include <forward_list>
void case2()
{
	using namespace boost::assign;

	vector<int> v;
	push_back(v)(1)(2)(3)(4)(5);

	list<string> l;
	push_front(l)("c")("cpp")("lua")("swift");

	forward_list<string> fl;
	push_front(l)("matrix")("reload");

	set<double> s;
	insert(s)(3.14)(0.618)(1.732);

	map<int, string> m;
	insert(m)(1, "one")(2, "two");
}

//
void case3()
{
	using namespace boost::assign;

	vector<int> v;
	push_back(v), 1, 2, 3, 4, 5;
	push_back(v)(6), 7, 64 / 8, (9), 10;

	for (auto& x : v)
		cout << x << ",";
	cout << endl;

	deque<string> d;
	push_front(d)() = "breath", "of", "the", "wild";
	assert(d.size() == 5);

	for (auto& x : d)
		cout << x << ",";
	cout << endl;

}

//
void case4()
{
	using namespace boost::assign;

	vector<int> v = list_of(1)(2)(3)(4)(5);
	// v = [1, 2, 3, 4, 5]

	deque<string> d =
		(list_of("power")("bomb"), "phazon", "suit");
	// d = [power bomb phazon suit]

	set<int> s = (list_of(10), 20, 30, 40, 50);
	// s = {10 20 30 40 50}

	map<int, string> m = list_of(make_pair(1, "one"))(make_pair(2, "two"));
	// m = [(1, “one”) (2, “two”)]

	map<int, int> m1 = map_list_of(1, 2)(3, 4)(5, 6);
	//m1 = [(1, 2)(3, 4)(5, 6)]

	map<int, string> m2 = map_list_of(1, "one")(2, "two");
	//m2 = [(1, "one")(2, "two")]

}

//
#include <cstdlib>                          //for rand()

void case5()
{
	using namespace boost::assign;

	vector<int> v = list_of(1).repeat(3, 2)(3)(4)(5);
	//v = 1,2,2,2,3,4,5
	for (auto& x : v)
		cout << x << ",";
	cout << endl;


	multiset<int> ms;
	insert(ms).repeat_fun(5, &rand).repeat(2, 1), 10;
	//ms = x,x,x,x,x,1,1,10
	for (auto& x : ms)
		cout << x << ",";
	cout << endl;


	deque<int> d;
	push_front(d).range(v.begin(), v.begin() + 5);
	//d = 3,2,2,2,1
	for (auto& x : d)
		cout << x << ",";
	cout << endl;

}

//
#include <stack>
#include <queue>

void case6()
{
	using namespace boost::assign;

	stack<int> stk = (list_of(1), 2, 3).to_adapter();
	stk += 4, 5, 6;
	for (; !stk.empty();)
	{
		cout << stk.top() << " ";
		stk.pop();
	}
	cout << endl;

	queue<string> q = (list_of("china")("us")("uk")).
		repeat(2, "russia").to_adapter();
	push(q)("germany");
	for (; !q.empty();)
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;

	priority_queue<double> pq = (list_of(1.414), 1.732, 2.236).to_adapter();
	push(pq), 3.414, 2.71828;
	for (; !pq.empty();)
	{
		cout << pq.top() << " ";
		pq.pop();
	}

}


//
void case7()
{
	using namespace boost::assign;

	int a = 1, b = 2, c = 3;

	vector<int> v = ref_list_of<3>(a)(b)(c);
	assert(v.size() == 3);
}

//

int main()
{
	case1();
	case2();
	case3();
	case4();
	case5();
	case6();
	case7();
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/876899.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

财报解读:继续押注Disney+,迪士尼距离盈利还有多远?

迪士尼最新一季的“答卷”&#xff0c;透露着不小的寒气。 近日&#xff0c;迪士尼披露了2023财年第三季度&#xff08;自然年2023年Q2&#xff09;业绩报告&#xff0c;营收223.3亿美元&#xff0c;同比仅增长4%&#xff0c;低于市场预期的225.1亿美元&#xff1b;归母净亏损…

Python第三方库 - Pandas库

文章目录 1. Pandas介绍2. Pandas基础2.1 引入2.2 数据结构2.2.1 Series2.3 DataFrame2.3.1 概念 3 Pandas CSV 文件3.1 语法3.2 遇到的问题 参考文档 1. Pandas介绍 概念: Pandas 是 Python 的核心数据分析支持库&#xff0c;提供了快速、灵活、明确的数据结构&#xff0c;旨…

【Vue-Router】重定向

First.vue <template><h1>First Seciton</h1> </template>Second.vue&#xff0c;Third.vue代码同理 UserSettings.vue <template><h1>UserSettings</h1><router-link to"/settings/children1">children1</ro…

41、可靠传输——停等ARQ

前面两节内容我们学习了传输层的基本概况的一些知识&#xff0c;包括传输层在TCP/IP协议栈中负责的任务、传输层的两大协议&#xff0c;以及端口号、套接字等一些基本的概念。从这一节开始&#xff0c;我们将开启两大协议中TCP协议的学习。 但是&#xff0c;经过之前的学习&am…

企望制造ERP系统 RCE漏洞复现(HW0day)

0x01 产品简介 企望制造纸箱业erp系统由深知纸箱行业特点和业务流程的多位IT专家打造&#xff0c;具有国际先进的管理方式&#xff0c;将现代化的管理方式融入erp软件中&#xff0c;让企业分分钟就拥有科学的管理经验。 erp的功能包括成本核算、报价定价、订单下达、生产下单、…

上传excel文件

文件上传&#xff0c;其实就是用el-upload组件来实现上传&#xff0c;只是换了样式&#xff0c;和图片上传一样 <el-form-item label"选择文件"><el-input placeholder"请选择文件" v-model"form.file" disabled style"width: 45…

自定义批量修改图像位深度

什么是图像位深度&#xff1f;&#xff1f;&#xff1f; 图像位深度(Bit Depth)是指图像中每个像素所占的比特数,它决定了图像能够表示的颜色数量和亮度层级。 简单来说: 位深度越高,每个像素所能表示的颜色数和亮度等级越多。位深度越低,每个像素所能表示的颜色数和亮度等级…

【设备树笔记整理4】内核对设备树的处理

1 从源头分析_内核head.S对dtb的简单处理 1.1 bootloader向内核传递的参数 &#xff08;1&#xff09;bootloader启动内核时&#xff0c;会设置r0&#xff0c;r1&#xff0c;r2三个寄存器&#xff1a; r0一般设置为0;r1一般设置为machine_id (在使用设备树时该参数没有被使用…

809协议nodejs编写笔记(还在更新)

一、总体流程 数据首先通过receiver接受层接收&#xff0c;去掉标识头和标识尾&#xff1b;再进入depacker解包层进行解包&#xff0c;把标识头分解出来并解析&#xff1b;之后发给handler处理层根据不同的消息id选择使用不同的业务逻辑&#xff1b;如果有应答&#xff0c;则通…

Vue2(组件开发)

目录 前言一&#xff0c;组件的使用二&#xff0c;插槽slot三&#xff0c;refs和parent四&#xff0c;父子组件间的通信4.1&#xff0c;父传子 &#xff1a;父传子的时候&#xff0c;通过属性传递4.2&#xff0c;父组件监听自定义事件 五&#xff0c;非父子组件的通信六&#x…

JL701N编译后查看内存使用情况

编译之后&#xff0c;可以在 cpu/br28/tools/sdk.map 中查看实际的使用情况.

MySQL 账号权限

mysql 在安装好后&#xff0c;默认是没有远端管理账号。 一、账号管理 1. 查看账号列表 MySQL用户账号和信息存储在名为 mysql 的数据库中。一般不需要直接访问 mysql 数据库和表&#xff0c;但有时需要直接访问。例如&#xff0c;查看数据库所有用户账号列表时。 USE mysql; …

第G2周:人脸图像生成(DCGAN)

&#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] 1. DCGAN原理 深度卷积对抗网络&#xff08;Deep Convolutional Generative Adversarial Networks, DCGAN&#…

Python的十二道编程题,码住战胜一切

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 一、计算文件大小 import os def get_size(path):size 0l [path]while l:path l.pop()lst os.listdir(path)for name in lst:son_path os.path.join(path,name)if os.path.isfile(son_path):size os.path.getsize(son_…

个人博客系统测试报告

文章目录 一、功能测试1.编写测试用例2.总结测试后发现的BUG 二、UI自动化测试0.搭建测试环境1. 创建公共类2.注册页面UI自动化测试用例编写3.登录页面UI自动化测试用例编写4.用户博客列表页面自动化测试5. 修改个信息页面6. 文章编辑页面7. 设置密保问题发现bug 8. 所有用户文…

QTreeView显示多级多列目录

效果图&#xff1a; 头文件&#xff1a; QStandardItemModel *m_treeMode;源文件 m_treeMode new QStandardItemModel(0,1,this);ui->treeView->setModel(m_treeMode);//控制第一列节点个数int mainLevel 3;for (int i 0; i < mainLevel; i) {QList<QStandar…

【设计模式】非GOF的常见设计模型

结构型模式 系列综述&#xff1a; 来源&#xff1a;该系列是主要参考《大话设计模式》和《设计模式(可复用面向对象软件的基础)》&#xff0c;其他详细知识点拷验来自于各大平台大佬的博客。 总结&#xff1a;设计模式汇总篇 如果对你有用&#xff0c;希望关注点赞收藏一波。 文…

无需停服!PostgreSQL数据迁移工具-NineData

PostgreSQL 是一种备受开发者和企业青睐的关系型数据库&#xff0c;其丰富的数据类型、地理空间负载和强大的扩展能力等特性使其备受欢迎。然而&#xff0c;在企业使用 PostgreSQL 承载应用的过程中&#xff0c;由于业务需要上云、跨云、下云、跨机房迁移、跨地域迁移、数据库版…

C语言学习笔记---操作符详解

C语言程序设计笔记---012 C语言操作符1、算数操作符1.1、算术操作符例程 2、移位操作符2.1、移位操作符例程12.2、移位操作符例程22.3、移位操作符例程3 3、位操作符3.1、位操作符例程13.2、位操作符例程23.3、位操作符例程3 --按位与&1扩展3.4、位操作符例程4 --按位异或^…

使用SpringAop切面编程通过Spel表达式实现Controller权限控制

目录 参考一、概念SpEL表达式 二、开发引入包定义注解定义切面定义用户上下文 三、测试新建Service在方法上注解新建Service在类上注解运行 参考 SpringBoot&#xff1a;SpEL让复杂权限控制变得很简单 一、概念 对于在Springboot中&#xff0c;利用自定义注解切面来实现接口…