Преобразование типов от пользовательского к основному.




Преобразование типов. Преобразование типов близко к перегрузке операций. Некоторые преобразования происходят между основными типами и типами, определенными пользователем. В табл. 7.1 отражены возможные варианты преобразований.

Преобразование типов от пользовательского к основному. Для преобразования от определенного пользователем типа к основному создается операция преобразования

Операция может быть вызвана явно

В обоих случаях происходит преобразование объекта в его эквивалентное значение основного типа. Например, преобразование строк в объекты класса string и наоборот:

 

25.Преобразование типов от пользовательского к пользовательскому. Для преобразования между объектами различных, определенных пользователем классов применяются те же два метода преобразования, что и для преобразований между основными типами и объектами определенных пользователем классов. То есть можно использовать конструктор с одним аргументом или операцию преобразования. Выбор зависит от того, нужно ли записать функцию преобразования в классе для исходного объекта или для объекта назначения.Функция в исходном объекте. В листинге 7.11 функция преобразования расположена в исходном классе. В данном случае она реализована в виде операции преобразования. #include <iostream>using namespace std;class RasstA // класс русских мер длины 18 века{ private:int arshin; int vershok; public: RasstA(): arshin(0), vershok(0) { } // конструктор без параметровRasstA(int ar, int ver) {// конструктор с двумя параметрамиif(ver >= 16) {ver = 16; ar++; } arshin = ar; vershok = ver; }void getras() {// получение информации от пользователяcout << "\nВведите аршины: ";cin >> arshin; cout << "Введите вершки: ";cin >> vershok; }void showras() {cout<<arshin<<"аршинов и "<< vershok<<" вершков\n;}}; class RasstB // класс русских мер длины 18 века {private:int verst; int sajen;public:RasstB(): verst(0), sajen(0) { } // конструктор без параметровRasstB(int vers, int saj) {// конструктор с двумя параметрамиif(saj >= 500) {saj = 500; vers++; } verst = vers; sajen = saj; }void getras() {// получение информации от пользователяcout << "\nВведите версты: ";cin >> verst;cout << "Введите сажни: ";cin >> sajen;} void showras(){ cout << verst << "верст и " << sajen << " сажней "; }operator RasstA()const { // операция преобразования типаint saj = sajen + 500*verst; return RasstA(3*saj, 0); } };int main(){ RasstA ras1(5, 10); //используется конструктор с двумя параметрамиRasstB ras2 (1, 2); //используется конструктор с двумя параметрамиRasstA ras3 = ras2; //перевод систем единиц измерения __ ras1.showras(); ras2.showras();cout << " == "; ras3.showras();return 0; }

Функция в объекте назначения. Это же преобразование выполняется функцией преобразования, которая находится в классе назначения. В этой ситуации используется конструктор с одним аргументом. Так как конструктор класса назначения должен иметь доступ к данным исходного класса для выполнения преобразования, нужно создать методы доступа к полям класса.Это методы getVer() и getSaj (). #include <iostream>using namespace std;class RasstB // класс русских мер длины{ private: int verst; int sajen;public:RasstB(): verst(0), sajen(0) { } // конструктор без параметровRasstB(int vers, int saj) {// конструктор с двумя параметрамиif(saj >= 500) {saj = 500; vers++; }verst = vers; sajen = saj; }void getras() {// получение информации от пользователяcout << "\nВведите версты: ";cin >> verst; cout << "Введите сажни: ";cin >> sajen;} void showras(){ cout << verst <<"верст и "<< sajen <<" сажней "; }int getVer() {return verst;} // получить верстыint getSaj() {return sajen;} }; // получить сажниclass RasstA // класс русских мер длины {private:int arshin;int vershok;public:RasstA(): arshin(0), vershok(0) { } // конструктор без параметровRasstA(RasstB); // конструктор с одним аргументомRasstA(int ar, int ver) {// конструктор с двумя параметрамиif(ver >= 16) {ver = 16; ar++; } arshin = ar; vershok = ver; }void getras() {// получение информации от пользователяcout << "\nВведите аршины: ";cin >> arshin;cout << "Введите вершки: ";cin >> vershok; }void showras() {cout << arshin << "аршинов и " << vershok <<" вершков " << endl; } };RasstA::RasstA(RasstB ras) { //преобразовать RasstB в RasstAint ars = (ras.getVer() * 500 + ras.getSaj()) *3;arshin = ars; vershok = 0; }int main() { RasstA ras1(5, 10); // используется конструктор с двумя параметрамиRasstB ras2 (1, 2); RasstA ras3 = ras2; ras1.showras(); ras2.showras();cout << " == "; ras3.showras();return 0; }

 

