5 (972473)
Текст из файла
Семинар 5. Статические элементы данных и функции. Наследование.Виртуальные методы.1. Статическая (static) переменная классаДобавим в определение класса статическую переменную:class Vector{private: //double x;double y;static int num;// описание статической переменной целого типа num…};В отличие от автоматических (обычных) переменных статическая переменная возникаетне в момент создания объекта, а при запуске программы, при условии явного объявленияэтой переменной (вне класса), например:int Vector::num;int main(){…}По умолчанию при объявлении статическая переменная получает нулевое значение, но ееможно в явном виде инициализировать любым константным выражением:int Vector::num = 1;Особенностью статической переменной является то, что она не дублируется при созданииобъекта, а создается в единственном экземпляре.
Можно сказать, что переменнаяпринадлежит самому классу, а не объектам, создаваемым на его основе.2. Статический методМетод, объявленный с ключевым словом static, называется статическим. Такой методвызывается не объектом (соответственно не получает в распоряжение указатель this и неимеет прямого доступа к нестатическим элементам данных класса), а как обычнаяфункция из любого места программы, но с учетом модификаторов доступа.
Скажем,private static метод может быть вызван другими методами класса (в том числестатическими), но не может быть вызван извне класса. Публичный статический методможет быть вызван из любого места программы, даже если ни один объект класса не былсоздан:class Vector{…public: //static void ShowNam();…};int main(){…Vector::ShowNam();…}3. Открытое наследование классовНаследование классов является одним из основных механизмов реализации концепцииповторного использование кода.
Этот механизм позволяет конструировать из имеющихся(базовых) классов новые (производные) классы. При этом производный класс наследуетсвойства, включая методы, базового класса.Предположим, у нас есть класс Vector (см. п.1). Мы хотим иметь возможность создаватьобъекты с аналогичными свойствами, но для каждого созданного вектора хранить егоуникальное символьное имя. В этом случае новый объект будет одновременно являться иобъектом класса Vector (но с дополнительными свойствами). Такой тип наследованияназывается «as a» - «является», и в Си++ может быть реализован с помощью открытого(public) наследования классов.Назовем новый класс NamedVector и при его объявлении укажем, что он являетсянаследником базового класса Vector:class NamedVector: public Vector{private:static const int Len = 40;char name[Len];public:NamedVector(const char *);}NamedVector::NamedVector(const char * str){strncpy(name, str, Len);}void main(){NamedVector nv("Vector 1");nv.show();}Результат программы: 1, 0class Vector{private:double x;double y;public:void show();Vector();…}void Vector::show(){cout << x << ", " << y;}Vector::Vector(){x = 1.;y = 0.;}В объявлении производного класса после «:» указывается имя базового класса.Модификатор public перед этим именем говорит об открытом характере наследования, врезультате которого публичные (и защищенные) данные и методы базового классастановятся напрямую доступными для объектов производного класса.
Однако частныеэлементы данных (x, y) и методы доступны только через публичные методы базовогокласса. В этом смысле говорят, что производный класс наследует интерфейс базовогокласса.В данном примере в производный класс NamedVector добавлен дополнительный элементданных – массив name типа char для хранения имени вектора. Статическая константа Lenобъявлена для ее использования в качестве символьной константы (для удобства). Такжеописан конструктор класса, принимающий в качестве аргумента константный указательна строку символов с именем создаваемого вектора.В функции main() демонстрируется создание объекта класса NamedVector и использованиеунаследованного от базового класса метода show().Что не наследуются?Конструкторы. Конструкторы производного класса обычно обращаются кконструкторам базового класса через список инициализаторов.Деструкторы.
Однако при удалении объекта производного класса в началевызывается его деструктор, а затем деструктор базового класса.2Оператор присваивания. Компилятор автоматически обеспечивает производный класс собственным оператором присваивания, в котором для присваивания элементов базового класса вызывается его оператор присваивания.Конструкторы (и деструктор), в отличие от обычных методов, не наследуются. Впроизводном классе либо в явном виде объявляются собственные конструкторы, либонеявно используется конструктор по умолчанию.
Тем не менее, создание объектапроизводного класса начинается с неявного создания объекта базового класса и вызова егоконструктора. Это позволяет инициализировать недоступные напрямую частные(скрытые) элементы, унаследованные из базового класса. Если не указано в явном виде(как в примере), то вызывается конструктор базового класса по умолчанию (безпараметров). Через «:» в качестве списка инициализаторов можно в явном виде указатьнеобходимый конструктор базового типа:class NamedVector: public Vector{private:static const int Len = 40;char name[Len];public:NamedVector(const char *);NamedVector(double,double,const char *);}Vector::Vector(double xx, double yy){x = xx;y = yy;}NamedVector::NamedVector(double x, double y, const char * str):Vector(x,y){strncpy(name, str, Len);}void main(){NamedVector nv(3, 4, "Vector 1");nv.show();}Результат программы: 3, 4В приведенном примере при создании объекта производного класса NamedVector спомощью конструктора с тремя параметрами конструктор базового класса Vectorвызывается с двумя параметрами.При уничтожении объекта производного класса последовательность вызововдеструкторов обратная: вначале вызывается деструктор производного класса, затембазового.В производном классе метод show() базового класса можно переопределить, чтобывыводить не только значения унаследованных элементов данных x и y, но и новыйэлемент name:class NamedVector: public Vector{public:void show();…}void NamedVector::show(){Vector::show();cout << name << endl;}void main(){NamedVector nv(3, 4, "Vector 1");nv.show();}Результат: 3, 4 Vector 13Для вызова в методе show() производного класса метода show() базового классаиспользуется оператор принадлежности ::.
В противном случае возникнет рекурсивныйвызов переопределенного метода.Переопределение метода не является разновидностью перегрузки. Переопределенный впроизводном классе метод независимо от сигнатуры скрывает все методы базового классас тем же именем.4. Указатели и ссылки на объекты базового и производного классовТак как класс может рассматриваться как новый тип данных, то должны существовать иуказатели этого типа. Например, можно присвоить переменной типа указателя (илиссылки) на класс адрес объекта данного класса и затем, используя этот указатель (илиссылку), вызывать методы (или обращаться к элементам данных) этого класса.
Синтаксисобращения к элементам данных по ссылке аналогичен синтаксису обращения к элементамструктуры по ее имени (через точку). Синтаксис обращения к элементам данных иметодам по указателю на объект так же ничем не отличается от синтаксиса обращения кэлементам структуры по указателю:void main(){NamedVector nv(3, 4, "Vector 1");// сам объектNamedVector *nvp = &nv; // указатель на объектNamedVector &nvr = nv; // ссылка на объектnvp->show(); // Результат: 3, 4 Vector 1nvr.show(); // Результат: 3, 4 Vector 1}При наследовании указателю на объект базового типа допускается присваивать адресобъекта производного типа (но не наоборот, если только не объявлено соответствующеепреобразование типов):void main(){NamedVector nv(3, 4, "Vector 1");Vector *nvp = &nv;nvp->show(); // Результат: 3, 4}5.
Виртуальные методыПолученный в предыдущем примере результат свидетельствует о том, что, несмотря нато, что указатель указывает на объект производного класса, вызывается при этом методshow() базового класса! Это есть следствие раннего связывания на этапе компиляции.Компилятор, видя, что указатель имеет тип указателя на базовый класс, обеспечиваетвызов метода базового класса. Так как узнать, на какой именно объект (базового илипроизводного класса) ссылается такой указатель можно только при выполнениипрограммы, то для вызова нужно метода необходимо организовать позднее связывание. ВСи++ это делается путем объявления метода базового класса виртуальным. Например,объявив в базовом классе метод show() виртуальным:class Vector{public:virtual void show();…}4мы добьемся того, что в главной функции предыдущего примера будет вызван методshow() производного класса.Объявление в базовом классе некоторого метода виртуальным автоматически делает еговиртуальным и в производном классе.
Это имеет значение, если производный класс, всвою очередь, используется как базовый для нового класса.6. Виртуальный деструкторПредположим мы хотим для хранения имени вектора в производном классе NamedVector нерезервировать статический массив из 40 элементов, а выделять в конструкторе памятьдинамически с помощью оператора new[]. В деструкторе класса для освобождения памятиследует использовать оператор delete [].Теперь предположим, что мы создаем новый объект класса NamedVector, присваивая егоадрес указателю типа Vector:Vector *vp = new NamedVector("Вектор 2");В тот момент, когда нам будет нужно освободить память и уничтожить созданный объект:delete vp;какой деструктор будет вызван? При раннем связывании – деструктор базового классаVector, который знать ничего не знает про освобождение памяти, динамическивыделенной в конструкторе производного класса.
Для вызова деструктора производногокласса нужно использовать позднее связывание, а для этого объявить деструктор базовогокласса виртуальным:virtual ~Vector();Правило здесь следующее. Если класс предполагается использовать в качестве базового,то его деструктор всегда следует объявлять виртуальным.7.
Характеристики
Тип файла PDF
PDF-формат наиболее широко используется для просмотра любого типа файлов на любом устройстве. В него можно сохранить документ, таблицы, презентацию, текст, чертежи, вычисления, графики и всё остальное, что можно показать на экране любого устройства. Именно его лучше всего использовать для печати.
Например, если Вам нужно распечатать чертёж из автокада, Вы сохраните чертёж на флешку, но будет ли автокад в пункте печати? А если будет, то нужная версия с нужными библиотеками? Именно для этого и нужен формат PDF - в нём точно будет показано верно вне зависимости от того, в какой программе создали PDF-файл и есть ли нужная программа для его просмотра.