инициализировать константный массив в инициализаторе класса в C ++ - PullRequest
71 голосов
/ 02 октября 2008

У меня есть следующий класс в 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 ]

72 голосов
/ 18 октября 2011

В 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;
}
32 голосов
/ 02 октября 2008

Как говорили другие, ISO C ++ не поддерживает это. Но вы можете обойти это. Просто используйте вместо него std :: vector.

int* a = new int[N];
// fill a

class C {
  const std::vector<int> v;
public:
  C():v(a, a+N) {}
};
25 голосов
/ 02 октября 2008

Это невозможно в текущем стандарте. Я полагаю, что вы сможете сделать это в C ++ 0x, используя списки инициализаторов (см. Краткий обзор C ++ 0x , Бьярн Страуструп, для получения дополнительной информации о списках инициализаторов и других хороших C +). + 0x функций).

12 голосов
/ 15 апреля 2010

std::vector использует кучу. Черт возьми, что за пустая трата времени - просто ради проверки работоспособности. Точка 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 - это крошечный закрытый класс для предоставления значений инициализации. Размер массива проверяется во время компиляции с помощью аргументов шаблона, поэтому нет шансов выйти за пределы.

Я уверен, что есть более экзотические способы изменить это. Это начальный удар. Я думаю, что вы можете в значительной степени восполнить любой недостаток компилятора с классами.

9 голосов
/ 02 октября 2008

Стандарт 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 имел разные значения элементов в массиве Вы делаете ошибку, пытаясь сделать массив 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;
}
4 голосов
/ 02 октября 2008

Там, где у меня постоянный массив, он всегда был статическим. Если вы можете принять это, этот код должен скомпилироваться и запустить.

#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;
}
4 голосов
/ 02 октября 2008

Вы не можете сделать это из списка инициализации,

Посмотрите на это:

http://www.cprogramming.com/tutorial/initialization-lists-c++.html

:)

3 голосов
/ 10 июня 2011

Как насчет эмуляции массива const через функцию доступа? Это не статично (как вы просили) и не требует 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.

3 голосов
/ 19 мая 2010

Решение без использования кучи с 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){};
};
2 голосов
/ 28 мая 2009

Интересно, что в 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, а также в случае изменения.

...