在C ++中的类初始值设定项中初始化const数组
我在C ++中有以下课程:
class a {
const int b[2];
// other stuff follows
// and here's the constructor
a(void);
}
问题是,由于b是const,我不能在构造函数的函数体内对其进行初始化,因此如何在初始化列表中初始化b?
这不起作用:
a::a(void) :
b([2,3])
{
// other initialization stuff
}
编辑:恰当的例子是当我可以为不同的实例使用不同的b值时,但是在实例的生命周期中这些值是恒定的。
解决方案
我们无法从初始化列表中执行此操作,
看看这个:
http://www.cprogramming.com/tutorial/initialization-lists-c++.html
:)
在当前标准中是不可能的。我相信我们可以使用初始化列表在C ++ 0x中做到这一点(有关初始化列表和其他不错的C ++ 0x功能的更多信息,请参见Bjarne Stroustrup的C ++ 0x简介)。
在我有一个常量数组的地方,它总是作为静态完成的。如果我们可以接受,则此代码应编译并运行。
#include <stdio.h>
#include <stdlib.h>
class a {
static const int b[2];
public:
a(void) {
for(int i = 0; i < 2; i++) {
printf("b[%d] = [%d]\n", i, b[i]);
}
}
};
const int a::b[2] = { 4, 2 };
int main(int argc, char **argv)
{
a foo;
return 0;
}
ISO标准C ++不允许我们这样做。如果是这样,语法可能是:
a::a(void) :
b({2,3})
{
// other initialization stuff
}
或者类似的规定。从问题来看,实际上听起来像我们想要的是数组的常量类(又称静态)成员。 C ++确实可以做到这一点。像这样:
#include <iostream>
class A
{
public:
A();
static const int a[2];
};
const int A::a[2] = {0, 1};
A::A()
{
}
int main (int argc, char * const argv[])
{
std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n";
return 0;
}
输出为:
A::a => 0, 1
当然,由于这是一个静态类成员,因此对于类A的每个实例都是相同的。如果这不是我们想要的,即,我们希望每个实例A在数组a中具有不同的元素值,那么试图使数组以const开头的错误。我们应该这样做:
#include <iostream>
class A
{
public:
A();
int a[2];
};
A::A()
{
a[0] = 9; // or some calculation
a[1] = 10; // or some calculation
}
int main (int argc, char * const argv[])
{
A v;
std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n";
return 0;
}
就像其他人说的那样,ISO C ++不支持该功能。但是我们可以解决它。只需使用std :: vector即可。
int* a = new int[N];
// fill a
class C {
const std::vector<int> v;
public:
C():v(a, a+N) {}
};
有趣的是,在C中,关键字const可以转换为C ++的静态const,而readonly则只能在构造函数和初始化中设置,甚至可以由非常量来设置,例如:
readonly DateTime a = DateTime.Now;
我同意,如果我们有一个const预定义数组,也可以将其设为静态。
此时,我们可以使用以下有趣的语法:
//in header file
class a{
static const int SIZE;
static const char array[][10];
};
//in cpp file:
const int a::SIZE = 5;
const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"};
但是,我没有找到解决常数" 10"的方法。尽管原因很明显,但它需要知道如何执行对阵列的访问。一个可能的替代方法是使用#define,但是我不喜欢该方法,并且我在标题的末尾#undef,并在CPP处进行了注释,以防发生更改。
std :: vector使用堆。真是的,这只是为了进行const健全性检查而造成的浪费。 " std :: vector"的意义在于运行时的动态增长,而不是编译时应进行的任何旧语法检查。如果我们不打算扩展,请创建一个类来包装常规数组。
#include <stdio.h>
template <class Type, size_t MaxLength>
class ConstFixedSizeArrayFiller {
private:
size_t length;
public:
ConstFixedSizeArrayFiller() : length(0) {
}
virtual ~ConstFixedSizeArrayFiller() {
}
virtual void Fill(Type *array) = 0;
protected:
void add_element(Type *array, const Type & element)
{
if(length >= MaxLength) {
// todo: throw more appropriate out-of-bounds exception
throw 0;
}
array[length] = element;
length++;
}
};
template <class Type, size_t Length>
class ConstFixedSizeArray {
private:
Type array[Length];
public:
explicit ConstFixedSizeArray(
ConstFixedSizeArrayFiller<Type, Length> & filler
) {
filler.Fill(array);
}
const Type *Array() const {
return array;
}
size_t ArrayLength() const {
return Length;
}
};
class a {
private:
class b_filler : public ConstFixedSizeArrayFiller<int, 2> {
public:
virtual ~b_filler() {
}
virtual void Fill(int *array) {
add_element(array, 87);
add_element(array, 96);
}
};
const ConstFixedSizeArray<int, 2> b;
public:
a(void) : b(b_filler()) {
}
void print_items() {
size_t i;
for(i = 0; i < b.ArrayLength(); i++)
{
printf("%d\n", b.Array()[i]);
}
}
};
int main()
{
a x;
x.print_items();
return 0;
}
ConstFixedSizeArrayFiller和ConstFixedSizeArray是可重用的。
第一个允许在初始化数组的同时进行运行时边界检查(与矢量可能相同),该数组在初始化之后可以变为" const"。
第二个允许将数组分配到另一个对象内,该对象可以在堆上,或者如果是对象所在的地方,则可以只是堆栈。没有浪费时间从堆分配资源。它还在数组上执行编译时const检查。
b_filler是一个很小的私有类,用于提供初始化值。数组的大小在编译时使用模板参数进行检查,因此不会超出范围。
我敢肯定,还有更多异国情调的方法可以对此进行修改。这是最初的刺伤。我认为我们几乎可以弥补编译器类的缺点。
没有将堆与std :: vector一起使用的解决方案是使用boost :: array,尽管我们不能直接在构造函数中初始化数组成员。
#include <boost/array.hpp>
const boost::array<int, 2> aa={ { 2, 3} };
class A {
const boost::array<int, 2> b;
A():b(aa){};
};

