инициализировать константный массив в инициализаторе класса в C++

StackOverflow https://stackoverflow.com/questions/161790

Вопрос

У меня есть следующий класс на 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 для разных экземпляров, но известно, что значения постоянны на протяжении всего времени существования экземпляра.

Это было полезно?

Решение

Как говорили другие, 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 имел разные значения элементов в массиве a, то вы совершаете ошибку, пытаясь с самого начала сделать массив константным.Вы должны просто делать это:

#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]; }
}

Поскольку a::privateB является частным, он фактически является константой вне a::, и вы можете получить к нему доступ аналогично массиву, например

a aobj(2,3);    // initialize "constant array" b[]
n = aobj.b(1);  // read b[1] (write impossible from here)

Если вы готовы использовать пару классов, вы можете дополнительно защитить PrivateB от функций-членов.Это можно сделать, унаследовав a;но я думаю, что предпочитаю Сообщение Джона Харрисона о comp.lang.c++ с использованием константного класса.

Интересно, что в C# у вас есть ключевое слово const, которое преобразуется в статическую константу C++, в отличие от readonly, которое может быть установлено только в конструкторах и инициализациях, даже неконстантами, например:

readonly DateTime a = DateTime.Now;

Я согласен, если у вас есть предопределенный константный массив, вы можете сделать его статическим.На этом этапе вы можете использовать этот интересный синтаксис:

//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 на случай изменения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top