Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Данные члены и управление доступом к элементам классов.



  • Данные-члены - это набор взаимосвязанной информации, возможно различных типов, объединенной в один объект.
  • Данные-члены могут находиться в закрытой (private), защищенной (protected) или открытой (public) части класса.
  • Данные-члены могут иметь статический класс памяти (static). Поля, имеющие статический класс памяти, совместно используются всеми объектами класса. К ним возможен доступ через имя класса ( с использованием операции разрешения доступа), а не через контекстный объект класса. Статические поля могут быть инициализированы; если нет, то инициализируется значением ноль.
  • Данные-члены могут быть объявлены как const. Константные данные должны быть инициализированы в каждом определении конструктора. Имена полей и начальные значения заключаются в скобки, отделяются от списка аргументов конструктора двоеточием.
  • Данные-члены могут быть переменными другого класса. В этом случае требуется явная инициализация, если только поля не имеют конструктора по умолчанию.

Способ создания и инициализация

Рассмотрим ряд моментов связанных с созданием и уничтожением. \

Первый это стек и куча. Вопрос о выделении памяти через стек или кучу связан с тем как выделяется и освобождается память. Разделяют автоматические и динамические переменные. Для автоматических переменных память создается при входе в процедуру или блок, управляющие этими переменными. При выходе из блока память (автоматически) освобождается. Многие языки используют термин статический (static) для обозначения переменных, автоматически размещаемых в стеке. (В данном случае речь не идет о переменных типа static в С++). Для динамического выделения памяти используется оператор new. Память выделяется по специальному запросу со стороны пользователя.

Автоматические переменные уничтожаются автоматически, динамические по запросу со стороны пользователя ключевым словом delete в С++. В Java нет освобождения памяти, осуществляется процесс сборки мусора – автоматически отслеживается ситуация, когда к объекту нет доступа.

Указатели – это эффективное средство работы с динамической информацией. В языке Java объекты представлены внутренним образом как указатели, но при этом никогда не используются в таком качестве.

Создание и инициализация в С++. Конструкторы.

Использование для обеспечения инициализации объекта класса функций вроде set_date() (установить дату) неэлегантно и чревато ошибками. Поскольку нигде не утверждается, что объект должен быть инициализирован, то программист может забыть это сделать, или (что приводит, как правило, к столь же разрушительным последствиям) сделать это дважды. Есть более хороший подход: дать возможность программисту описать функцию, явно предназначенную для инициализации объектов. Поскольку такая функция конструирует значения данного типа, она называется конструктором. Конструктор распознается по тому, что имеет то же имя, что и сам класс. Например:

class date {

date(int, int, int);

};

Метод конструктор автоматически и неявно вызывается каждый раз, когда создается объект, принадлежащий к соответствующему классу. Это происходит или во время объявления переменной или, когда объект создается динамически с помощью оператора new или когда применяются временные переменные.
Часто бывает хорошо обеспечить несколько способов инициализации объекта класса. Это можно сделать, задав несколько конструкторов. Например:

class date {
int month, day, year;
public:

date(int, int, int); // день месяц год
date(char*); // дата в строковом представлении
date(int); // день, месяц и год сегодняшние
date(); // дата по умолчанию: сегодня
};

data:: data(){
month=01, day=22, year=1999;
}

date today(4);
date july4(" Июль 4, 1983" );
date now; // инициализируется по умолчанию
Тело конструктора часто представляет собой последовательность операторов присваивания. Они могут быть заменены инициализаторами в заголовке функции.

Data(int x, int y, int z): month(x), day(y), year(z) {}

Динамическое создание переменной выглядит следующим образом.

Data *d, *g;
D=new data(1, 3, 1987);
G=new data;
Конструкторы подчиняются тем же правилам относительно типов параметров, что и перегруженные функции. Если конструкторы существенно различаются по типам своих параметров, то компилятор при каждом использовании может выбрать правильный.
Размножение конструкторов в примере с date типично. При разработке класса всегда есть соблазн обеспечить " все", поскольку кажется проще обеспечить какое-нибудь средство просто на случай, что оно кому-то понадобится или потому, что оно изящно выглядит, чем решить, что же нужно на самом деле. Последнее требует больших размышлений, но обычно приводит к программам, которые меньше по размеру и более понятны. Один из способов сократить число родственных функций - использовать параметры со значением по умолчанию, пример. В случае date для каждого параметра можно задать значение по умолчанию, интерпретируемое как " по умолчанию принимать: today" (сегодня).

