Самый простой метод – это объявление переменных внутри функций.
При создании
int
funct()
{
double f; // значение f не определено
f = 1.2;
// теперь значение f определено
// явная инициализация автоматической
// переменной
bool result = true;
. . .
}
Аналогично
Замечание. Распространенной ошибкой является использование
int*
func()
{
int x;
. . .
return х;
}
дает непредсказуемый результат.
Другой способ выделения памяти –
Если переменная определена вне функции, память для нее отводится статически, один раз в начале выполнения программы, и переменная уничтожается только тогда, когда выполнение программы завершается. Можно статически выделить память и под переменную, определенную внутри функции или блока. Для этого нужно использовать ключевое слово static в его определении:
double globalMax;
// переменная определена вне функции
void
func(int x)
{
static bool visited = false;
if (!visited) {
. . . // инициализация
visited = true;
}
. . .
}
В данном примере переменная visited задается в начале выполнения программы. Ее начальное значение – false. При первом вызове функции func условие в операторе if будет истинным, выполнится инициализация, и переменной visited будет присвоено значение true. Поскольку func инициализация производиться не будет.
Если бы переменная visited не была объявлена static, то инициализация происходила бы при каждом вызове функции.
Третий способ выделения памяти в языке Си++ – new. В качестве операнда выступает название типа, а результатом является
long* lp; // создать новое целое число lp = new long; Complex* cp; // создать новый объект типа Complex cp = new Complex;
Созданный таким образом объект существует до тех пор, пока память не будет явно освобождена с помощью delete. В качестве операнда delete должен быть задан new:
delete lp; delete cp;
Если необходимо динамически создать массив, то нужно использовать немного другую форму new:
new int[100];
В отличие от определения переменной типа массив, размер массива в операции new может быть произвольным, в том числе вычисляемым в ходе выполнения программы. (Напомним, что при объявлении переменной типа массив размер массива должен быть константой.)
Освобождение памяти, выделенной под массив, должно быть выполнено с помощью следующей операции delete
delete [] address;
В следующем фрагменте программы мы
// стандартная функция strlen подсчитывает // количество символов в строке int length = strlen(src_str); // выделить память и добавить один байт // для завершающего нулевого байта char* buffer = new char[length + 1]; strcpy(buffer, src_str); // копирование строки
Операция new возвращает new обязательно завершится успешно. Объем оперативной памяти ограничен, и может случиться так, что найти еще один участок свободной памяти будет невозможно. В таком случае new возвращает нулевой new необходимо проверять:
char* newstr;
size_t length = 4;
newstr = new (std::nothrow) char[length];
if (newstr == NULL) { // проверить результат
// обработка ошибок
}
// память выделена успешно
Приведем несколько примеров.
Использование неверного delete. Результат такой операции непредсказуем. Вполне возможно, что сама операция пройдет успешно, однако внутренняя структура памяти будет испорчена, что приведет либо к ошибке в следующей операции new, либо к порче какой-нибудь информации.
Пропущенное освобождение памяти, т.е. программа многократно выделяет память под данные, но "забывает" ее освобождать. Такие ошибки называют утечками памяти. Во-первых, программа использует ненужную ей память, тем самым понижая производительность. Кроме того, вполне возможно, что в 99 случаях из 100 программа будет успешно выполнена. Однако если
Запись по неверному
Примеры ошибок можно приводить бесконечно. Общие их черты, обуславливающие сложность обнаружения, это, во-первых, непредсказуемость результата и, во-вторых, проявление не в момент совершения ошибки, а позже, быть может, в том месте программы, которое само по себе не содержит ошибки (неверная операция delete – сбой в последующей операции new, запись по неверному
Отнюдь не призывая отказаться от применения указателей (впрочем, в Си++ это практически невозможно), мы хотим подчеркнуть, что их использование требует внимания и дисциплины. Несколько общих рекомендаций.
Complex x;
то можно определить x как
Complex y = x;
и тогда x и y обозначают одну и ту же величину. Если выполнены операторы
x.real = 1; x.imaginary = 2;
то y.real равно 1 и y.imaginary равно 2. Фактически, -- знак операции взятия
Во-первых, определяя переменную типа
int xref;
можно только
int xref = x;
Во-вторых, нельзя переопределить мы выполним присваивание
xref = y;
то выполнится присваивание значения переменной y той переменной, на которую ссылается . по-прежнему будет ссылаться на x. В результате выполнения следующего фрагмента программы:
int x = 10; int y = 20; int xref = x; xref = y; x += 2; cout << "x = " << x << endl; cout << "y = " << y << endl; cout << "xref = " << xref << endl;
будет выведено:
x = 22 y = 20 xref = 22
В-третьих, синтаксически обращение к
Complex a; Complex* aptr = a; Complex aref = a; aptr->real = 1; aref.imaginary = 2;
Как и
Рассказывая о функциях, мы отметили, что у функций (как и у методов классов) есть
Рассмотрим более подробно метод Add класса Complex. Изменим его немного, так, чтобы он вместо изменения состояния объекта возвращал результат операции сложения:
Complex
Complex::Add(Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
При вызове этого метода
Complex n1; Complex n2; . . . Complex n3 = n1.Add(n2);
значение переменной n2 передается в качестве Complex, копирует в нее значение n2 и передает эту переменную в метод Add. Такая передача Add был бы определен как
Complex
Complex::Add(Complex x)
{
Complex result;
x.imaginary = 0;
// изменение аргумента метода
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
то при вызове n3 = n1.Add(n2) результат был бы, конечно, другой, но значение переменной n2 не изменилось бы. Хотя в данном примере изменяется значение аргумента метода Add, этот n2, а не сам объект. По завершении выполнения метода Add его
При возврате результата функции выполняются те же действия, т.е. создается временная переменная, в которую копируется результат, и уже затем значение временной переменной копируется в переменную n3. Временные переменные потому и называют временными, что компилятор сам создает их на время выполнения метода и сам их уничтожает.
Другим способом передачи Add на
Complex
Complex::Add(Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
то при вызове n3 = n1.Add(n2) компилятор будет создавать n2 и передавать ее методу Add. В большинстве случаев это намного эффективнее, так как для
Complex
Complex::Add(Complex x)
{
Complex result;
x.imaginary = 0; // изменение значения
// по переданной ссылке
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
изменит значение переменной n2. Операция Add не предусматривает изменения собственных операндов. Чтобы избежать ошибок, лучше записать const, который определяет соответствующую переменную как неизменяемую.
Complex::Add(const Complex x)
В таком случае попытка изменить значение Coord класса Figure записывает координаты некой фигуры в свои
void
Figure::Coord(int x, int y)
{
x = coordx;
y = coordy;
}
При вызове
int cx, cy; Figure fig; . . . fig.Coord(cx, cy);
переменным cx и cy будет присвоено значение координат фигуры fig.
Вернемся к методу Add и попытаемся оптимизировать передачу вычисленного значения. Простое на первый взгляд решение возвращать
Complex
Complex::Add(const Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
При выходе из метода result уничтожается, и память, выделенная для нее, освобождается. Поэтому результат Add – Add прибавляет значение
Complex
Complex::Add(const Complex x)
{
real += x.real;
imaginary += x.imaginary;
return *this;
// передать ссылку на текущий объект
}
Как и в случае с Add слева от операции присваивания, например в следующем выражении:
x.Add(y) = z;
К значению объекта x прибавляется значение y, а затем результату присваивается значение z (фактически это эквивалентно x = z ). Чтобы запретить подобные конструкции, достаточно добавить описатель const перед типом возвращаемого значения:
const Complex Complex::Add(const Complex x) . . .
Передача
Complex*
Complex::Add(Complex* x)
{
real += x->real;
imaginary += x->imaginary;
return this;
}
Если нет особых оснований использовать в качестве
Самый простой метод – это объявление переменных внутри функций.
При создании
int
funct()
{
double f; // значение f не определено
f = 1.2;
// теперь значение f определено
// явная инициализация автоматической
// переменной
bool result = true;
. . .
}
Аналогично
Замечание. Распространенной ошибкой является использование
int*
func()
{
int x;
. . .
return х;
}
дает непредсказуемый результат.
Другой способ выделения памяти –
Если переменная определена вне функции, память для нее отводится статически, один раз в начале выполнения программы, и переменная уничтожается только тогда, когда выполнение программы завершается. Можно статически выделить память и под переменную, определенную внутри функции или блока. Для этого нужно использовать ключевое слово static в его определении:
double globalMax;
// переменная определена вне функции
void
func(int x)
{
static bool visited = false;
if (!visited) {
. . . // инициализация
visited = true;
}
. . .
}
В данном примере переменная visited задается в начале выполнения программы. Ее начальное значение – false. При первом вызове функции func условие в операторе if будет истинным, выполнится инициализация, и переменной visited будет присвоено значение true. Поскольку func инициализация производиться не будет.
Если бы переменная visited не была объявлена static, то инициализация происходила бы при каждом вызове функции.
Третий способ выделения памяти в языке Си++ – new. В качестве операнда выступает название типа, а результатом является
long* lp; // создать новое целое число lp = new long; Complex* cp; // создать новый объект типа Complex cp = new Complex;
Созданный таким образом объект существует до тех пор, пока память не будет явно освобождена с помощью delete. В качестве операнда delete должен быть задан new:
delete lp; delete cp;
Если необходимо динамически создать массив, то нужно использовать немного другую форму new:
new int[100];
В отличие от определения переменной типа массив, размер массива в операции new может быть произвольным, в том числе вычисляемым в ходе выполнения программы. (Напомним, что при объявлении переменной типа массив размер массива должен быть константой.)
Освобождение памяти, выделенной под массив, должно быть выполнено с помощью следующей операции delete
delete [] address;
В следующем фрагменте программы мы
// стандартная функция strlen подсчитывает // количество символов в строке int length = strlen(src_str); // выделить память и добавить один байт // для завершающего нулевого байта char* buffer = new char[length + 1]; strcpy(buffer, src_str); // копирование строки
Операция new возвращает new обязательно завершится успешно. Объем оперативной памяти ограничен, и может случиться так, что найти еще один участок свободной памяти будет невозможно. В таком случае new возвращает нулевой new необходимо проверять:
char* newstr;
size_t length = 4;
newstr = new (std::nothrow) char[length];
if (newstr == NULL) { // проверить результат
// обработка ошибок
}
// память выделена успешно
Приведем несколько примеров.
Использование неверного delete. Результат такой операции непредсказуем. Вполне возможно, что сама операция пройдет успешно, однако внутренняя структура памяти будет испорчена, что приведет либо к ошибке в следующей операции new, либо к порче какой-нибудь информации.
Пропущенное освобождение памяти, т.е. программа многократно выделяет память под данные, но "забывает" ее освобождать. Такие ошибки называют утечками памяти. Во-первых, программа использует ненужную ей память, тем самым понижая производительность. Кроме того, вполне возможно, что в 99 случаях из 100 программа будет успешно выполнена. Однако если
Запись по неверному
Примеры ошибок можно приводить бесконечно. Общие их черты, обуславливающие сложность обнаружения, это, во-первых, непредсказуемость результата и, во-вторых, проявление не в момент совершения ошибки, а позже, быть может, в том месте программы, которое само по себе не содержит ошибки (неверная операция delete – сбой в последующей операции new, запись по неверному
Отнюдь не призывая отказаться от применения указателей (впрочем, в Си++ это практически невозможно), мы хотим подчеркнуть, что их использование требует внимания и дисциплины. Несколько общих рекомендаций.
Complex x;
то можно определить x как
Complex y = x;
и тогда x и y обозначают одну и ту же величину. Если выполнены операторы
x.real = 1; x.imaginary = 2;
то y.real равно 1 и y.imaginary равно 2. Фактически, -- знак операции взятия
Во-первых, определяя переменную типа
int xref;
можно только
int xref = x;
Во-вторых, нельзя переопределить мы выполним присваивание
xref = y;
то выполнится присваивание значения переменной y той переменной, на которую ссылается . по-прежнему будет ссылаться на x. В результате выполнения следующего фрагмента программы:
int x = 10; int y = 20; int xref = x; xref = y; x += 2; cout << "x = " << x << endl; cout << "y = " << y << endl; cout << "xref = " << xref << endl;
будет выведено:
x = 22 y = 20 xref = 22
В-третьих, синтаксически обращение к
Complex a; Complex* aptr = a; Complex aref = a; aptr->real = 1; aref.imaginary = 2;
Как и
Рассказывая о функциях, мы отметили, что у функций (как и у методов классов) есть
Рассмотрим более подробно метод Add класса Complex. Изменим его немного, так, чтобы он вместо изменения состояния объекта возвращал результат операции сложения:
Complex
Complex::Add(Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
При вызове этого метода
Complex n1; Complex n2; . . . Complex n3 = n1.Add(n2);
значение переменной n2 передается в качестве Complex, копирует в нее значение n2 и передает эту переменную в метод Add. Такая передача Add был бы определен как
Complex
Complex::Add(Complex x)
{
Complex result;
x.imaginary = 0;
// изменение аргумента метода
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
то при вызове n3 = n1.Add(n2) результат был бы, конечно, другой, но значение переменной n2 не изменилось бы. Хотя в данном примере изменяется значение аргумента метода Add, этот n2, а не сам объект. По завершении выполнения метода Add его
При возврате результата функции выполняются те же действия, т.е. создается временная переменная, в которую копируется результат, и уже затем значение временной переменной копируется в переменную n3. Временные переменные потому и называют временными, что компилятор сам создает их на время выполнения метода и сам их уничтожает.
Другим способом передачи Add на
Complex
Complex::Add(Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
то при вызове n3 = n1.Add(n2) компилятор будет создавать n2 и передавать ее методу Add. В большинстве случаев это намного эффективнее, так как для
Complex
Complex::Add(Complex x)
{
Complex result;
x.imaginary = 0; // изменение значения
// по переданной ссылке
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
изменит значение переменной n2. Операция Add не предусматривает изменения собственных операндов. Чтобы избежать ошибок, лучше записать const, который определяет соответствующую переменную как неизменяемую.
Complex::Add(const Complex x)
В таком случае попытка изменить значение Coord класса Figure записывает координаты некой фигуры в свои
void
Figure::Coord(int x, int y)
{
x = coordx;
y = coordy;
}
При вызове
int cx, cy; Figure fig; . . . fig.Coord(cx, cy);
переменным cx и cy будет присвоено значение координат фигуры fig.
Вернемся к методу Add и попытаемся оптимизировать передачу вычисленного значения. Простое на первый взгляд решение возвращать
Complex
Complex::Add(const Complex x)
{
Complex result;
result.real = real + x.real;
result.imaginary = imaginary + x.imaginary;
return result;
}
При выходе из метода result уничтожается, и память, выделенная для нее, освобождается. Поэтому результат Add – Add прибавляет значение
Complex
Complex::Add(const Complex x)
{
real += x.real;
imaginary += x.imaginary;
return *this;
// передать ссылку на текущий объект
}
Как и в случае с Add слева от операции присваивания, например в следующем выражении:
x.Add(y) = z;
К значению объекта x прибавляется значение y, а затем результату присваивается значение z (фактически это эквивалентно x = z ). Чтобы запретить подобные конструкции, достаточно добавить описатель const перед типом возвращаемого значения:
const Complex Complex::Add(const Complex x) . . .
Передача
Complex*
Complex::Add(Complex* x)
{
real += x->real;
imaginary += x->imaginary;
return this;
}
Если нет особых оснований использовать в качестве
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.