Главная страница
Навигация по странице:

  • 7.3.1 Композиция – сильная агрегация

  • 7.3.2 Наполнение – слабая агрегация Отношение между классами, при котором количество объектов некоторого класса, включаемых в класс не ограничено и может меняться

  • Зависимость

  • 8 Полиморфизм

  • Ранний (Простой) полиморфизм Реализация раннего связывания метода и объекта - на этапе компиляции. Правила простого полиморфизма 4.

  • Поздний (Сложный) полиморфизм

  • 9 Абстрактные классы

  • ООП. Пособие по ООП в С++. Создание сложных программных систем Объектно ориентированная технология


    Скачать 2.1 Mb.
    НазваниеСоздание сложных программных систем Объектно ориентированная технология
    Дата21.02.2022
    Размер2.1 Mb.
    Формат файлаpdf
    Имя файлаПособие по ООП в С++.pdf
    ТипАнализ
    #369034
    страница8 из 15
    1   ...   4   5   6   7   8   9   10   11   ...   15

    Агрегация
    Отношение части и целого. Если объект или список объектов включены в класс, то говорят об отношении агрегации.
    Так два крыла самолета являются его частью – то его неотъемлемые составляющие. При уничтожении самолета, уничтожены будут и его крылья.
    История полетов данного самолета связана с ним и является так же частью жизненного цикла аппарата. Но при уничтожении самолета его история может сохраниться.
    Приведенные примеры характеризуют два вида агрегации: композиция
    (сильная агрегация) и наполнение (слабая агрегация)

    7.3.1 Композиция – сильная агрегация
    Это отношение между классами, когда объекты одного класса являются элементами другого класса.
    В С++ композиция реализуется включением в класс фиксированного
    числа полей, которые являются объектами другого класса (статический массив или просто поля типа класс). Количество полей может быть любым.
    Применение композиции зависит как от свойств объектов, включаемых в класс, так и от количества объектных полей. При удалении класса, объекты в ключенные в удаляемый объект удаляются.
    Библиотека позволяет читателю держать на руках не более 5 книг.
    Запись о книге взятой читателем является частью карточки читателя.
    Тогда карточка читателя CardReader может иметь список с фиксированным числом элементов. Т.е свойство recordlist –список книг взятых читателем можно реализовать статическим массивом из 5 элементов.
    Особенность отношения композиции: при удалении карточки читателя, удаляется и список взятых книг.
    +setinvnumer() : void
    +setdatain() : void
    +setdataout() : void
    +getinvnumer() : int
    +getdatain() : string
    +getdataout() : string
    -invnumer : int
    -datain : string
    -dataout : string
    RecordBook
    +setnumreader() : void
    +addrecordbook(в rb : RecordBook) : void
    +getlistrecordbook() : RecordBook[0..*]
    +getnumreader() : int
    -numreader : int
    -recordlist : RecordBook[0..*]
    CardReader
    -Содержит запись
    1 0..*
    Реализация классов class
    RecordBook{ private
    : int invnumer; string datain; string dataout; public
    : void setdatain(string din){ datain=din;
    } void setdataout(string dout){ dataout=dout;
    } void setinvnumer(
    int num){ invnumer=num;
    } string getdatain(){ return datain;
    } string getdataout(){ return dataout;
    } int getinvnumer(){ return invnumer;
    }

    }; class
    CardReader{ private
    : int numreader;
    RecordBook recordlist[5]; int n; public
    :
    CardReader(){this.n=0;} void addRecordBook(RecordBook r){ recordlist[n]=r;
    } int getnumreader(){ return numreader;
    } vector getrecordlist(){ return recordlist;
    } void setnumreader(
    int num){ numreader=num;
    }
    }; ostream& operator
    <<(ostream &s,CardReader &o){ s< l=o.getrecordlist(); for
    (
    int i=0;i} return s;
    }
    int
    _tmain(
    int argc, _TCHAR* argv[])
    {
    ………………………………………………………………………………………… string d1=
    "10.01.2019"
    , d2=
    "17.02.2019"
    ;
    R.setinvnumer(createRecordBook(listBook));
    R.setdatain(d1);
    R.setdataout(d2);
    CardReader CR; int numreader; cout<<
    "Введите номер читателя"
    <>numreader;
    CR.setnumreader(numreader);
    CR.addRecordBook(R);
    CR.addRecordBook(R); return
    0;
    }
    7.3.2 Наполнение – слабая агрегация
    Отношение между классами, при котором количество объектов
    некоторого класса, включаемых в класс не ограничено и может меняться
    (динамические массивы или списки из объектов некоторого класса).
    Добавим в модель класс FondBooks (фонд книг библиотеки). Этот класс представляет все,имеющиеся в библиотеке книги - список книг, в этот список
    можно добавлять сведения о книгах, удалять. Представить в классе такой список можно динамическим массивом или файлом.
    +setname(в s : string) : void
    +setauthor(в author1 : string) : void
    +setinvnum(в n : int) : void
    +Book(в n : string, в a : string, в num : int)
    +getname() : string
    +getauthor() : string
    +getinvnum() : int
    -name : string
    -author : string
    -invnumer : int
    Book
    +addBook(в b : Book)
    +getlistBooks() : *Book
    +FondBooks()
    -listBooks : *Book
    FondBooks
    1
    *
    Реализация классов class
    Book{ private
    : string name; string author; int invnumer; public
    :
    Book(string name1,string author1, int invn){ name=name1; author=author1; invnumer=invn;
    } void setname(string sname){ name=sname;
    } void setauthor(string sauthor){ author=sauthor;
    } void setinvnumer(
    int num){ invnumer=num;
    } string getname(){ return name;
    } string getauthor(){ return name;
    } string getinvnum(){ return name;
    }
    }; class
    FondBooks{ private
    : vector listBooks; public
    :
    FondBooks(){n=0;listBooks=0;} void addBookinFond(Book b){ listBooks.push_back(b);
    } vector getlistBook(){
    return listBooks;
    }
    }; void outputBook(vector list){ for
    (
    int i=0;i{
    std::cout<' '
    <' '
    <'
    '
    <}
    } int
    _tmain(
    int argc, _TCHAR* argv[])
    {
    FondBooks fond; fond.push_back(Book(
    "AAA"
    ,
    "Ab"
    ,111)); fond.push_back(Book(
    "BBB"
    ,
    "Bb"
    ,122)); fond.push_back(Book(
    "CCC"
    ,
    "Cb"
    ,133)); vector listBook=fond.getlistBook(); outputBook(listBook); return
    0;
    }
    7.4
    Зависимость
    Для организации диалога с читателем введем в систему класс «Menu» с одним методом showlistreader, который показывает список книг, взятых читателем. Параметром для метода является массив listreader класса
    «CardReader» читателя. Таким образом, изменения, внесенные в класс
    «CardReader» могут потребовать и изменения класса «Menu».
    Класс «Menu» не принадлежит предметной области, а представляет системный класс приложения.
    +showlistbookreader(в r : *RecordBook) : void
    Menu
    +setnumreader() : void
    +addrecordbook(в rb : RecordBook) : void
    +getlistrecordbook() : RecordBook[0..*]
    +getnumreader() : int
    -numreader : int
    -recordlist : RecordBook[0..*]
    CardReader
    Рис. 7 — Зависимость
    Реализация классов class
    CardReader{ private
    : int numreader; vector recordlist; public
    : void addRecordBook(RecordBook r){ recordlist.push_back(r);
    } int getnumreader(){ return numreader;
    } vector getrecordlist(){ return recordlist;
    }
    void setnumreader(
    int num){ numreader=num;
    }
    }; class
    Menu{ static void showlistbookreader(vector r){ for
    (
    int i=0;i{ std::cout<' '
    <' '
    <'
    '
    <}} int main(
    {
    //...............................
    CardReader r;r.getrecordlist();
    Menu::showlistbookreader(r.getrecordlist()); return
    0;
    }
    8 Полиморфизм
    Поддерживается только в наследовании.
    Это механизм, обеспечивающий возможность определения метода
    (методов) с одинаковой сигнатурой для классов различных уровней иерархии.
    В разделе 5.8.4.2 мы познакомились с перегрузкой методов, а теперь с переопределением методов.
    В разделе 5.8.4.2 мы познакомились с перегрузкой методов, а теперь с переопределением методов (функций-членов).
    Реализуется посредством переопределения функций. Метод базового класса и метод производного класса считаются переопределенными, если их сигнатуры идентичны, но функцию они будут реализовывать каждый по своему.
    Полиморфизм позволяет строить более гибкие и совершенные иерархии классов, заменяя в производных классах методы в соответствии с требованиями разрабатываемой программы.
    Рассмотрим, как при таком наследовании is-a обрабатываются ссылки и указатели на объекты.
    В С++ нельзя присваивать указателю одного типа адрес переменной другого типа, а так же ссылке на один тип ссылаться на другой тип.
    Пример. double x=2.5; int *pi=&x; long &r1=x;
    Ссылка или указатель на базовый класс может ссылаться на объект производного класса без явного приведения типа. Использование указателя и ссылки на объект базового класса для доступа к объектам наследникам.

    Указателю на объект базового класса можно присваивать адрес объекта производного класса (приведение вверх).
    Полиморфизм применяется к объектам, а не к классам.
    Это механизм, позволяющий определить в родительском классе группу методов, которые наследники могут переопределить и применять их к своим объектам, сохраняя функцию метода, но применительно к нуждам своего класса – т.е. создать единый интерфейс для всех объектов класса.
    Единый интерфейс позволяет программистам помнить функции родительского, зная, что метод производного класса выполняет то же действие, что и родительский (меньше надо запоминать). Механизм связывания метода с объектом.
    Методы применяются к объектам, т.е. метод при вызове связывается с объектом. Как компилятор понимает, какой из переопределенных методов должен быть связан с объектом, ведь сигнатуры у них одинаковы?
    Существует два механизма связывания:
    • Ранний полиморфизм ( раннее связывание).
    • Позднее (динамическое) связывание.
    8.1
    Ранний (Простой) полиморфизм
    Реализация раннего связывания метода и объекта - на этапе компиляции.
    Правила простого полиморфизма
    4.
    Метод жестко связан с объектом на этапе компиляции.
    5.
    Преобразование указателя производного класса в указатель базового класса. При присваивании указателю базового класса указателя производного класса операция выполнится корректно. Но поля и методы производного класса не будут видны указателю базового класса. Объект базового класса вызовет свои методы.
    6.
    Внешние функции. Формальным параметрам - указателям на объект базового класса, при вызове метода можно передать в качестве фактического параметра – объект производного класса. Передача осуществляется по адресу.
    Но при этом передается адрес, который становится известным только на этапе выполнения, а связывание метода с объектами на этапе компиляции. Поэтому обращение через переданный объект произойдет обращение к базовому классу.
    Пример простого полиморфизма. Класс геометрических фигур. Базовый класс
    Base имеет методы вычисления площади (S) и периметра (P). Производные классы Круг и Квадрат переопределяют методы S и P, в каждом классе по своему. Переопределение методов S и P наследниками.
    class
    Base
    { private: char nameF[20]; public:
    Base();
    Base(
    char
    *s); void set_name(
    char
    *s); char
    * get_name() ; double
    S();
    //вычисление пощади double
    P();
    //вычисление периметра
    };
    Base::Base(){ strcpy_s(nameF,
    ""
    );}
    Base:: Base(
    char
    *s){ strcpy_s(nameF,s);} void
    Base::set_name(
    char
    *s)
    { strcpy_s(nameF,s);
    } char
    * Base::get_name()
    { return nameF;
    } double
    Base::S()
    { return 0;
    } double
    Base::P()
    { return
    0;
    }
    //Наследник - Круг class
    Circle:
    public
    Base
    { private: double r; public:
    Circle(){r=0;}

    Circle( double r1, char
    *s):Base(s)
    {r=r1;}

    Circle()
    {cout<<"delete object";} double
    S(); double
    P();
    }; double
    Circle::S()
    { return
    3.14*r*r;
    } double
    Circle::P()
    { return
    (2*3.14*r);
    }
    //Наследник - Квадрат class
    Rect:
    public
    Base
    { private: double a,b; public:
    Rect():Base(){a=b=0;}
    Rect(
    double a1, double b1,
    char
    *s):Base(s){a=a1; b=b1;} double
    S(); double
    P();
    }; double
    Rect::S()
    { return a*b;
    } double
    Rect::P()
    { return
    (2*a+2*b);
    } int main()
    {
    Base B; cout<<
    "P="
    <' '
    <<
    "S="
    <"Base"
    ;
    strcpy_s(s,
    "Circle"
    );
    Circle C(3,s);
    B=C;
    //присваивание объектов побайтно, но присаиваются тоько данные, а ссылки на методы – нет, поэтому вызовутся методы родительского класса. cout<"P="
    <' '
    <<
    "S="
    <Base *PB=&C;
    //присваивание адреса объекта С cout<
    get_name()<<
    "P="
    <
    P()<<
    '
    '
    <<
    "S="
    <
    >S()<}
    Результат
    8.2
    Поздний (Сложный) полиморфизм
    Для выполнения правильного связывания объекта с переопределенным методом необходимо использование сложного полиморфизма (позднее связывание).
    Такой полиморфизм реализуется посредством виртуальных функций.
    Виртуальные функции определяются в базовом классе с ключевым словом virtual и они переопределяются (замещаются в производных классах).
    При этом прототипы функций в разных классах идентичны, но функции отличаются алгоритмами.
    Три правила полиморфизма (определенные в разделе 8.1), начинают работать правильно, связывают объект и метод на этапе выполнения правильно:
    1.
    При использовании указателя на базовый класс, которому присвоен адрес объекта производного класса, вызовет метод присвоенного объекта.
    2.
    Такой же результат будет и у внешней функции, параметр которой указатель на базовый класс, а примет она в качестве параметра адрес производного класса.
    3.
    Виртуальная функция остается таковой во всех производных классах.
    Если она в каком-то классе не переопределена, то механизм виртуальных функций в этом классе сохраняется. Виртуальная функция может быть
    дружественной другому классу. При реализации виртуальной функции наследниками слово virtual не указывается.
    Пример реализации класса геометрическая фигура и реализация позднего полиморфизма. В данном примере достаточно определить виртуальные методы в базовом классе, реализация методов в производных классах не изменяется.
    //Базовый класс – Плоская геометрическая фигура class
    Base
    { private: char nameF[20]; public:
    Base();
    Base(
    char
    *s); void set_name(
    char
    *s); char
    * get_name() ; virtual double S();
    //вычисление пощади – виртуаьная функция virtual double P();
    //вычисление периметра– виртуаьная функция
    };
    Base::Base(){ strcpy_s(nameF,"");}
    Base:: Base(
    char
    *s){ strcpy_s(nameF,s);} void
    Base::set_name(
    char
    *s)
    { strcpy_s(nameF,s);
    } char
    * Base::get_name()
    { return nameF;
    } double
    Base::S()
    { return 0;
    } double
    Base::P()
    { return
    0;
    }
    //Наследник - Круг
    class
    Circle:
    public
    Base
    { private: double r; public:
    Circle(){r=0;}
    Circle( double r1, char
    *s):Base(s)
    {r=r1;}
    Circle()
    {cout<<"delete object";} double
    S(); double
    P();
    };
    //Наследник - Квадрат class
    Rect:
    public
    Base
    { private: double a,b; public:
    Rect():Base(){a=b=0;}
    Rect(
    double a1, double b1,
    char
    *s):Base(s){a=a1; b=b1;} double
    S(); double
    P();
    }; int
    _tmain(
    int argc, _TCHAR* argv[])
    {
    Base B; cout<<
    "P="
    <' '
    <<
    "S="
    <"Base"
    ; strcpy_s(s,
    "Circle"
    );
    Circle C(3,s);
    B=C; //позднее связывание не выпоняется так как В не ссылка cout<"P="
    <' '
    <<
    "S="
    <Base *PB=&C; //позднее связывание выпоняется так как В ссылка cout<
    get_name()<<
    "P="
    <
    P()<<
    '
    '
    <<
    "S="
    <
    >S()<Base *Masobject[5];//массив ссылок на объекты базового класса
    for
    (
    int i=0;i<5;i++)
    Masobject[i]=
    new
    Base; strcpy_s(s,
    "Base"
    );
    //заполнение массива ссылками на объекты производных классов
    Masobject[0]=&Base(s); strcpy_s(s,
    "Circle"
    );
    Masobject[1]=&C;
    Circle C1(8,s);
    Masobject[2]=&C1; strcpy_s(s,
    "Rect"
    );
    Masobject[3]=&Rect(2,3,s);
    Masobject[3]=&Rect(2,3,s); cout<//вызывются методы объектов содержащихся в элементах cout<get_name()<S()<get_name()<S()<}
    Результат
    Механизм позднего связывания
    Основан на таблице виртуальных методов ТВМ. Таблица хранит информацию о переопределяемых методах. Ссылка на таблицу присутствует в объекте, использующем виртуальные функции.
    При вызове переопределенного метода он будет найден в ТВМ.
    9 Абстрактные классы
    Это класс, который не предполагает создание экземпяра.
    В ООП родительский класс, выражает некоторую общую концепцию, описывает интерфейс для использования в производных классах. Этот интерфейс поддерживают производные классы.

    Абстрактные классы невозможно использовать для следующих элементов:
    • переменных и данных членов;
    • типов аргументов;
    • типов возвращаемых функциями значений;
    • типов явных преобразований.
    Другое ограничение состоит в том, что при вызове конструктором абстрактного класса чистой виртуальной функции (прямо или косвенно) результат будет неопределенным. Однако конструкторы и деструкторы абстрактных классов могут вызывать другие функции-члены.
    Для абстрактных классов можно определять чистые виртуальные функции, но вызывать их можно только непосредственно с использованием следующего синтаксиса:
    Имя абстрактного класса::имя функции)
    Это помогает при разработке иерархий классов, базовые классы которых содержат чистые виртуальные деструкторы, поскольку деструкторы базовых классов всегда вызываются в процессе удаления объекта. Рассмотрим следующий пример.
    Абстрактный класс предоставляет программисту удобство при использовании производных классов, так как они используют интерфейс базового класса, заставляя в дочерних кассах переопределить чисто виртуальные функции.
    Абстрактный класс содержит члены - данные и члены - функции, которые будут
    1   ...   4   5   6   7   8   9   10   11   ...   15


    написать администратору сайта