class date {
int month, day, year;
public:
date(int d =0, int m =0, int y =0);
date(char*); // дата в строковом представлении
};

Констукторы и массивы объектов

Чтобы описать вектор объектов класса, имеющего конструктор, этот класс должен иметь конструктор, который может вызываться без списка параметров. Нельзя использовать даже параметры по умолчанию. Например:
table tblvec[10];

будет ошибкой, если нет конструктора без параметров так как для table:: table(int sz=10) требуется целый параметр. Нет способа задать параметры конструктора в описании вектора. Чтобы можно было описывать вектор таблиц table, можно модифицировать описание table, например, так:

class table {
void init(int sz); // как старый конструктор
public:
table(int sz) { init(sz); } // как раньше, но без по умолчанию
table() { init(10); } // по умолчанию
};

Когда вектор уничтожается, деструктор должен вызываться для каждого элемента этого вектора. Для векторов, которые не были размещены с помощью new, это делается неявно. Однако для векторов в свободной памяти это не может быть сделано неявно, поскольку компилятор не может отличить указатель на один объект от указателя на первый элемент вектора объектов. Например:
void f() {
table* t1 = new table;
table* t2 = new table[10];
table* t3 = new table[10];
delete t1; // одна таблица
delete t2; // неприятность: 10 таблиц
delete[] t3;
}
Компилятор не может найти число элементов вектора из объема выделенной памяти, потому, что распределитель свободной памяти не является частью языка и может быть задан программистом.
Конструкторы могут инициализировать массивы объектов класса таким образом, как и массивы встроенных типов. Максимальное число элементов может быть опущенно, если оно равно числу инициализирующих значений. Если максимальное число элементов больше числа значений, то оставшиеся значения инициализируются при помощи конструктора по умолчанию. Если это не конструктор по умолчанию, то все же значения должны быть указаны. Если заданы все значения для данного размера массива, то фигурные скобки при указании списка значений могут быть опущены.

class Phone {
int a, b, c;
public:
Phone(int a1, int b1, int c1): a(a1), b(b1), c(c1) {}
};

Phone office[]= { // Компилятор вычислит размер за нас
900, 800, 905,
678, 456, 546 };
Phone office[3]= { // Требуетс конструктор по умолчанию, которого y Phone нет
890, 790, 343,
238, 279, 564
};

Деструктор

Определяемый пользователем тип чаще имеет, чем не имеет, конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие, деструктор, чтобы обеспечить соответствующую очистку объектов этого типа. Имя деструктора для класса X есть ~X() (" дополнение конструктора" ). В частности, многие типы используют некоторый объем памяти из свободной памяти, который выделяется конструктором и освобождается деструктором. Вот, например, традиционный стековый тип, из которого для краткости полностью выброшена обработка ошибок:

class char_stack {
int size;
char* top, * s;
public:
char_stack(int sz) { top=s=new char[size=sz]; }
~char_stack() { delete []s; } // деструктор
void push(char c) { *(top++) = c; }
char pop() { return *(--top); }
};
Когда char_stack выходит из области видимости, вызывается деструктор:
void f(){
char_stack s1(100), s2(200);
s1.push('a'); s2.push(s1.pop());
char ch = s2.pop();
cout < < char(ch) < < " \n";
}
Когда вызывается f(), конструктор char_stack вызывается для s1, чтобы выделить вектор из 100 символов, и для s2, чтобы выделить вектор из 200 символов. При возврате из f() эти два вектора будут освобождены.

Конструктор копирования

Объект класса без конструкторов можно инициализировать путем присваивания ему другого объекта этого класса. Это можно делать и тогда, когда конструкторы описаны. Например:

date d = today; // инициализация посредством присваивания

По существу, имеется конструктор по умолчанию, определенный как почленная (в ранних версиях С++ - побитовая) копия объекта того же класса. Если для класса X такой конструктор по умолчанию нежелателен, его можно переопределить конструктором с именем X(X& ).
Семантика вызова по значению требует, чтобы локальная копия типа параметра создавалась и инициализировалась от значения выражения, переданного как фактический параметр. Для этого необходим конструктор копии. Компилятор предоставляет такой конструктор по умолчанию. Его сигнатура

stack:: stack(const stack& );

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

Stack:: stack(const stack& str) {
s=new char [str.max_len]; max_len=str.max__len; top=str.top;
memcpy(s, str.s, max_len);
}

