|
Программирование >> Аргументация конструирования
Передача абстрактных классов Поскольку вы не можете реализовать абстрактный класс, упоминание о возможности создавать указатели на абстрактные классы звучит несколько странно. Однако если вспомнить о полиморфизме, то станет ясно, что это не так уж глупо, как кажется поначалу. Рассмотрим следующий фрагмент кода: voi *pAccount); это допустимо void otherFn () Savings s; Chec:<:ing c; Savings ЯВЛЯЕТСЯ Account fn(i3) ; Checking - тоже fn(sc); В этом примере объявлен как указатель на Account. Разумеется, при вызове функции ей будет передаваться адрес какого-то объекта неабстрактного класса, например Checking или Savings. Все объекты, полученные функцией f n (), будут объектами либо класса Checking, либо Savings (или другого неабстрактного подкласса Account). Можно с уверенностью заявить, что вы никогда не передадите этой функции объект класса Account, поскольку никогда не сможете создать объект этого класса. Нужны ли чисто виртуальные функции Если нельзя определить функцию withdrawal;), почему бы просто не опустить ее? Почему бы не объявить ее в классах Savings и Checking, где она может быть определена, оставив в покое класс Account? Во многих объектно-ориентированных языках вы могли бы именно так и сделать. Но C + + предпочитает иметь возможность убедиться в вашем понимании того, что вы делаете. Не забывайте, что объявление функции - это указание полного имени функции, включающего ее аргументы. Определение же функции включает в себя и код, который будет выполняться в результате вызова этой - функции. Чтобы продемонстрировать суть сказанного, можно внести следующие незначительные изменения В класс Account: class Account С то же, что и раньше, но нет функции withdrawal О class Savs : lic Acct public: }; virtual vid withdrawal (float amnt) ; void снлть некоторую сумму pAcc->withdrawal(100.OOf); этот вызов недопустим, поскольку withdrawal() не является членом класса Account int main О ! Savings s; открыть счет fn(s,s) ; продолжение программы Представьте себе, что вы открываете сберегательный счет s. Затем вы передаете адрес этого счета функции fn (}, которая пытается выполнить функцию withdrawal (). Однако, поскольку функция withdrawal () не член класса Account, компилятор сгенерирует сообщение об ошибке. Некоторые языки выполняют такую проверку, когда функция уже вызвана, во время выполнения программы. В таком случае приведенный выше фрагмент кода будет работать: raain() будет вызывать fn () и передавать ей объект s. Когда f п (), в свою очередь, вызовет функцию withdrawal (), программа увидит, что withdrawal!) действительно определена в переданном ей объекте. Цена такой гибкости - снижение скорости выполнения программы, поскольку язык должен проводить множество проверок во время ее выполнения. Это также чревато некоторыми ошибками. Например, если кто-то передаст объект, который является счетом, но не содержит определенной в нем функции withdrawal О, то программа аварийно прекратит работу, так как не сможет определить, что делать с этим вызовом. Я думаю, пользователи будут не очень рады этому. Взгляните, как чисто виртуальная функция помогает решить эту проблему. Вот та же ситуация с абстрактным классом Account: class Account почти то же, что и в предыдущей программе, однако функция withdrawal () определена virtual void withdrawal(float amnt) = 0 ; class Savings : public Account public: virtual voi al (float amnt); voi count *pAcc) снять некоторую сумму теперв этот код будет работатв pAcc->withdrawal(100.OOf) ; int main() { Savings s; открытв счет fn(&s); продолжение программы Ситуация та же, но теперь класс Account содержит функцию-член withdrawal {). Поэтому, когда компилятор проверяет, ли функция (), он видит ожидаемое онредедение Account: :withdrawal (). Комнидятор счастдив. Вы счастливы. А значит, и я тоже счастлив. (Честно говоря, для того чтобы сделать меня счастливым, достаточно футбола и холодного пива.) Чисто виртуальная функция занимает место в базовом классе для функции с тем, чтобы позже быть переопределенной в подклассе, который будет знать, как ее реализовать. Если место не будет занято в базовом классе, не будет и переопределения. Рационализация южайа: В этой части продолжается превращение исключительно функциональной версии программы 1, приведенной в конце части 2, которая затем прошла через объектно-основанный этап своей эволюции - Budget2, представленную в конце части 3. Теперь она наконец превратится в объектно-ориентированную программу Budget3. Программа Budget осуществляет вложение денег на счет и снятие со счета в воображаемом банке. Пользователь поочередно вводит номера банковских счетов и суммы вкладов на этот счет и снятий с него. После того как пользователь выполнил все транзакции, программа показывает баланс каждого счета и общий бадане. Обе программы - Budget2 и Budget3 - эмулируют Checking (чековый) и Savings (сберегательный) счета. Чековые счета взимают небольшой гонорар за обслуживание при каждом снятии, если баланс упал ниже 500 долларов, тогда как сберегательный счет взимает большой гонорар за обслуживание при первом снятии, независимо от баланса. Программа Budget2 превосходит 3udgetl только в одном: она изолирует особенности классов, описывающих счет, от внешних функций, которые манипулировали счетами. К сожадению, Budget2 содержала большое количество дублированного кода в классах Savings и Checking, и именно от него мы и хотим избавиться, используя принципы наследования. Программа Budget3 обладает следующими преимуществами: использует наследование, чтобы выделить общие для чековых и сберегательных счетов свойства, избегая избыточности; использует виртуальные функции-члены для улучшения читаемости и гибкости программы; создает чисто виртуальные классы для выделения общих черт, присущих чековым и сберегательным счетам; вместо массива использует связанный список во избежание ограничения на количество счетов, которые может поддерживать банк. С помощью такого супергероя объектно-ориентированного программирования, как наследование, и его верного бокового удара - полиморфизма мы смогли оптимизировать два класса счетов, объединив в один класс Account все то общее, что присуще этим двум классам. В результате получилась гораздо более короткая и простая программа.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |