现在有这么几个结构体:
typedef struct _info_head{ u_int src_ip; u_int dest_ip; u_int src_port; u_int dest_port; }info_head; typedef struct _pkt_info{ u_long sec; u_long u_sec; u_int pkt_size; u_int pld_size; } pkt_info; typedef struct _pkt{ info_head head; pkt_info info; } pkt;
虽然这几个结构体都是由基本类型构成的,而且也不涉及动态内存管理,但是体积确实是有一点的。其中info_head是16字节,pkt_info是24个字节,那么一个pkt就是40个字节啦。这还只是在32位的机器上的情况。
之后我们又遇到了一个这样的类,这个类使用list容器,存储了多个pkt类型的变量。
class pkt_list{ public: static list< pkt > head_to_pkt; public: static int append(const info_head &, const pkt_info &); };
下面就来实现一下append()函数吧
int pkt_list::append(const info_head &head, const pkt_info &info){ pkt tmp; tmp.head = head; tmp.info = info; head_to_pkt.push_back(tmp); return 1; }
由于pkt只包含基本类型,所以直接用就好啦,不用new,也不用担心内存管理的问题。
这里我们看到了第一个问题,要想把一组info_head和pkt_info类型的变量合并成一个pkt类型的变量,就必须重新组建一个pkt类型的变量。就是说,内存中已经有了16B的info_head和24B的pkt_info之后,但是还要再使用40B去存储和前边一对变量值完全相同的一个pkt类型的变量。虽然有浪费内存的嫌疑,但这个是避免不了的,因为我们没法保证之前的那两个16B和24B的东西就挨在一起。所以其实也不能算是浪费。
但是接下来又有一个问题,就是把这个pkt类型的变量,搬进list<pkt>里时,内存又做了一次拷贝。因为tmp在append函数结束后就消失了,那么理所当然的,想要在list<pkt> head_to_pkt里留住这个变量的信息,就只好再拷贝一份了。
那么这样算下来,我们已经用了120B的内存来保存同一段数据了。这样确实是一个很浪费的举动。但是在C++14出现之前,我们无能为力。
C++14中提出了右值引用的概念,也叫move语义。具体怎么个意义就不说了,直接说效果,那就是可以省下40B的开销。但这是咋做到的呢?很简单,就是直接把第二次的那40B直接移入容器中就可以了,而不是再拷贝复制一次。这么说来,list<T>::push_back(T&);就应该又另一个更高效的版本了,那就是list<T>::push_back(T&&);。总的来说,STL有得重写了。
接下来,为了使用第二个版本的push_back函数的重载,我们必须在参数列表里写上作为右值的无名变量,但是如果已经使用了一个寄存器变量tmp,那么这40B的开销是无论如何也省不下来的,因此tmp已经有了名字,就不再是右值了。而要想得到pkt类型的无名变量,就要重新为pkt写一个构造函数了。全部改完之后,大概是这个样子的。
class pkt{ public: pkt(){} pkt(const info_head &h, const pkt_info i): head(h), info(i){} info_head head; pkt_info info; }; ... int pkt_list::append(const info_head &head, const pkt_info &info){ head_to_pkt.push_back( pkt( head, info ) ); //With move semantics version being used, it works. return 1; }
OK。就总结这么多。