Конструктор копирования вызывается при возвращении значения объекта из функции
stack f(stack r) // вызов конструктора копирования при создании копии
{
return r; // вызов конструктора копирования для создании копии при возвращении
}
Конструктор копирования вызывается в случае инициализации при объявлении.
Stack s(10); // конструктор с одним параметром
Stack d=s; // конструктор копирования.
Все хорошо разработанные классы должны иметь конструктор копирования, особенно те, которые имеют дело с динамической памятью, открытыми файлами, любыми указателями.

Резюме

В С++ существуют специальные функции-члены класса, которые определяют как объекты класса создаются, инициализируются, копируются и уничтожаются. Важнейшие из них - конструкторы и деструкторы. Им свойственны многие черты обычных функций-членов класса, но существуют и свои особенности:

  • Конструкторы и деструкторы не могут описываться как функции, возвращающие значение (даже типа void).
  • Конструкторы могут иметь аргументы. В качестве параметров могут быть элементы, получающие значения по умолчанию. Деструктор не имеет параметров.
  • Имя конструктора совпадает с именем класса. Имя деструктора - имя класса, которому предшествует символ ~.
  • Нельзя получить адрес конструктора или деструктора.
  • Если они явно не описаны, то конструкторы и деструкторы автоматически создаются компилятором.
  • Конструктор не может быть вызван как обычная функция. Дестуктор может быть вызван как обычная функция с указанием полного имени.

Int s(100), *ps=new Int(50);
s.~Int(); // явное разрушение s
this-> ~Int(); // Явное разрушение *this (may be error this=ps? )

  • Компилятор автоматически вставляетвызовы конструктора и деструктора при описании и уничтожении объекта.
  • Конструкторы могут быть и в закрытой, и в защищенной, и в открытой части класса. Если конструктор находится в закрытой части класса, то объект этого класса с использованием такого конструктора могут создавать только те, кто имеет доступ к закрытой части.
  • Деструктор автоматически вызывается компилятором:
    • при выходе из области видимости;
    • при создании временных объектов для преобразования в выражениях или для передачи функциям аргументов;
    • когда возвращенные функцией значения более не нужны
    • при выполнении операции delete для объектов, размещенных в динамической памяти.
  • Конструктор не может быть ни const, ни volatile, ни static, ни virtual.
  • Конструктор может быть вызван тремя эквивалентными способами (пользователю предоставляется право выбора):

Int good(100);
Int bad=Int(100);
Int ff=100;

  • Конструкторы используются для инициализации константных полей и полей, которые являются переменными класса. Константное поле должно быть инициализировано в каждом определении конструктора. Классы с константными полями должны иметь по крайне мере один конструктор.

class Int {
const int size;
public:
Int(int i): size(i) { }
};
Если у класса есть конструктор, то он вызывается всегда, когда создается объект класса. Если у класса есть деструктор, то он вызывается всегда, когда объект класса уничтожается. Объекты могут создаваться как:

  • Автоматический объект: создается каждый раз, когда его описание встречается при выполнении программы, и уничтожается каждый раз при выходе из блока, в котором оно появилось;
  • Статический объект: создается один раз, при запуске программы, и уничтожается один раз, при ее завершении;
  • Объект в свободной памяти: создается с помощью операции new и уничтожается с помощью операции delete;
  • Объект член: как объект другого класса или как элемент вектора.

Примет класса - Строка

#include < string.h>
class StringHolder {
char *contens;
public:
StringHolder(char *aString=0);
~StringHolder(void);
char *getContens(void);
void setContens(char *aString);
};
StringHolder:: StringHolder(char *aString) {
if(aString) { contens=new char[strlen(aString)+1]; strcpy(contens, aString); }
else {contens=new char[1]; *contens=‘\0’; }
}
StringHolder:: ~StringHolder(void) { delete contens; }
char *StringHolder:: getContens(void) { return contens; }
void StringHolder:: setContens(char *aString) {
delete contens;
contens=new char[strlen(aString)+1]; strcpy(contens, aString);
}
void main()
{
StringHolder str1, str2(“Object2”), *str3, *str4;
str3=new StringHolder(“object3”);
str4=new StringHolder;
cout < < str1.getContens(); cout < < str2.getContens();
cout < < str3-> getContens(); cout < < str4-> getContens();
}

 

Задания на лабораторные работы


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-07-14; Просмотров: 668; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.022 с.)
Главная | Случайная страница | Обратная связь