Справочник от Автор24
Поделись лекцией за скидку на Автор24

Свойства объектов в языках С++, Java.Часть 2

  • 👀 516 просмотров
  • 📌 456 загрузок
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Свойства объектов в языках С++, Java.Часть 2» pdf
Свойства объектов в языках С++, Java ч. 2 1) Статические поля и методы на C++ и Java Статические (static) поля и методы вызываются не объектами, а классами, они принадлежат классу целиком. Статическое поле имеет одно и тоже значение для всех объектов C++, инициализируется один раз вне функции и если объект изменит его значение, оно изменится для всех объектов. Статические методы могут обращаться только к статическим полям и методам. Пример статического поля и метода в C++, Java Марафонский бег - к min, sec в Record добавлено статическое поле hour (час). Кроме этого добавлен статический метод Roundmin – округление до минут. C++ Record.h #pragma once class Record { private: int min,sec; public: void Init(int m,int s); static int Roundmin(Record x); // статический метод static int hour; // статическое поле }; Record.cpp #include "StdAfx.h" #include "Record.h" void Record::Init(int m,int s) { min=m; sec=s; } int Record::Roundmin(Record x) { if(x.sec>=30) // if(this->sec>=30) - ошибка, обращение к не статическому полю return x.min+1; 1 else return x.min; } int Record::hour=1; // инициализация статического поля вне класса Lab5.cpp #include "stdafx.h" #include "Record.h" int _tmain(int argc, _TCHAR* argv[]) { int k; Record a,b; a.hour=3; // у b : hour=3 ! a.Init(4,55); k=Record::Roundmin(a); // вызов статического метода k=5 return 0; } Java: class Record { private int min,sec; static int hour; // статическое поле public void Init(int m,int s) { min=m; sec=s; } static int Roundmin(Record x) // статический метод { int z; if(x.sec>=30) { return x.min+1; } else { return x.min; } } } public class lab5 { public static void main(String[] args) 2 { Record a= new Record(); Record b=new Record(); a.Init(3, 5); b.Init(2, 45); a.hour=2; // присваивание статическому полю b.hour =2 всем объектам ! int z; z=Record.Roundmin(b); // вызов статического метода } } Практический пример класса (Java) со статическими методами Небольшая математическая библиотека с функциями, отсутствующими в Math. import java.lang.Math.*; class MyMath // класс расширение мат. методов { static public double drob(double x) // Дробная часть числа { return x - (int)x; } static public double ctg(double x) // котангенс { return Math.cos(x)/Math.sin(x); } } public class lab5 { public static void main(String[] args) { // TODO code application logic here double y,z; double u,v; u=48.12; v=1.5; z= MyMath.ctg(v); y=MyMath.drob(u); // y=0.12 } } 2) Конструкторы инициализации и деструктор на C++, Java Конструкторы – специальные методы класса, имя конструктора совпадает с именем класса и отсутствует тип, в том числе void, вызываются при инициализации объектов. Конструкторы могут быть с числом параметров, равным числу полей, меньшим числом 3 параметров или без параметров. Конструкторов может быть несколько (перегрузка!) C++ Конструктор с 2 параметрами В классе Record заменим Init конструктом с двумя параметрами. class Record { private: int min,sec; public: void Display(); /* вместо void Init(int m,int s); */ Record(int m,int s); // конструктор }; void Record::Display() { printf(“%d %d “,min,sec); } Record::Record(int m,int s) // конструктор { min=m; sec=s; } int main(int argc, char* argv[]) { Record z(2,5); // вызов конструктора в момент объявления вместо z.Init(2,5) z.Display(); Record *x; x=new Record(1,4); // вызов конструктора динамического объекта оператор new x->Display(); delete x; } Добавим конструктор с одним параметром и без параметров. class Record { public: int min,sec; void Display(); Record(int m,int s); Record(int m); Record(); // конструктор с 2 параметрами // конструктор с 1 параметром // конструктор без параметров 4 }; void Record::Display() { printf(“%d %d “,min,sec); } Record::Record(int m,int s) // с двумя параметрами { min=m; sec=s; } Record::Record(int m) // с одним параметром { min=m; sec=0; } Record::Record() // без параметров { min=0; sec=1; } int main(int argc, char* argv[]) { Record z(2,5); // вызов конструктора 1 z.Display(); Record a(6); // 2 конструктор a.min=6 sec=0 a.Display(); Record c; // 3 конструктор c.min=0 c.sec=1 c.Display(); Record *d; d=new Record(5); // 3 конструктор динамический объект min=5 sec=0 d->Display(); delete d; } Конструктором с одним параметром можно инициализировать массив объектов class Record { public: void Display(); Record(int m); // конструктор с одним параметром private: 5 int min; int sec; }; void Record::Display() { printf("%d %d ",min,sec); } Record::Record(int m) // конструктор с одним параметром { min=m; sec=0; } int main(int argc, char* argv[]) { Record z[5]={1,2,3,4,5}; // z[0].min=1 z[1].min=2... z[4].min=5 sec=0 у всех элементов ... } Если в классе нет конструкторов, создается пустой конструктор без параметров, при создании объектов вызывается этот конструктор. int main(int argc, char* argv[]) { Record z; // вызов пустого конструктора по умолчанию значения min sec не определены ........... Если есть хоть один конструктор, конструктор по умолчанию не создается class Record { public: void Display(); Record(int m,int s); // конструктор с 2 параметрами private: int min; int sec; }; void Record::Display() { printf("%d %d ",min,sec); } Record::Record(int m,int s) // конструктор с 2 параметрами { min=m; sec=s; 6 } int main(int argc, char* argv[]) { Record z(1,4); // min=1 sec=4 Record y; // ошибка, нет конструктора без параметров! } Деструктор. Деструктор в программе один, не имеет типа, как конструктор и не имеет аргументов, вызывается при завершении модуля, где определен статический объект или оператором delete для динамического объекта class Student { public: void Display(); Student(char *s,int k); // конструктор фамилия, курс ~Student(); // деструктор private: char Fam[30]; int Kurs; }; void Student::Display() { printf("%s %d \n",Fam,Kurs); } Student::Student(char *s,int k) { strcpy(Fam,s); Kurs=k; } Student::~Student() // деструктор { printf("%s %s\n",Fam,"удален"); } int main(int argc, char* argv[]) { Student x("Иванов",3); x.Display(); Student *y; y=new Student; y->Init("Петров",2); y->Display(); 7 delete y; // вызов деструктора } // вызов деструктора Вывод на консоль: Иванов 3 Петров 2 Петров удален Иванов удален Мнемоническое правило: Конструктор для статического объекта вызывается всегда в момент его объявления (Record c;) . Для динамического объекта через new (Record x; x=new Record(); ) . Если нет конструкторов, то создается пустой. Деструктор вызывается для статического объекта в момент выхода из функции, где он объявлен, для динамического вызывается delete. Если нет деструктора, создается пустой. Java package javaapplication10; class Record { private int min,sec; public Record(int m,int s) { min=m; sec=s; } public Record(int m) { min=m; sec=0; } public Record() { min=0; sec=1; } public class lab5 { public static void main(String[] args) { // TODO code application logic here Record a= new Record(3,5); // с 2 параметрами Record b=new Record(2); // 1 параметр min=2 sec=0 Record c=new Record(); // без параметров min=0 sec=1 8 Record d[]=new Record[5]; int i; for(i=0;i<5;i++) { d[i]=new Record(i); // у всех элементов sec=0; d[0].min=0; d[1].min=1 ... } } } Замечание по деструктору. Деструктор отсутствует, есть метод finalize(); class Record { ........ protected void finalize() // вызов при сборке мусора { System.out.printf("объект %d %d уничтожен", min,sec); } ..... } вызывается, когда происходит сборка мусора (неизвестно, когда). 3) Конструктор копирования на C++ Конструктор копирования без параметров по умолчанию создается всегда, даже если есть конструкторы инициализации и копирования с параметрами, если он не перегружен явно без параметров. class Record { private: int min,sec; public: Record(); // конструктор инициализации без параметров }; Record::Record() { min=1; sec=2; } int _tmain(int argc, _TCHAR* argv[]) { Record a; // min=1 sec=2 конструктор инициализации без параметров Record b=a; // вызов конструктора копирования по умолчанию b: min=1 sec=2 // копируются поля a в b 9 Record c(a); // вызов конструктора копирования по умолчанию с: min=1 sec=2 Record d=Record(b); // вызов конструктора копирования по умолчанию d: min=1 sec=2 return 0; } Перегрузка конструктора копирования. #include "stdafx.h" class Record { private: int min,sec; public: Record(); // конструктор без параметров Record(int m,int s); // конструктор с параметрами Record (Record & r); // конструктор копирования }; Record::Record() { min=1; sec=2; } Record::Record(int m,int s) { min=m; sec=s; } Record::Record(Record &r) // r - возвращаемый параметр аргумента - ссылка { this->min=r.min*2; // минуты удваиваются при копировании this->sec=r.sec+3; // к секундам прибавляется 3 } int _tmain(int argc, _TCHAR* argv[]) { Record a(3,4); // min=3 sec=4 Record b=a; // конструктор копирования, т.к. b создается : min=6 sec=7 Record c; // конструктор без параметров min=1 sec=2 Record d(c); // конструктор копирования min=2 sec=5 Record e; // конструктор без параметров min=1 sec=2 b=e; // присваивание, b был создан ранее; b: min=1 sec2 return 0; } Обычно параметр в конструкторе копирования объявляется const (не изменяемым) 10 ................... Record (const Record & r); // конструктор копирования с const .......................... Record::Record(const Record &r) { this->min=r.min*2; this->sec=r.sec+3; } Без const возможен конструктор, меняющий поля копируемого объекта. Record::Record( Record &r) { this->min=r.min*2; this->sec=r.sec+3; r.min=0; // изменение поля копируемого } ...... Record a; // min=1 sec=2 Record b=a; // b: min=2 sec=5 a: min=0 ! Перегрузка конструктора с дополнительным параметром, при этом конструктор копии по умолчанию создается!! class Record { private: int min,sec; public: Record(); Record (const Record & r,int m); // конструктор копирования // с дополнительным параметром }; Record::Record() { min=1; sec=2; } Record::Record( const Record &r,int m) { this->min=m; this->sec=r.sec+3; `} 11 int _tmain(int argc, _TCHAR* argv[]) { Record a; // a: min=1 sec=2 Record c=Record(a,7); // вызов конструктора копирования // с дополнительным параметром c: min=7 sec=5 Record b=a; // вызов конструктора копирования по умолчанию // конструктор создается, т.к. нет перегрузки // Record (const Record & r); // b: min=1 sec=2 поля копируются return 0; } 4) "Мелкое"(shallow) и "глубокое"(deep)копирование на C++ В классе Person есть год рождения и фамилия в виде массива char. #include class Person { private: char Fam[20]; int Year; public: Person(char *f,int y); // конструктор с параметрами Person(const Person &p); // копирующий конструктор void Putfam(char *f); // задание новой фамилии void Putyear(int y); // задание года рождения }; Person::Person(char *f,int y) { strcpy(Fam,f); Year=y; } Person::Person(const Person &p) { strcpy(this->Fam,p.Fam); this->Year=p.Year; } void Person::Putfam(char *f) { strcpy(Fam,f); } 12 void Person::Putyear(int y) { Year=y; } int _tmain(int argc, _TCHAR* argv[]) { Person a("Иванова",1990); Person b=a; // конструктор копирования b: Иванова 1990 a.Putyear(1992); a.Putfam("Петрова"); // a:Петрова 1992, b: Fam осталась Иванова 1990 return 0; } Заменим массив char на указатель: #include "stdafx.h" #include class Person { private: char *Fam; // указатель фамилии int Year; public: Person(char *f,int y); // конструктор с параметрами Person(const Person &p); // копирующий конструктор void Putfam(char *f); // задание новой фамилии void Putyear(int y); }; Person::Person(char *f,int y) { Fam=new char[20]; // выделение памяти для фамилии strcpy(Fam,f); Year=y; } Person::Person(const Person &p) { this->Fam=p.Fam; // присваивание указателя! это мелкое копирование this->Year=p.Year; } void Person::Putfam(char *f) { strcpy(Fam,f); } void Person::Putyear(int y) 13 { Year=y; } int _tmain(int argc, _TCHAR* argv[]) { Person a("Иванова",1990); Person b=a; // конструктор копирования b: Иванова 1990 a.Putyear(1992); a.Putfam("Петрова"); // b: Петрова 1990 !! фамилия меняется синхронно (указывают на одну область памяти), а год рождения остался прежним. return 0; } Исправление. Глубокое копирование. ................... Person::Person(const Person &p) { Fam=new char(strlen(p.Fam)+1); // выделение памяти из кучи для нового объекта strcpy(this->Fam,p.Fam); // копирование в выделенную память this->Year=p.Year; } ....... int _tmain(int argc, _TCHAR* argv[]) { Person a("Иванов",1980); Person b=a; a.Putfam("Петров"); // b: осталась Иванова 1990 return 0; } 5) Перегрузка оператора присваивания на C++ В C++ возможна перегрузка большинства операторов, в том числе оператора присваивания, в Java перегрузка операторов отсутствует, в C# имеется перегрузка операторов, но нет перегрузки оператора присваивания. При присваивании динамических объектов они указывают на ту же область памяти и при изменении полей одного объекта автоматически меняются поля другого. class Record { private: int min,sec; public: Record(int m,int s); // конструктор с параметрами 14 void Putmin(int m); // задание значению поля }; Record::Record(int m,int s) { min=m; sec=s; } void Record::Putmin(int m) { min=m; } int _tmain(int argc, _TCHAR* argv[]) { Record a (2,4); Record b(3,5); a=b; b.Putmin(5); // b: min=5 sec=5 a: min=3 sec=5 – у a прежние значения полей после присваивания Record *c = new Record(2,4); Record *d=new Record(3,5); c=d; // c и d указывают на одну и ту же область памяти d->Putmin(5); // d: min=5 sec=5 c: min=5 sec=5 ! изменение в одной области памяти return 0; } Можно выполнить присваивание содержимого объектов (копирование полей): *c=*d; // вместо c=d; - объекты указывают на разные области d->Putmin(5); // d: min=5 sec=5 c: min=3 sec=5 остались прежними В случае наличия указателей при присваивании содержимого объектов происходит мелкое копирование. Пусть в классе Person имеются поля Year – год рождения и указатель Fam. #include "stdafx.h" #include class Person { private: char *Fam; int Year; public: Person(char *f,int y); // конструктор с параметрами void Putfam(char *f); // задание новой фамилии void Putyear(int y); // задание нового года рождения }; 15 Person::Person(char *f,int y) { Fam=new char[20]; // выделение памяти для фамилии strcpy(Fam,f); Year=y; } void Person::Putfam(char *f) { strcpy(Fam,f); } void Person::Putyear(int y) { Year=y; } int _tmain(int argc, _TCHAR* argv[]) { Person *a; a=new Person("Иванова",1990); Person *b; b=new Person("Петрова",1992); *b=*a; // копирование содержимого - год рождения и указатель (!) на область с фамилией ; b: Иванова 1990 a->Putyear(1992); a->Putfam("Петрова"); // занесение по указателю Fam // b: Year =1992 год остался Fam="Петрова" !! Fam в той же области return 0; } Если у класса имеются указатели, стандартный метод перегрузки оператора присваивания с глубоким копированием имеет вид. #include "stdafx.h" #include class Person { private: int Year; char *Fam; public: Person(char *f,int y); void Putfam(char *f); void Putyear(int y); Person & operator = (Person &p); // перегрузка оператра присваивания }; 16 Person::Person(char *f,int y) // конструктор с параметрами { int k; Year=y; k=strlen(f)+1; Fam=new char[k]; // выделение памяти столько, сколько у аргумента strcpy(Fam,f); } void Person::Putfam(char *f) // задание фамилии объекту { strcpy(Fam,f); } void Person::Putyear(int y) { Year=y; } Person & Person::operator = (Person &p) // перегрузка присваивания (глубокое) { int k; if(Fam) { delete Fam; // очистка паяти Fam } Fam=NULL; this->Year=p.Year; // копирование обычного поля k=strlen(p.Fam)+1; Fam=new char[k]; // выделение памяти для новой копии памяти для Fam strcpy(this->Fam,p.Fam); // копирование в новую выделенную область return *this; } int _tmain(int argc, _TCHAR* argv[]) { Person *a; a=new Person("Иванова",1990); Person *b; b=new Person("Петрова",1992); *b=*a; // перегруженный оператор присваивания b: Иванова 1990, но Fam в новой области отдельно от a ! a->Putyear(1992); a->Putfam("Петрова"); // b – остались Иванова 1990 ! return 0; } 17 6) Исключения на C++, Java C++ Ошибки времени выполнения. Класс exception на C++ [catch (exception &e)] используется для встроенных ошибок. Открыть для чтения несуществующий файл, перевести в число строку (“-4.45,6”), деление на нуль. Не работающий в Visual Studio пример. #include #include #include using namespace std; int _tmain(int argc, _TCHAR* argv[]) { int x = 0; try // блок в котором могут быть ошибки (исключения) { cout <<1/x; // Исключение. Последующие операторы в try выполняться не будут } catch (...) // ловить все исключения (многоточие) { cout << "Деление на ноль"; } return 0; } Исключения, генерируемые программой. Исключения могут совпадать с системными (выход за границы массива) или тем, что программист считает за ошибку (возраст <0 или >100). Пример. Вычисление числа, обратного сумме двух введенных чисел: z=1/(x+y) #include "stdafx.h" #include #include using namespace std; int _tmain(int argc, _TCHAR* argv[]) { 18 float x,y,z; cin >> x >> y; z=1/(x+y); cout << z; return 0; } При вводе 2 -2 возникает деление на нуль. В стандартном компиляторе – сообщение "деление на нуль" и завершение программы. 1.#iINF - обработка интегрированной средой (Visual Studio) и продолжение работы программы. Обработка деления на нуль с помощью исключения внутри программы. try, throw, catch catch{ } следует за try{ } и выполняет действия на оператор throw. int _tmain(int argc, _TCHAR* argv[]) { float x,y,z; cin >> x >> y; try // попытка вычисления { if(x==-y) throw 0; // исключение (тип исключения - целый), при выполнении условия if z=1/(x+y); // если вып. throw – не вычисляются операторы до закр. } от try // иначе выполняется деление } catch(...) // ... многоточие - ловить все типы исключения { z=0; } return 0; } Ввод 2 -2 -> z=0 Возможные типы исключения – целое число, символ, строка, объект класса. throw 0; и throw 1; - одно и то же. Вариант программы – тип исключения - строка и блоки try{ } catch{ } находятся не в функции, где произошло исключение throw. #include "stdafx.h" #include #include 19 using namespace std; float f(float x,float y) // вспомогательная функция { if (x==-y) { throw "bad numbers"; // исключение типа строка } return 1/(x+y); // в случае исключения оператор пропускается } int _tmain(int argc, _TCHAR* argv[]) { float x,y,z; int p; p=0; while (p==0) // пока не введены правильные числа { p=1; cin >> x >> y; try // попытка вычислить функцию { z=f(x,y); } catch (char *s) // ловить только тип исключения - строку { cout << "Введите другие числа"; cout << '\n'; p=0; // снова ввод } if (p==1) // вычислено { cout << z; } } return 0; } Вывод: 2 -2 Введите другие числа 42 0.166667 20 При срабатывании исключения throw сначала ищется try внутри функции с исключением – [в примере f(x,y)], если try отсутствует, try ищется в функции, вызвавшей f(x,y) [в примере main]. Если нет - в следующей вызвавшей функции и т.д. Движение вверх по стеку. При обнаружении try, проверяется тип следующего за try блока catch{ } . Если подходит для throw, исключение обрабатывается в catch. в примере -[catch (char *s) ловит и выводит “Введите другие числа”]. Пример двух исключений в программе. Вычисление факториала, исключениями считаются отрицательный аргумент (первое исключение) и аргумент равный нулю (второе исключение). #include "stdafx.h" #include #include using namespace std; int fact(int n) { int i,p; try { if(n==0) throw '0'; // для n=0 тип исключения - символ if(n<0) throw 1; // для отрицательного n тип исключения – целое число } catch (char c) // ловится тип исключения - символ { printf( "Аргумент равен нулю "); // исключение символ, аргумент равен нулю return -1; } // оба исключения не сработали, вычисление факториала p=1; for(i=1;i<=n;i++) p=p*i; return p; } int _tmain(int argc, _TCHAR* argv[]) { int n,f; char c; scanf("%d",& n); try // блок try в вызывающей программе { f=fact(n); } 21 catch(int k) // ловится тип исключения целое, аргумент n отрицателен { printf( "Аргумент отрицателен"); return 0; } if(f>=0) printf( "%d \n",f); return 0; } Примеры: Аргумент равен 0 -2 Аргумент отрицателен 4 24 Java В java при возникновении возможности ошибки использование блоков try{ } и catch(Exception е){} обязательно. При отсутствии – ошибка компиляции. пример обработки системных исключений на java: public class TestTry { public static void main(String args[]) { try { int a ; a=0; int b; b = 10 / a; // деление на нуль int c[] = { 1 }; c[6] = 99; // выход за границу массива } catch (ArithmeticException e) // исключение в арифметических операциях { System.out.printf("деление на ноль: " + e); // e - сообщение класса exception } catch(ArrayIndexOutOfBoundsException e) // исключение – выход за границу массива { System.out.printf("неправильный индекс массива: " + e); // e - сообщение класса exception 22 } } } Вывод – сработало первое исключение деление на ноль: java.lang.ArithmeticException: / by zero Если заменить на a=1; вывод – сработало второе исключение неправильный индекс массива: java.lang.ArrayIndexOutOfBoundsException: 6 Пример исключения, генерируемого программой. Вычисление факториала, исключения: первое - аргумент равен 0, второе - аргумент меньше 0 package javaapplication1; import java.util.*; class Ex extends Exception // подкласс от общего класса исключений { private int Type; // номер варианта исключения Ex(int a) // конструктор определяемого класса { Type = a; } public String toString() // перегрузка встроенного метода Exception перевода в строку сообщения { if(Type==2) return "Аргумент<0"; // Сообщение второго исключения else return "Аргумент=0"; // Сообщение первого исключения } } public class calculate { static int fact(int n) throws Ex // факториал с исключениями { int i,p; if(n<0) throw new Ex(1); // 1 вариант исключения =0 if(n==0) throw new Ex(2); // 2 вариант исключения <0 23 p=1; // вычисления, аргумент положителен for(i=1;i<=n;i++) p=p*i; return p; } public static void main(String[] args) { int n,f; Scanner inp= new Scanner(System.in); n = inp.nextInt(); inp.close(); f=1; try { f=fact(n); } catch ( Ex e) // блок catch оба исключения в блоке { System.out.printf("исключение: " + e); return; } System.out.printf("f= %d",f); } } Вывод: Аргумент=0 -2 Аргумент<0 4 24 7)Массивы в качестве аргументов методов на C++, Java В языке Си и С++ одномерные массивы передаются через указатель (вариант метода класса статический): #include "stdafx.h" class Record { public: Record(int m,int s); static void Arr(double *a,int n); private: 24 int min; int sec; }; void Record::Arr(double *a,int n) { int i; for(i=0;i
«Свойства объектов в языках С++, Java.Часть 2» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ
Получи помощь с рефератом от ИИ-шки
ИИ ответит за 2 минуты

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot