Инициализировать константный массив в инициализаторе класса в C++
У меня есть следующий класс в C++:
class a {
const int b[2];
// other stuff follows
// and here's the constructor
a(void);
}
Вопрос в том, как мне инициализировать b в списке инициализации, учитывая, что я не могу инициализировать его внутри тела функции конструктора, потому что b const
?
Это не работает:
a::a(void) :
b([2,3])
{
// other initialization stuff
}
Редактировать: речь идет о том, когда я могу иметь разные значения для b
для разных экземпляров, но известно, что значения постоянны в течение всего времени существования экземпляра.
10 ответов
Как и другие говорили, 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++11 ответ на этот вопрос теперь изменился, и вы можете сделать:
struct a {
const int b[2];
// other bits follow
// and here's the constructor
a();
};
a::a() :
b{2,3}
{
// other constructor work
}
int main() {
a a;
}
Это невозможно в текущем стандарте. Я полагаю, что вы сможете сделать это в C++0x, используя списки инициализаторов (см . Краткий обзор C++0x, Бьярн Страуструп, для получения дополнительной информации о списках инициализаторов и других замечательных возможностях C++0x).
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
после этой инициализации.
Второй позволяет размещать массив внутри другого объекта, который может находиться в куче или просто в стеке, если там находится объект. Там нет траты времени на выделение из кучи. Он также выполняет проверку констант во время компиляции массива.
b_filler
это крошечный закрытый класс для предоставления значений инициализации. Размер массива проверяется во время компиляции с аргументами шаблона, поэтому нет шансов выйти за пределы.
Я уверен, что есть более экзотические способы изменить это. Это начальный удар. Я думаю, что вы можете в значительной степени восполнить любой недостаток компилятора с классами.
Стандарт 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, тогда вы делаете ошибка в попытке сделать массив 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;
}
Там, где у меня постоянный массив, он всегда был статическим. Если вы можете принять это, этот код должен скомпилироваться и запустить.
#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;
}
Вы не можете сделать это из списка инициализации,
Посмотри на это:
http://www.cprogramming.com/tutorial/initialization-lists-c++.html
:)
Решение без использования кучи с 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){};
};
Как насчет эмуляции константного массива через функцию доступа? Это не статично (как вы просили) и не требует stl или какой-либо другой библиотеки:
class a {
int privateB[2];
public:
a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; }
int b(const int idx) { return privateB[idx]; }
}
Поскольку::privateB является приватным, он фактически является константой вне a::, и вы можете обращаться к нему подобно массиву, например
a aobj(2,3); // initialize "constant array" b[]
n = aobj.b(1); // read b[1] (write impossible from here)
Если вы хотите использовать пару классов, вы можете дополнительно защитить privateB от функций-членов. Это может быть сделано путем наследования; но я думаю, что я предпочитаю сообщение Джона Харрисона comp.lang.C++, использующее класс const.
Интересно, что в C# у вас есть ключевое слово const, которое переводит в статический const C++, в отличие от 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, а также в случае изменения.