确保以下大型结构的整数初始化为0的最佳方法是什么?
struct Statistics {
int num_queries;
int num_respones;
// ... 97 more counters here
int num_queries_filtered;
}
我希望避免必须检查每个位置此结构已初始化,以确保使用Statistics s();
初始化值,而不是使用Statistics s;
初始化默认值。
Statistics s; // Default initialized by accident here
s.num_queries++; // Oh no, this is a bug because it wasn't initialized to zero
Statistics s2{}; // Correctly value initialized
s2.num_queries++; // Successful
提案1 - 使用memset
,但这感觉就像一个黑客,我们利用值初始化发生的等价于0填充数据结构:
struct Statistics {
Statistics() { memset(this, 0, sizeof(*this)); }
// ... counters here
}
提案2 - 使用构造函数初始化列表,但这很麻烦,当人们将来添加新计数器时,他们可能会忘记在构造函数中对它们进行零初始化:
struct Statistics {
Statistics() : num_queries(0), num_respones(0), /* ... */, num_queries_filtered(0) {}
// ... counters here
}
提案3 - 强制值初始化按如下方式进行:
struct StatisticsUnsafe {
// ... counters here
}
struct Statistics : public StatisticsUnsafe {
Statistics() : StatisticsUnsafe() {}
}
您觉得最好的方法是什么?你有其他选择吗?
编辑我想澄清一下,在我的实际代码中,每个计数器都有一个有意义的名称,例如" num_queries_received"," num_responses",这就是为什么我不选择使用形式的矢量或数组" counter [100]"
EDIT2 将示例从Statistics s2();
更改为Statistics s2{};
答案 0 :(得分:7)
从C ++ 11开始,你也可以这样做:
struct Statistics {
int counter1 = 0;
int counter2 = 0;
// ... more counters here
int counter100 = 0;
};
答案 1 :(得分:5)
除非您有相当具体的理由不这样做,否则您的首选应该是std::vector
,例如:
std::vector<int> Statistics(100);
这将自动归零所有内容。您可以将数组中的个人counter
称为:
++Statistics[40];
...这会增加41 st 项目(第一项是Statistics[0]
)。
如果大小确实固定为100(或者您在编译时知道的其他数字),您可能更愿意使用std::array
:
std::array<int, 100> Statistics;
这可能会更快一点,通常使用(少)内存,但修复了大小(而使用std::vector
,您可以使用push_back
,erase
等,添加和删除项目。)
鉴于编辑过的问题(对象真的不像数组一样),我可能会考虑一些不同的东西,可能是这样的:
template <class T>
class inited {
T val;
public:
inited(T val=T()) : val(val) {}
operator T() const { return val; }
operator=(T const &newval) { val = new_val; }
};
struct Statistics {
inited<int> sum;
inited<int> count;
inited<double> mean;
};
然后inited<T>
总是被初始化为某个值 - 如果你愿意,你可以指定一个值,如果你没有指定任何值,它会使用值初始化(对于算术类型,它将给出0,a指针类型的空指针,或者使用默认构造函数来定义一个类型的类型。
由于它定义了operator T
和operator=
,您仍然可以像往常一样分配到/来自元素:
Statistics.sum = 100;
Statistics.count = 2;
Statistics.mean = static_cast<double>(Statistics.sum) / Statistics.count;
您可能更喜欢使用单曲:
operator T&() { return val; }
相反。这既支持读写又支持(如上所述),还支持复合赋值运算符(例如+=
和-=
)。
答案 2 :(得分:3)
您是否考虑为每个数据成员编写初始化程序?
struct Statistics {
typedef int counter_t;
counter_t counter1 = 0;
counter_t counter2 = 0;
// ... more counters here
counter_t counter100 = 0;
};
请注意,如果您包含此类初始值设定项,则该结构不再是聚合,因此无法通过支持列表使用aggregate initialization初始化该结构。这种类型是否重要很难说。
答案 3 :(得分:3)
嗯,你当然可以这样做:
struct Statistics {
int counter1 = 0;
int counter2 = 0;
// ... more counters here
int counter100 = 0;
};
这在c ++ 11中完全有效。但问题是,你真的需要这个吗?使用矢量不是更方便吗?
struct Statistics {
std::vector<int> counters = std::vector<int>(100, 0);
};
如果vector不是一个选项,你可以在构造函数中做一些魔术:
struct Statistics {
int counter1;
int counter2;
// ... more counters here
int counter100;
Statistics() {
for (int * i : {&counter1, &counter2, ..., &counter100 }) {
*i = 0;
}
}
};
Statistics s;
s.counter2; // now stores 0 or anything you like.
答案 4 :(得分:0)
以下是C
的方式:
#include <assert.h>
#include <cstring>
#include <type_traits>
struct Statistics {
int counter1;
int counter2;
int counter3;
int counter4;
// maybe more //
Statistics() {
// checks whether Statistics is standard-layout
// to be sure that memset won't break it
static_assert(
std::is_standard_layout<Statistics>(),
"Someone broke Statistics, can't use memset to zero it.");
// initializes hole Statistics's memory by zeros
memset(this, 0, sizeof(Statistics));
}
};
// Here is a way how to check Statistics
void assert_Statistics() {
Statistics s;
int* ptr = reinterpret_cast<int*>(&s);
int count = sizeof(Statistics) / sizeof(int);
for (int i = 0; i < count; ++i) {
assert(*(ptr++) == 0);
}
}
int main()
{
Statistics s;
assert_Statistics();
}