C++中的变长参数深入理解

2020-01-06 15:53:16刘景俊

上述宏定义中, _INTSIZEOF(n) 将地址的低2位指令,做内存的4字节对齐。每次取参数时,调用__crt_va_arg(ap,t)  ,返回t类型参数地址的值,同时将ap偏移到t之后。最后,调用_crt_va_end(ap)将ap置0.

变长参数的函数的使用及其原理看了宏定义是很好理解的。从上文可知,要使用变长参数函数的参数,我们必须知道传入的每个参数的类型。printf中,有format字符串中的特殊字符组合来解析后面的参数类型。但是当传入类的构造函数的参数时,我们并不知道每个参数都是什么类型,虽然参数能够依次传入函数,但无法解析并获取每个参数的数值。因此传统的变长参数函数并不足以解决传入任意构造函数参数的问题。

二、变长参数模板

我们需要用到C++11的新特性,变长参数模板。

这里举一个使用自定义内存池的例子。定义一个内存池类MemPool.h,以count个类型T为单元分配内存,默认分配一个对象。每当内存内空闲内存不够,则一次申请MEMPOOL_NEW_SIZE个内存对象。内存池本身只负责内存分配,不做初始化工作,因此不需要传入任何参数,只需实例化模板分配相应类型的内存即可。


#ifndef UTIL_MEMPOOL_H
#define UTIL_MEMPOOL_H

#include <stdlib.h>

#define MEMPOOL_NEW_SIZE 8

template<typename T, size_t count = 1>
class MemPool
{
private:
 union MemObj {
  char _obj[1];
  MemObj* _freelink;
 };

public:
 static void* Allocate()
 {
  if (!_freelist) {
   refill();
  }
  MemObj* alloc_mem = _freelist;
  _freelist = _freelist->_freelink;
  ++_size;
  return (void*)alloc_mem;
 }

 static void DeAllocate(void* p)
 {
  MemObj* q = (MemObj*)p;
  q->_freelink = _freelist;
  _freelist = q;
  --_size;
 }

 static size_t TotalSize() {
  return _totalsize;
 }

 static size_t Size() {
  return _size;
 }
private:
 static void refill()
 {
  size_t size = sizeof(T) * count;
  char* new_mem = (char*)malloc(size * MEMPOOL_NEW_SIZE);
  for (int i = 0; i < MEMPOOL_NEW_SIZE; ++i) {
   MemObj* free_mem = (MemObj*)(new_mem + i * size);
   free_mem->_freelink = _freelist;
   _freelist = free_mem;
  }
  _totalsize += MEMPOOL_NEW_SIZE;
 }

 static MemObj* _freelist;
 static size_t _totalsize;
 static size_t _size;
};

template<typename T, size_t count>
typename MemPool<T, count>::MemObj* MemPool<T, count>::_freelist = NULL;

template<typename T, size_t count>
size_t MemPool<T, count>::_totalsize = 0;

template<typename T, size_t count>
size_t MemPool<T, count>::_size = 0;
#endif

接下来在没有变长参数的情况下,实现通用MemNewMemDelete函数模板。这里不对函数模板作详细解释,用函数模板我们可以对不同的类型实现同样的内存池分配操作。如下: