Можно ли создавать в классе массив?
От: TARGRED  
Дата: 15.02.09 15:54
Оценка:
Здравствуйте, подскажите пожалуйста.
Почему когда массив обьявляю и провожу инициализацию компилятор выдает ерор, а когда убираю класс и оставляю один массив, то все компилируется хорошо.Спасибо


Преблизительный код


 ///2059 sintax eror :}
class f
{
   int i [] = {1,2,3,4};
};

int main (....)
{
}



//Работает нормально
int i [] = {1,2,3,4};
int main (....)
{
}
Re: Можно ли создавать в классе массив?
От: _Ursus_  
Дата: 15.02.09 16:47
Оценка: +1
Здравствуйте, TARGRED, Вы писали:

TAR>Почему когда массив обьявляю и провожу инициализацию компилятор выдает ерор, а когда убираю класс и оставляю один массив, то все компилируется хорошо.Спасибо


Шо значит "почему"?? Потому шо синтаксис языка запрещает такие вещи
Re[2]: Можно ли создавать в классе массив?
От: Аноним  
Дата: 15.02.09 17:08
Оценка:
Здравствуйте, _Ursus_, Вы писали:

_U_>Здравствуйте, TARGRED, Вы писали:


TAR>>Почему когда массив обьявляю и провожу инициализацию компилятор выдает ерор, а когда убираю класс и оставляю один массив, то все компилируется хорошо.Спасибо


_U_>Шо значит "почему"?? Потому шо синтаксис языка запрещает такие вещи




То есть массив можно инициализировать только в функции класса, а в классе только обьявить?
Re[3]: Можно ли создавать в классе массив?
От: Alexander G Украина  
Дата: 15.02.09 17:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>То есть массив можно инициализировать только в функции класса, а в классе только обьявить?


Кроме статического члена класса, массив-член класс не может иметь инициализатор через фигурные скобки.
Русский военный корабль идёт ко дну!
Re[4]: Можно ли создавать в классе массив?
От: Аноним  
Дата: 15.02.09 17:40
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, Аноним, Вы писали:


А>>То есть массив можно инициализировать только в функции класса, а в классе только обьявить?


AG>Кроме статического члена класса, массив-член класс не может иметь инициализатор через фигурные скобки.


В какой книге это написанно?
Re[5]: Можно ли создавать в классе массив?
От: Alex Dav Россия  
Дата: 15.02.09 18:45
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Alexander G, Вы писали:


AG>>Здравствуйте, Аноним, Вы писали:


А>>>То есть массив можно инициализировать только в функции класса, а в классе только обьявить?


AG>>Кроме статического члена класса, массив-член класс не может иметь инициализатор через фигурные скобки.


А>В какой книге это написанно?


просто несколько некорректно высказались — нельзя инициализировать переменые подобным образом, если они не объявлены как static. Т.е. массив надо объявить и инициализировать его позже в конструкторе.
Re[5]: Можно ли создавать в классе массив?
От: _Ursus_  
Дата: 15.02.09 20:42
Оценка: +4 :)
Здравствуйте, Аноним, Вы писали:

А>В какой книге это написанно?


Книга называется "Стандарт языка С++"
Re: Можно ли создавать в классе массив?
От: alzt  
Дата: 16.02.09 08:19
Оценка:
Здравствуйте, TARGRED, Вы писали:

class My
{
   int x = 7; //тоже не работает
};


Может и удобно так писать, но стандарт С++ запрещает.
Re[5]: Можно ли создавать в классе массив?
От: LaptevVV Россия  
Дата: 16.02.09 10:07
Оценка:
Здравствуйте, Аноним, Вы писали:

AG>>Кроме статического члена класса, массив-член класс не может иметь инициализатор через фигурные скобки.

А>В какой книге это написанно?
Вот:

Поля-массивы в классе
В С++ нет запрета на объявление в классе поля-массива. Естественно, размер класса с полем-массивом увеличивается на размер массива. Однако использование массива в классе недостаточно хорошо отражено в стандарте. Это вызывает многочисленные вопросы, особенно связанные с инициализацией. Мы начнем разбираться с этими вопросами на самом простом примере, в котором объявляются и инициализируются несколько полей-массивов (листинг 4.1).
Листинг 4.1. Поля-массивы в классе

class Arrays
{    int m0[10];
    static const unsigned int k = 10;
    enum { n = 10 };
    int m1[k];
    int m2[n];
    public: 
        Arrays()                         // конструктор
        { for (int i = 0; i < k; ++i)
            m0[i]=m1[i]=m2[i]=0; 
        }
};

В классе Arrays объявлено три поля-массива: m0, m1 и m2. Как видим, задать количество элементов массива можно либо явной константой, либо константным выражением со статическими и (или) перечислимыми константами, причем константы должны быть определены раньше массива. Задавать количество элементов поля-массива обязательно.
Нельзя задать количество элементов как значение другого поля, заполняемого конструктором, даже если это поле константное. Например, пусть в классе объявлены следующие поля:
const int k;
int m[k];

В этом случае система Visual C++.NET 2003 выдает ошибку компиляции C2327, которая сигнализирует о том, что k в данном случае не является ни статической константой, ни константой перечислимого типа.
Конструктор без аргументов Array() обнуляет массивы, выполняя цикл в теле. Такой способ инициализации поля-массива — в теле конструктора — является наиболее простым и позволяет присваивать элементам массива любые значения. Однако наиболее часто выполняется обнуление, поэтому для массивов разрешается применять инициализацию нулем. Тогда наш конструктор выглядит значительно проще:
Arrays(): m0(), m1(), m2() {}

Для массива объектов некоторого типа такая запись означает вызов конструктора по умолчанию (без аргументов), и не забудьте, что этот вызов выполняется для каждого элемента массива.
К сожалению, применение списков инициализации для полей-массивов этим и ограничивается — в скобках нельзя ничего указывать. Например, при написании следующей конструкции система Visual C++.NET 2003 выдаст сообщение об ошибке С2536, сигнализируя о том, что явная инициализация массивов запрещена:
Arrays(): m0(1,2,3,4,5), m1(), m2() {}

Попытки написать в скобках некоторое одиночное выражение (например, вызов функции, заполняющей массив) компилятор отвергает по той же причине.
Явная инициализация не проходит даже для символьных массивов. Например, объявим в классе Arrays символьный массив:
char s[4];

После этого попытаемся в списке инициализации конструктора присвоить этому массиву символьную константу:
Arrays(): m0(), m1(), m2(), s("abc") {}

Тогда в системе Visual C++.NET 2003 мы получим сообщение об ошибке:
error C2536: 'Arrays::Arrays::s' :
cannot specify explicit initializer for arrays

Это сообщение говорит о том, что нельзя определять явный инициализатор для массивов.
Тем не менее, можно задать в классе указатель на символ:
char *s;

Затем можно инициализировать указатель символьной константой в списке инициализации конструктора, как было показано ранее.
Как это ни странно, но большие проблемы возникают при попытках объявить в классе константный массив встроенного типа! Как мы уже выяснили, константы нельзя инициализировать в теле конструктора, значения им присваиваются только в списке инициализации конструктора. Однако для константного массива встроенного типа не работает даже инициализация нулем.

ПРИМЕЧАНИЕ
Этот вопрос практически не отражен в стандарте, поэтому компиляторы ведут себя по-разному. В системе Visual C++.NET 2003 выдается ошибка компиляции C2439, а Borland C++ Builder 6 выдает только предупреждение W8038 о том, что массив не инициализируется.

Не проходит и отмена константности. Например, зададим массив m0 как константный, а в теле конструктора определим инициализацию в цикле:
for (int i = 0; i < 10; ++i) 
    const_cast<int>(m0[i]) = 0;

Однако и Visual C++.NET 2003, и Borland C++ Builder 6 отказываются компилировать такой цикл.
Но для константного массива из объектов не встроенного типа задавать инициализацию нулем разрешается. Для этого в классе должен быть определен конструктор без аргументов, который вызывается для инициализации каждого элемента константного поля-массива. Например, вполне можно инициализировать константный массив денег:
const TMoney ss[10];
Для этого достаточно задать в списке инициализации конструктора инициализацию нулем ss(). Как реально инициализируется такой массив, конечно, зависит от реализации конструктора по умолчанию.

Статические поля-массивы
Методы перевода перевода регистра в классе, реализующем строки, должны обеспечивать перевод и русских, и английских букв. Простейшая реализация методов преобразования в верхний и нижний регистры может быть такой, как в листинге 4.19. Поскольку перевод регистра — разовое действие, то пусть обе операции возвращают значение.
Листинг 4.19. Простейшая реализация методов изменения регистра строки
TString TString::operator++()    // toUpper
{ const char lower[59] 
      = "abcdefghijklmnopqrstuvwxyzабвгдежзийклмнопрстуфхцчшщъыьэюя";
  const char upper[59] 
      = "ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"; 
  for(int i = 0; i<this->Length(); i++)
  { for (int j = 0; j < 59; j++) if (s[i] == lower[j]) s[i]=upper[j]; }
  return *this;
}
TString TString::operator--()    // toLower
{ const char lower[59] 
    = "abcdefghijklmnopqrstuvwxyzабвгдежзийклмнопрстуфхцчшщъыьэюя";
  const char upper[59] 
    = "ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"; 
   for(int i = 0; i<this->Length(); i++)
  { for (int j = 0; j < 59; j++) if (s[i] == upper[j]) s[i]=lower[j]; }
  return *this;
}

Методы прекрасно работают, однако таком виде они нас, конечно, не устраивают — любой программист тут же скажет, что использование локальных массивов очень неэффективно. Массивы заново создаются при каждом входе в функцию — на это тратится и время и место при выполнении программы.
Первое побуждение — вынести массивы из методов и объявить два поля-массива. Но тут нас ожидают некоторые сложности. Во-первых, поля нельзя объявить константными, хотя по сути своей они таковыми и являются. Дело в том, что тогда мы не сможем эти поля инициализировать — константные массивы встроенного типа никаким образом не инициализируются . Но если массивы не константные, то их придется заполнять в каждом конструкторе, написав для этого приватную функцию.

Во-вторых, и это гораздо важнее, неконстантные поля-массивы существенно увеличивают размер класса — на 118 байт. Пока объектов немного, это не доставляет беспокойства. Но представьте, что нам надо объявить массив размером в 1000 элементов типа TString. Ситуация отнюдь не надуманная, так как подобный массив, например, в качестве буфера может использовать простой текстовый редактор. Но такой массив имеет уже 118 000 лишних байтов, которые еще и заполняются во время создания — ведь конструктор вызывается для каждого элемента массива. Ситуация совершенно неприемлемая как с точки зрения расхода памяти, так и с точки зрения эффективного выполнения программы.

Так как массивы у нас символьные, то можно было бы обойтись указателями на символ — тем более, что их можно инициализировать символьной константой. Ситуация с памятью несколько улучшается: два указателя занимают в классе всего 8 байт. Однако указатели тоже будут инициализироваться для каждого создаваемого объекта.

Массивы нужно все-таки иметь в единственном экземпляре; они, очевидно, должны быть объявлены константными и проинициализированы один раз при объявлении. Мы можем это сделать, если объявим их глобальными. Однако такое решение совершенно неприемлемо с точки зрения инкапсуляции — глобальные переменные излишне доступны и их использование способствует появлению трудноуловимых ошибок. Массивы по сути своей являются (и должны быть) частью класса TString.

В языке С++ есть средство, позволяющее сделать именно то, что нам требуется: объявить массивы в единственном экземпляре, сделать их константными, проинициализировать при объявлении и, тем не менее, локализовать их в классе. Такие константные поля-массивы надо объявить в классе статическими (см. п.п. 9.4.2 в [1]):
static const char lower[59];        // маленикие буквы
static const char upper[59];        // БОЛЬШИЕ БУКВЫ

Инициализация (определение) статических полей выполняется вне класса:
const char TString::lower[59] = "abcdefghijklmnopqrstuvwxyzабвгдежзийклмнопрстуфхцчшщъыьэюя";
const char TString::upper[59] = "ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";

