Fri*_*igo 8 c++ arrays gcc g++
我一直在努力实现一段时间(T*v)函数,到目前为止还没有任何成功.
T v [n]阵列有两种基本的,众所周知的解决方案,一旦阵列衰减成T*v指针,这两种解决方案都是无用的甚至是危险的.
#define SIZE(v) (sizeof(v) / sizeof(v[0]))
template <class T, size_t n>
size_t lengthof (T (&) [n])
{
return n;
}
Run Code Online (Sandbox Code Playgroud)
有一些涉及包装类和容器的解决方法,如STLSoft的array_proxy,boost :: array,std :: vector等.所有这些都有缺点,缺乏简单性,语法糖和数组的广泛使用.
关于涉及特定于编译器的调用的解决方案存在神话,当delete []需要知道数组的长度时,编译器通常会使用这些调用.根据C++ FAQ Lite 16.14,编译器使用两种技术来了解要释放多少内存:过度分配和关联数组.在过度分配时,它会分配一个更多的单词,并将数组的长度放在第一个对象之前.另一种方法显然将长度存储在关联数组中.是否有可能知道G ++使用哪种方法,并提取适当的数组长度?开销和填充怎么样?对非特定于编译器的代码有什么希望吗?甚至是非平台特定的G ++内置程序?
还有一些解决方案涉及重载operator new []和operator delete [],我实现了:
std::map<void*, size_t> arrayLengthMap;
inline void* operator new [] (size_t n)
throw (std::bad_alloc)
{
void* ptr = GC_malloc(n);
arrayLengthMap[ptr] = n;
return ptr;
}
inline void operator delete [] (void* ptr)
throw ()
{
arrayLengthMap.erase(ptr);
GC_free(ptr);
}
template <class T>
inline size_t lengthof (T* ptr)
{
std::map<void*, size_t>::const_iterator it = arrayLengthMap.find(ptr);
if( it == arrayLengthMap.end() ){
throw std::bad_alloc();
}
return it->second / sizeof(T);
}
Run Code Online (Sandbox Code Playgroud)
它工作得很好,直到我得到一个奇怪的错误:lengthof找不到数组.事实证明,G ++在这个特定数组的开头分配了8个字节,而不是应该拥有的字节数.虽然operator new []应该返回整个数组的开头,称之为ptr,调用代码改为ptr + 8,所以lengthof(ptr + 8)显然因异常而失败(即使它没有,它可能有可能返回错误的数组大小).这8个字节是某种开销还是填充?不能是前面提到的过度分配,该功能对许多阵列都能正常工作.它是什么以及如何禁用或解决它,假设可以使用G ++特定的调用或欺骗?
编辑: 由于有多种方式可以分配C风格的数组,通常不可能通过指针来判断任意数组的长度,正如Oli Charlesworth建议的那样.但是根据Ben Voigt的想法,可以使用非衰减的静态数组(参见上面的模板函数)和使用自定义运算符new [](size_t,size_t)分配的数组:
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#include <iostream>
#include <map>
typedef std::map<void*, std::pair<size_t, size_t> > ArrayLengthMap;
ArrayLengthMap arrayLengthMap;
inline void* operator new [] (size_t size, size_t count)
throw (std::bad_alloc)
{
void* ptr = GC_malloc(size);
arrayLengthMap[ptr] = std::pair<size_t, size_t>(size, count);
return ptr;
}
inline void operator delete [] (void* ptr)
throw ()
{
ArrayLengthMap::const_iterator it = arrayLengthMap.upper_bound(ptr);
it--;
if( it->first <= ptr and ptr < it->first + it->second.first ){
arrayLengthMap.erase(it->first);
}
GC_free(ptr);
}
inline size_t lengthof (void* ptr)
{
ArrayLengthMap::const_iterator it = arrayLengthMap.upper_bound(ptr);
it--;
if( it->first <= ptr and ptr < it->first + it->second.first ){
return it->second.second;
}
throw std::bad_alloc();
}
int main (int argc, char* argv[])
{
int* v = new (112) int[112];
std::cout << lengthof(v) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,由于编译器的任意开销和填充,到目前为止还没有可靠的方法来确定自定义运算符new [](size_t)中动态数组的长度,除非我们假设填充小于1的大小数组的元素.
然而,正如Ben Voigt所建议的,还有其他类型的数组也可以进行长度计算,因此构造一个可以在其构造函数中接受多种数组(及其长度)的包装类是可能的,也是可取的,并隐式或显式转换为其他包装类和数组类型.不同类型阵列的不同生命周期可能是一个问题,但它可以通过垃圾收集来解决.
您的分析在大多数情况下是正确的,但是我认为您忽略了以下事实:带有琐碎析构函数的类型不需要存储长度,因此对于不同类型,过度分配可能会有所不同。
该标准允许operator new[]
窃取一些字节以供自己使用,因此您必须对指针进行范围检查,而不是精确匹配。 std::map
可能对此并不有效,但是应该对排序后的向量进行排序(可以进行二进制搜索)。平衡的树也应该很好地工作。