Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 30
Текст из файла (страница 30)
Измените ваше решение упражнения 1 из раздела 6.6 так, чтобы оператор []использовать для индексирования динамического массива. То есть заменитефункции get() и put() оператором [].202\,Самоучитель C++Проверка усвоенииматериала главы~~2^Теперь вам необходимо выполнить следующие упражнения и ответить навопросы:1. Перегрузите операторы сдвига » и « относительно класса coord так,чтобы стали возможными следующие типы операций:ob « integerob » integerУдостоверьтесь, что ваши операторы действительно сдвигают значения хи у на заданное количество разрядов.2.
Пусть дан классclass three_d (int х, у, z;public:three_d(int i,{int j,int 3t)x = i; у = j ; z = k;}three_d() { x = 0; у - 0; z = 0; }void get (int &i, int sj, int &k){i = x; j = y; k = z;Перегрузите для этого класса операторы +, — , ++ и — . (Для операторовинкремента и декремента перегрузите только префиксную форму.)3.
Измените ваше решение вопроса 2 так, чтобы в оператор-функциях вместо параметров-значений использовать параметры -ссылки. (Подсказка.Для операторов инкремента и декремента вам потребуются дружественные функции.)4. Чем действие дружественной оператор- функции отличается от действияоператор -функции — члена класса?5. Объясните, почему может потребоваться перегрузка оператора присваивания.6. Может ли функция operator=() быть дружественной?7.
Перегрузите оператор + для класса three_d из вопроса 2 так, чтобы иметьвозможность выполнять следующие типы операций:Глава 6. Введение в перегрузку операторов203ob + int;int + ob;8. Перегрузите операторы ==, != и || относительно класса three_d извопроса 2.9.
Приведите главный довод в пользу перегрузки оператора [].Проверка усвоенияматериала в целомВ этом разделе проверяется, хорошо ли вы усвоили материал этой и предыдущих глав.1. Создайте класс strtype, который допускает следующие типы операций:• Конкатенацию строк с помощью оператора +•Присваивание строк с помощью оператора =•Сравнение строк с помощью операторов <, > и ==Можете пользоваться строками фиксированной длины. На первый взглядэто может показаться непростой задачей, но, немного подумав (и поэкспериментировав), вы должны справиться.Глава 7НаследованиеРанее в этой книге вы познакомились с концепцией наследования.
Сейчаспришло время осветить эту тему более детально. Наследование — это одиниз трех базовых принципов OOP, и потому является одним из важнейшихинструментов C++. В C++ наследование используется не только для поддержки иерархии классов, но, как вы узнаете из главы 10, и для поддержкидругого важнейшего инструмента OOP — полиморфизма.Материал, который приведен в этой главе, включает в себя следующиетемы: управление доступом к базовому классу, спецификатор доступаprotected, множественное наследование, передача аргументов конструкторамбазового класса, виртуальные базовые классы.Повторение пройденногоПеред тем как продолжить, необходимо правильно ответить на следующиевопросы и сделать упражнения.
1. Теряет ли оператор при перегрузке что-либо из своей исходной функциональности?2. Нужно ли перегружать оператор относительно определенного пользователем типа данных, например, класса?3. Можно ли изменить приоритет перегруженного оператора? Можно лиизменить количество операндов?4.
Дана следующая, почти законченная программа, добавьте недостающиеоператор-функции:^include <iostrearn>using namespace std;class array {int nums[10];public:array (} ;206Самоучитель C++void set(int n[10]);void show();array operator+(array ob2) ;array operator-(array ob2} ;int operator==(array ob2);-};array::array(){int i ;for{i = 0; i < 10; i++) nums[i] - 0;1void array::set(int *n){int i ;for(i = 0 ; i < 10; i++) nums[i] = n [ i ] ;}void array::show(){int i;for(i = 0; i < 10; i++)cout « numsfi] « ' ' ;cout « "\n";}// Впишите оператор-функцииint main(){arrayJ ol, o2, o3;int i[10] - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };ol.set (i);o2.set(i);o3 = ol + o2;o3.show();o3 = ol - o3;o3.show() ;if{ol==o2) cout « "ol равно o2\n";else cout « "ol не равно о2\п";if(ol==o3} cout « "ol равно o3\n";else cout « "ol не равно оЗ\п";return 0;Глава 7.
Наследование207Перегруженный оператор + должен поэлементно складывать оба операнда. Перегруженный оператор — должен вычитать все элементы правогооперанда из элементов левого. Перегруженный оператор == должен возвращать значение true, если все элементы обоих операндов равны, в противном случае он должен возвращать значение false.5. Переработайте решение упражнения 4 так, чтобы перегрузить операторыс использованием дружественных функций.6. Используя класс и функции из вопроса 4, перегрузите оператор ++ спомощью функции — члена класса, а оператор — с помощью дружественной функции. (Перегрузите только префиксные формы операторов++ и —:.)7.
Можно ли, используя дружественную функцию, перегрузить операторприсваивания?7.1. Управление доступомк базовому классуКогда один класс наследуется другим, используется следующая основнаяформа записи:class имл производного класса: ел доступа имя_базового_хласса (// . . .JЗдесь cn_docmyna — это одно из трех ключевых слов: public, private илиprotected.
Обсуждение спецификатора доступа protected отложим до следующего раздела этой главы. Здесь рассмотрим спецификаторы public иprivate.Спецификатор доступа (access specifier) определяет то, как элементы базового класса (base class) наследуются производным классом (derived class).Если спецификатором доступа наследуемого базового класса являетсяключевое слово public, то все открытые члены базового класса остаются открытыми и в производном.
Если спецификатором доступа наследуемого базового класса является ключевое слово private, то все открытые членыбазового класса в производном классе становятся закрытыми. В обоих случаях все закрытые члены базового класса в производном классе остаютсязакрытыми и недоступными.Важно понимать, что если спецификатором доступа является ключевое слово private, то хотя открытые члены базового класса становятся закрытыми впроизводном, они остаются доступными для функций — членов производного класса.208Самоучитель C++Технически спецификатор доступа не обязателен. Если спецификатор доступа не указан и производный класс определен с ключевым словом class, тобазовый класс по умолчанию наследуется как закрытый.
Если спецификатордоступа не указан и производный класс определен с ключевым словомstruct, то базовый класс по умолчанию наследуется как открытый. Тем неменее, для ясности большинство программистов предпочитают явное задание спецификатора доступа.ПримерыР1. Здесь представлены базовый и наследующий его производный классы(наследование со спецификатором public):^include <iostream>using namespace std;class base {int x;public:void setx{int n) { x = n; }void showx() ( cout « x « '\n'; }/ / Класс наследуется как открытыйclass derived: public base {int y;public:void setyfint n) { у = n; }void showy () { cout « у « '\n'; }};int main(){derived ob;ob.setx(lO); // доступ к члену базового классаob.sety(20); // доступ к члену производного классаob.showx(); // доступ к члену базового классаob.showyO; // доступ к члену производного классаreturn 0;Как показано в программе, поскольку класс base наследуется как открытый,открытые члены класса base — функции setx() и showx() — становятся открытыми производного класса derived и поэтому доступны из любой частиГлава7.Наследование_209программы.
Следовательно, совершенно правильно вызывать эти функции изфункции main О2. Важно понимать, что наследование производным классом базового как открытого совсем не означает, что для производного класса станут доступнымизакрытые члены базового. Например, это небольшое изменение в классеderived из предыдущего примера неправильно:class base {int х;public :void setx(int n) { x = n; }void ghowxf) { cout « x « '\n'; }// Класс наследуется как открытыйclass derived: public base {int y;public:void sety(int n) { у = n; }/* Закрытые члены базового класса недоступны, х — это закрытыйчлен базового класса и поэтому внутри производного класса оннедоступен */rvoid show_sum(} { cout « х+у « '\n ; } // Ошибка! ! !void showy {) { cout « у « '\n'; }Здесь в производном классе derived сделана попытка доступа к переменной х,которая является закрытым членом базового класса base.
Это неверно, поскольку закрытые члены базового класса остаются закрытыми, независимо оттого, как он наследуется.3. Ниже представлена слегка измененная версия программы из примера 1. Базовый класс base наследуется как закрытый, т. е. с ключевым словом private.Такое изменение, как показано в комментариях, при компиляции ведет кошибке./ / В этой программе есть ошибкаtinclude <iostream>using namespace std;class base {int x;public:void s e t x f i n t n} ( x = n; }void showx{) { cout « x « ' \ n ' ; }210_Самоучитель_С++/! Класс наследуется как закрытыйclass derived: private base {int у;public:void setyfint n) { у = n; }rvoid showy () { cout « у « \n'; }int raain()(derived ob;ob.setx(lO); // ОШИБКА — теперь закрыто для производного классаob.sety(20); // правильный доступ к члену производного классаob.showxi); // ОШИБКА — теперь закрыто для производного классаob.
showy (); // правильный доступ к члену производного классаreturn 0;Как отражено в комментариях к этой (неправильной) программе, функцииshowx() и setx() становятся закрытыми в производном классе и недоступными вне его.Запомните, что функции showxQ и setx() в базовом классе base по-прежнемуостаются открытыми независимо от того, как они наследуются производнымклассом. Это означает, что объект типа base мог бы получить доступ к этимфункциям в любом месте программы. Однако для объектов типа derived онистановятся закрытыми.