Этот оператор присваивания является определением статического члена класса (а в классе — только объявление). Такой оператор присваивания — единственная форма инициализации константных статических массивов (и любых других константных статических полей встроенного нецелочисленного типа). Обратите внимание, что слово static в операторе инициализации отсутствует — его там писать нельзя. Статические массивы, как и статические константы (и вообще любые статические поля), являются частью класса, создаются в единственном экземпляре при запуске программы (до создания любых объектов класса), поэтому их нельзя инициализировать в конструкторе.

ПРИМЕЧАНИЕ
Помимо статических полей, в классе можно объявлять и статические методы. Такие методы являются методами класса и могут вызываться до создания первого объекта этого класса.

Cтатические константы места в классе не занимают. Точно так же не увеличивают размер класса и любые статические поля — они хранятся вне класса. Но принцип инкапсуляции выполняется — доступ к таким полям имеют только методы и «друзья» класса. Наши методы перевода регистра упрощаются (листинг 4.20).
Листинг 4.20. Реализация методов перевода регистра
TString TString::operator++()            // toUpper
{ for(int i = 0; i < this->Length(); i++)
  { for (int j = 0; j < 59; j++) if (s[i] == lower[j]) s[i]=upper[j]; }
  return *this;
}
TString TString::operator--()            // toLower
{ for(int i = 0; i < this->Length(); i++)
  { for (int j = 0; j < 59; j++) if (s[i] == upper[j]) s[i]=lower[j]; }
  return *this;
}

Проверим работу методов.
TString t ("Мама мыла Милу");    std::cout << t << std::endl;
++t;    std::cout << t << std::endl;
--t;    std::cout << t << std::endl;
TString b = "abCDefGH";  std::cout << b << std::endl;
++b; std::cout << b << std::endl;
--b; std::cout << b << std::endl;

При выполнении этого фрагмента на экране появятся следующие строки:
Мама мыла Милу
МАМА МЫЛА МИЛУ
мама мыла милу
abCDefGH
ABCDEFGH
abcdefgh

Как видим, все работает правильно.

Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[6]: Можно ли создавать в классе массив?
От: Alexey F  
Дата: 16.02.09 10:27
Оценка: 18 (1)
Здравствуйте, LaptevVV, Вы писали:

LVV>

LVV>Этот вопрос практически не отражен в стандарте, поэтому компиляторы ведут себя по-разному. В системе Visual C++.NET 2003 выдается ошибка компиляции C2439, а Borland C++ Builder 6 выдает только предупреждение W8038 о том, что массив не инициализируется.

LVV>Не проходит и отмена константности. Например, зададим массив m0 как константный, а в теле конструктора определим инициализацию в цикле:
LVV>

for (int i = 0; i < 10; ++i) 
LVV>    const_cast<int>(m0[i]) = 0;

LVV>Однако и Visual C++.NET 2003, и Borland C++ Builder 6 отказываются компилировать такой цикл.


Я чего-то не понимаю, или Вы просто опечатались в
int&
? Если да, то такой код бы скомпилировался... Правда, опасность его не уменьшилась бы
Re[7]: Можно ли создавать в классе массив?
От: LaptevVV Россия  
Дата: 16.02.09 10:39
Оценка:
Здравствуйте, Alexey F, Вы писали:

AF>Здравствуйте, LaptevVV, Вы писали:


LVV>>

LVV>>Этот вопрос практически не отражен в стандарте, поэтому компиляторы ведут себя по-разному. В системе Visual C++.NET 2003 выдается ошибка компиляции C2439, а Borland C++ Builder 6 выдает только предупреждение W8038 о том, что массив не инициализируется.

LVV>>Не проходит и отмена константности. Например, зададим массив m0 как константный, а в теле конструктора определим инициализацию в цикле:
LVV>>

for (int i = 0; i < 10; ++i) 
LVV>>    const_cast<int>(m0[i]) = 0;

LVV>>Однако и Visual C++.NET 2003, и Borland C++ Builder 6 отказываются компилировать такой цикл.


AF> Я чего-то не понимаю, или Вы просто опечатались в
int&
? Если да, то такой код бы скомпилировался... Правда, опасность его не уменьшилась бы

Спасибо!
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.