26.Предотвращение преобразования типа от основного к пользовательскому с помощью конструктора. Предотвращение преобразования типа от основного к пользовательскому с помощью конструктора. В стандарт языка C++ включены ключевые слова explicit и mutable. Ключевое слово explicit относится к преобразованию типов, a mutable предназначе- но для изменения полей констант. Ключевое слово еxplicit используется для предотвращения не- явного преобразования типа. Оно помещается перед объявлением конструктора с одним аргументом. Конструктор, объявленный с ключевым словом explicit, не может быть использован в ситуации неявного преобразования данных. Пример#include <iostream>using namespace std; class Rasst // класс русских мер длины { private: const float artm = 0.7112F; //коэффициент перевода метров в аршины int arshin; int vershok; public: Rasst(): arshin(0), vershok(0) { } // конструктор без параметров explicit Rasst(float meters) {// конструктор преобразования float f_arshin = meters / artm; // переводим в аршины arshin = int(f_arshin); // берем число полных аршиновvershok = 16 * (f_arshin — arshin); } // остаток — это вершки Rasst(int ar, int ver) {// конструктор с двумя параметрами if(ver >= 16) {ver -= 16; ar++;} arshin = ar; vershok = ver; } void getras() {// получение информации от пользователяcout << "\nВведите аршины: "; cin >> arshin; cout << "Введите вершки: "; cin >> vershok; } void showras() { cout << arshin << "~" << vershok << endl; } }; int main() { void showInf(Rasst); // объявление Rasst ras1(2.35F); // конструктор с 1 аргументом, преобразовывает метры в Расстояние // Rasst dist1 = 2.35F; ОШИБКА, если конструктор является явным(EXPLICIT) cout << "\nras1 = "; ras1.showras(); float mtrs = 3.0F; cout << "\nras1 "; //showInf(mtrs); // ОШИБКА, если конструктор является явным(EXPLICIT) return 0; } void showInf(Rasst r) { cout << "(в аршинах и вершках) = "; r.showras(); }

Для создания объекта-константы, имеющего определенное поле, которое нужно будет изменять, несмотря на то, что сам объект 73 является константой, используется ключевое слово mutable. Данные, объявленные с этим ключевым словом, могут быть изменены, даже если их объект объявлен как const. Пример использования ключевого слова mutable #include <iostream> #include <string> using namespace std; class scrollbar { private: int size; // релевантный для константы mutable string owner; // не релевантный для константы public: scrollbar(int sz, string own): size(sz), owner(own) { } void setSize(int sz) // изменения размера { size = sz; } void setOwner(string own) const // изменения владелеца { owner = own; } int getSize() const // возвраты размера { return size; } string getOwner() const // возвраты владелеца { return owner; } }; int main() { const scrollbar sbar(60, "#1"); // sbar.setSize(100); // не может быть изменен в объекте-константе sbar.setOwner("#2"); // может быть изменен даже если объект—константа cout << sbar.getSize() << ", " << sbar.getOwner() << endl; return 0; }

27. Базовый и производный классы. Конструкторы производного класса. Перегрузка методов при наследовании. Алгоритм выбора перегруженного метода. Наследование — процесс создания новых классов, называемых наследниками или производными, из уже существующих, базовых классов. Производный класс получает все возможности базового класса, но может быть усовершенствован. Есть возможность использовать существующий код несколько раз.Основная форма наследования:сlass имя_производного_класса: режим_доступаимя_базового_классаСуществуют три режима доступа: public (общий), private (при- ватный), protected (защищенный). Последний позволяет членам быть доступными методам своего и произвольного класса; при этом ограничивает доступ из функций, не принадлежащих этим классам. Если режим доступа отсутствует, то у производного класса class подразумевается private.Для объектов производного класса могут быть использованы методы базового класса. Это называется правами доступа. Если в 75 производном классе не определен конструктор, то будет использоваться подходящий конструктор базового класса. Кроме того, есть возможность использовать доступный метод взамен отсутствующего. Заметим, что наследование не работает в обратном направлении и базовому классу не доступны производные классы.Пример, в котором показаны два класса Counter и CountDn.#include <iostream> using namespace std;class Counter //базовый класс{protected:unsigned int count; //счетчик public:Counter (): count (0) { } //конструктор без аргументовCounter (int c): count (c) { }unsigned int get_count () const{ return count; } // возвращает значение счетчика Counter operator++ () //увеличивает значение//счетчик (префикс){ return Counter (++count); }};class CountDn: public Counter//производный класс{public:Counter operator‐‐ () //уменьшает значение счетчика{ return Counter (‐‐count); }};int main (){CountDn c1; // объект с1cout << "\n c1=" << c1.get_count (); //вывод на печать ++c1; ++c1; ++c1; //увеличиваем c1 три разаcout << "\n c1=" << c1.get_count (); //вывод на печать‐‐c1; ‐‐c1; //уменьшаем c1 два разаcout << "\n c1=" << c1.get_count (); //вывод на печатьcout << endl;return 0;}

Конструкторы производного класса. Для инициализации объекта производного класса нельзя воспользоваться конструкто- ром базового класса (компилятор будет использовать только кон- структор базового класса без аргументов). Нужно написать новый конструктор для производного класса. // counten2.cpp// конструкторы в производных классах#include <iostream>using namespace std; class Counter{ protected:unsigned int count; // счетчикpublic:Counter (): count (0) // конструктор без параметров{ }Counter (int c): count (c) // конструктор с одним параметром{ }unsigned int get_count () const // получение значениЯ{ return count; }Counter operator++ () // оператор увеличениЯ{ return Counter (++count); }};class CountDn: public Counter //режим доступа при наследовании public:{public: CountDn (): Counter () { }// конструктор без параметровCountDn (int c): Counter (c) { }// конструктор с одним параметромCountDn operator‐‐ () // оператор уменьшениЯ{ return CountDn (‐‐count); }};int main (){CountDn c1; // переменные класса CountDnCountDn c2 (100);cout << "\nc1 = " << c1.get_count (); // выводим значениЯ на экранcout << "\nc2 = " << c2.get_count ();++c1; ++c1; ++c1; // увеличиваем c1cout << "\nc1 = " << c1.get_count (); // показываем результат‐‐c2; ‐‐c2; // уменьшаем c2cout << "\nc2 = " << c2.get_count (); // показываем результатCountDn c3 = ‐‐c2; //создаем переменную c3 на основе c2cout << "\nc3 = " << c3.get_count (); //показываем значениеcout << endl;return 0;}В конструкторе CountDn(): Counter() {} появилась новая возможность: имя функции, следующее за двоеточием. Оно используется конструктором CountDn() для вызова конструктора Counter() базового класса. При создании объекта производного класса вызовется конструктор CountDn() для его инициализации, который в свою очередь вызовет конструктор Counter() (кроме того, он может выполнять и свои операции). В строке: CountDn c2 (100); Функция main() использует конструктор класса CountDn с одним аргументом: CountDn (int c): Counter (c) Такая конструкция означает, что аргумент будет передан от конструктора CountDn() в Counter(), где будет использован для инициализации объекта.

