如何抑制类型的自动初始化和销毁?虽然T buffer[100]
自动初始化buffer
的所有元素并在它们超出范围时销毁它们,但这不是我想要的行为。
#include <iostream>
static int created = 0,
destroyed = 0;
struct S
{
S()
{
++created;
}
~S()
{
++destroyed;
}
};
template <typename T, size_t KCount>
class fixed_vector
{
private:
T m_buffer[KCount];
public:
fixed_vector()
{
// some way to suppress the automatic initialization of m_buffer
}
~fixed_vector()
{
// some way to suppress the automatic destruction of m_buffer
}
};
int main()
{
{
fixed_vector<S, 100> arr;
}
std::cout << "Created:\t" << created << std::endl;
std::cout << "Destroyed:\t" << destroyed << std::endl;
return 0;
}
该程序的输出是:
Created: 100
Destroyed: 100
我希望它是:
Created: 0
Destroyed: 0
我唯一的想法是让m_buffer
像char
那样简单地构造和破坏类型,然后依靠operator[]
为我包装指针数学,虽然这看起来像是一个可怕的黑客解。另一个解决方案是使用malloc
和free
,但是这提供了我不想要的间接级别。
我想要这个的原因是因为我正在制作一个容器而且我不想为我不会使用的东西支付初始化开销。例如,如果我的main
函数是:
int main()
{
{
std::vector<S> vec;
vec.reserve(50);
}
std::cout << "Created:\t" << created << std::endl;
std::cout << "Destroyed:\t" << destroyed << std::endl;
return 0;
}
输出正确:
Created: 0
Destroyed: 0
答案 0 :(得分:4)
您可能需要查看boost::optional
template <typename> struct tovoid { typedef void type; };
template <typename T, size_t KCount, typename = void>
struct ArrayStorage {
typedef T type;
static T &get(T &t) { return t; }
};
template <typename T, size_t KCount>
struct ArrayStorage<T, KCount, typename tovoid<int T::*>::type> {
typedef boost::optional<T> type;
static T &get(boost::optional<T> &t) {
if(!t) t = boost::in_place();
return *t;
}
};
template <typename T, size_t KCount>
class Array
{
public:
T &operator[](std::ptrdiff_t i) {
return ArrayStorage<T, KCount>::get(m_buffer_[i]);
}
T const &operator[](std::ptrdiff_t i) const {
return ArrayStorage<T, KCount>::get(m_buffer_[i]);
}
mutable typename ArrayStorage<T, KCount>::type m_buffer_[KCount];
};
对类类型进行专门化,将它们包装到optional
中,从而懒惰地调用构造函数/析构函数。对于非类类型,我们不需要包装。不包装它们意味着我们可以将&a[0]
视为连续的内存区域,并将该地址传递给需要数组的C函数。 boost::in_place
将在原地创建类类型,而不使用临时T
或其复制构造函数。
不使用继承或私有成员允许类保持聚合,从而允许方便的初始化形式
// only two strings are constructed
Array<std::string, 10> array = { a, b };
答案 1 :(得分:3)
您可以将数组创建为char
的数组,然后在需要时使用placement new
创建元素。
template <typename T, size_t KCount>
class Array
{
private:
char m_buffer[KCount*sizeof(T)]; // TODO make sure it's aligned correctly
T operator[](int i) {
return reinterpret_cast<T&>(m_buffer[i*sizeof(T)]);
}
重新阅读你的问题之后,似乎你想要一个稀疏数组,这有时会出现在 map ; o)的名称(当然性能特征不同......)
template <typename T, size_t KCount>
class SparseArray {
std::map<size_t, T> m_map;
public:
T& operator[](size_t i) {
if (i > KCount)
throw "out of bounds";
return m_map[i];
}
答案 2 :(得分:1)
此代码:
#include <iostream>
#include <vector>
using namespace std;
int created = 0, destroyed = 0;
struct S
{
S()
{
++created;
}
S(const S & s ) {
++created;
}
~S()
{
++destroyed;
}
};
int main()
{
{
std::vector<S> vec;
vec.reserve(50);
}
std::cout << "Created:\t" << created << std::endl;
std::cout << "Destroyed:\t" << destroyed << std::endl;
return 0;
}
具有您想要的输出 - 我不确定您的问题是什么。
答案 3 :(得分:1)
如果你想成为矢量,你应该做这样的事情:
template <typename T>
class my_vector
{
T* ptr; // this is your "array"
// ...
public:
void reserve(size_t n)
{
// allocate memory without initializing, you could as well use malloc() here
ptr = ::operator new (n*sizeof(T));
}
~my_vector()
{
::operator delete(ptr); // and this is how you free your memory
}
void set_element(size_t at, const T& element = T())
{
// initializes single element
new (&ptr[at]) T(element); // placement new, copies the passed element at the specified position in the array
}
void destroy_element(size_t at)
{
ptr[at].~T(); // explicitly call destructor
}
};
这段代码显然只是为了演示,我省略了my_vector的copy-constructor以及对创建和不创建的内容的任何跟踪(在你没有调用构造函数的位置调用析构函数可能是未定义的行为)。此外,STL的vector
分配和解除分配通过使用分配器(vector
的第二个模板参数)被抽象出来。
希望有所帮助
答案 4 :(得分:0)
你可以看一下使用STL容器的方式,但我怀疑你可以省去malloc
和free
s