28.Общее и частное наследование. Уровни наследования. Множественное наследование. Неопределенность при множественном наследовании. Алгоритм выбора перегруженного метода. Наследование — это механизм создания нового класса на основе уже существующего. При этом к существующему классу могут быть добавлены новые элементы (данные и функции), либо существующие функции могут быть изменены. Основное назначение механизма наследования — повторное использование кодов, так как большинство используемых типов данных являются вариантами друг друга, и писать для каждого свой класс нецелесообразно.При общем наследовании порожденный класс имеет доступ к наследуемым членам базового класса с видимостью public и protected. Члены базового класса с видимостью private – недоступны. Общее наследование означает, что порожденный класс – это подтип базового класса. Таким образом, порожденный класс представляет собой модификацию базового класса, которая наследует общие и защищенные члены базового класса.Примерclass student {protected:char fac[20];char spec[30];char name[15]; public:student(char *f, char *s, char *n);void print(); }; class grad_student: public student {protected:int year;char work[30]; public:grad_student(char *f, char *s, char *n, char *w, int y);void print(); }; Частное наследование Порожденный класс может быть базовым для следующего порождения. При порождении private наследуемые члены базового класса, объявленные как protected и public, становятся членамипорожденного класса с видимостью private. При этом члены базового класса с видимостью public и protected становятся недоступными для дальнейших порождений. Цель такого порождения — скрыть классы или элементы классов от использования в дальнейших порождениях. При порождении privateне выполняются предопределенные стандартные преобразования:class grad_student: private student {...}; int main() {...grad_student *gs = &stud;student *m;gs->print();m = gs; // ошибкаm->print();cin.get();return 0; } Уровни наследования. Производные классы могут являться базовыми классами для других производных классов:class A {};class B: class A {};class C: class B {};Здесь класс B является производным класса A, a класс С является производным класса B. Результат иерархии классов обобщение схожих характеристик. Чем более общим является класс, тем выше он находится в схеме.

Множественное наследование. Класс может быть производным не только от одного базового класса, а от многих. Этот случай называется множественным наследованием. Форма наследования в этом случае следующая:class имя_производного_класса: список базовых классов{};Список базовых классов содержит перечисленные через запя- тую базовые классы с соответствующими режимами доступа к каждому из базовых классов.Пока конструкторы базовых классов не имеют аргументов, то производный класс может не иметь функцию-конструктор. Если же конструтор базового класса имеет один или несколько аргументов, каждый производный класс обязан иметь конструктор. Чтобы передать аргументы в базовый класс, нужно определить их после объявления конструктора базового класса: конструктор_производного_класса(список аргументов):базовый класс1(список аргументов){}…базовый классN(список аргументов){}Здесь базовый класс1….базовый классN — это имена конструкторов базовых классов, которые наследуются производным классом. Двоеточие отделяет имя конструктора производного класса от списка аргументов базового класса. Список аргументов, ассоциированный с базовым классом, может состоять из констант, глобальных параметров. Так как объект инициализируется во время выполнения программы, можно в качестве параметров использовать переменные.Неопределенность при множественном наследовании. В определенных ситуациях могут возникнуть некоторые проблемы, связанные со множественным наследованием. Допустим, что в обоих классах существуют методы с одинаковыми именами, а в производном классе метода стаким именем нет. Как в этом случае объект производного класса определит, какой из методов базовых классов выбрать? Проблема решается путем использования оператора разрешения, определяющего класс, в котором находится метод. Процесс направления к версии метода конкретного класса называется устранением неоднозначности.Другой вид неопределенности возникает, если мы создаем производный класс от двух базовых классов, которые, в свою очередь, являются производными одного класса. Это создает дерево наследования в форме ромба#include <iostream>using namespace std;class A{public:void func (){cout << "A";};};class B: public A{ };class C: public A { };class D: public B, public C{ };int main () {D objD; objD.func (); // неоднозначность: программа не скомпилируетсяobjD.B::func (); //так можноreturn 0;}Классы В и С являются производными класса А, а класс D — производный классов В и С. Трудности начинаются, когда объект класса D пытается воспользоваться методом класса А. В этом примере объект D использует метод func(). Однако классы В и С содержат копии метода func(), унаследованные от класса А. Компилятор не может решить, какой из методов использовать, и сообщает об ошибке.Алгоритм выбора перегруженного метода?

29.Указатели. Инициализация, арифметические операции. Указатель — это переменная, значением которой является адрес некоторого объекта (обычно другой переменной) в памяти компьютера. Подобно тому, как переменная типа char имеет в качестве значения символ, а переменная типа int — целочисленное значение, переменная типа указателя имеет в качестве значения адрес ячейки оперативной памяти. Допустимые значения для переменной-указателя — множество адресов оперативной памяти компьютера.Понятие указателя тесно связано с понятием адреса переменной. &p —получение адреса, где p — идентификатор переменной. Результатом операции является адрес переменной p. Инициализация указателей. Инициализация указателей может быть осуществлена различными способами.1.Присваивание указателю адреса существующего объекта: С помощью операции получения адреса:int a=5; //целая переменнаяint*p=&a; //в указатель записывается адресaС помощью значения другого инициализированного указателя:int*p1=p;// указательpуже инициализирован С помощью имени массива, которое трактуется как адрес:int[]b=newint[]{10,20,30,40}; //массивfixed(int*p2=b) {...}; //присваивание указателю р2 адреса начала массиваfixed(int*р2=&b[0]){...}; //то же самое2. Присваивание указателю адреса области памяти в явном виде:сhar*p3=(char*)0x12F69E;здесь 0x12F69E– шестнадцатеричная константа, (Char*) – операция явного приведения типа: константа преобразуется к типу указателя на char.3.Присваивание нулевого значения:int*p4=null;4.Выделение области памяти в стеке и присваивание её адреса указателю:int*р5=stackalloc int[10];Здесь операция stackalloc выполняет выделение памяти под 10 величин типа int (массив из 10 элементов) и записывает адрес начала этой области памяти в переменную p5, которая может трактоваться как имя массива, так и указателем. Операции с указателями Операция Описание* Разадресация –получение значения, которое находится по адресу, хранящемуся в указателе.-> Доступ к элементу структуры через указатель[] Доступ к элементу массива через указатель& Получение адресапеременной++,-- Увеличение и уменьшение значения указателя на один адресуемыйэлемент+,- Сложение с целой величиной и вычитание указателей= =,!=,<>,<=,>= Сравнение адресов, хранящихся в указателях. Выполняется как сравнение беззнаковых целых величин.stackalloc Выделение памяти в стеке под переменную, на которую ссылается указательОсновными операциями с указателями являются * и &.Обе эти операции являются унарными, т. е. имеют один операнд, перед которыми они ставятся. Операция &соответствует операции "взять адрес". Операция* соответствует словам "значение, расположенное по указанному адресу".Операция *называется разадресацией или разыменованием. Она предназначена для доступа к величине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так идля изменения значения величины.Пример:int a=5; //целая переменнаяint*p=&a; //инициализация указателя адресом переменной аConsole.WriteLine(*p);//операция разадресации, результат равен 5Console.WriteLine (++(*p)); //результат 6int[]b=new int[] {10,20,30,40}; //массивfixed(int*t=b) //инициализация указателя адресом начала массива{int*z=t; //инициализация указателя значением другого указателяfor (int i=0;i<b.length;++i){ t[i]+=5; //доступ к элементу массива (увеличение на 5)*z+=5; //доступ с помощью адресации (увеличение ещё на 5)++z; //инкремент указателя}}В приведённом примере доступ к элементам массива выполняется двумя способами: путём индексации указателя t и путём разадресации указателя z, значение которого инкрементируется при каждом проходе цикла для перехода к следующему элементу массива.Конструкцию *переменная можно использовать в левой части оператора присваивания, так как она определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы всей действия, определённые для величин соответствующего типа.Оператор fixed фиксирует объект, адрес которого заносится в указатель, для того чтобы его не перемещал сборщик мусора и, таким образом, указатель остался корректным. Фиксация происходит на время выполнения блока, который записан после круглых скобок.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-10-12 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: