|
Программирование >> Разработка устойчивых систем
istream ostream iostream Классы istream и ostream сами по себе являются полезными, но они также могут быть объединены посредством множественного наследования в класс, сочетающий их характеристики и поведение. Класс ios предоставляет общую функциональность всех потоковых классов, поэтому в данном случае множественное наследование является механизмом логического структурирования программы. Но какими бы соображениями не объяснялось применение множественного наследования, с ним обычно связано слишком много проблем. Наследование интерфейса Одно из применений множественного наследования, не вызывающее никаких возражений, связано с наследованием интерфейса. В С++ все наследование является наследованием реализации, поскольку все аспекты базового класса, интерфейс и реализация становятся частью производного класса. Унаследовать только часть класса (скажем, интерфейс) невозможно. Как объясняется в главе 14 первого тома, защищенное и закрытое наследование позволяет ограничить доступ к членам, унаследованным от базового класса, со стороны клиентов объекта производного класса, но на самом производном классе это не сказывается; он все равно содержит все данные базового класса и может обращаться ко всем незакрытым членам базового класса. С другой стороны, наследование интерфейса только добавляет объявления функций в интерфейс производного класса. Такая возможность не поддерживается в С++ напрямую. Стандартная методика имитации наследования интерфейса основана на наследовании от интерфейсного класса, то есть класса, содержащего только объявления (но не данные или определения функций). Все объявления интерфейсного класса, кроме деструктора, должны быть чисто виртуальными функциями. Пример: : С09:Interfaces.Срр Множественное наследование интерфейса linclude <iostream> linclude <sstream> linclude <string> using namespace std: class Printable { public: virtual -PrintableO {} virtual void print(ostream&) const = 0: class Intable { public: virtual -IntableO {} virtual int toIntO const class Stringable { public: virtual -StringableO {} virtual string toStringO const = 0: class Able : public Printable, public Intable, public Stringable { int myData: public: AbleCint x) { myData - x: } void print(ostream& os) const { OS myData: int toIntO const { return myData: string toStringO const { ostringstream os: OS myData: return os.strO: void testPrintableCconst Printable& p) { p.print(cout): cout endl: void testIntable(const Intable& n) { int i = n.toIntO + 1: cout i endl: void testStringable(const Stringable& s) { string buf = s.toStringO + th : cout buf endl: int mainO { Able a(7): testPrintable(a): testlntable(a): testStringable(a): } III:- Класс Able реализует интерфейсы Printable, Intable и Stringable, то есть предоставляет реализации для функций, объявленных в этих классах. Так как Able наследует от всех трех классов, объекты Able воплощают множественные связи типа является частным случаем . Например, объект А может использоваться как объект Printable, потому что его класс Able открыто наследует от Printable и предоставляет реализацию print(). Тестовым функциям не нужно знать фактический тип их параметра; достаточно того, что тип переданного объекта может заменить тип их параметра. tempiate<class Printable> void testPrintable(const PrintableS p) p.print(cout): cout endl: tempiate<class Intable> void testIntable(const IntableS n) { cout n.toIntO + 1 endl: tempiate<class Stringable> void testStringable(const Stnngable& s) { cout s.toStringO + th endl: int mainO { Able a(7): testPrintable(a): testlntable(a): testStringable(a): } III:- Имена Printable, Intable и Stringable стали обычными параметрами шаблонов, которые предполагают существование операций, обозначенных в их контекстах. Иначе говоря, тестовые функции могут получать аргументы любого типа, предоставляющего определение функции класса с правильной сигнатурой и типом возвращаемого значения; наследование от общего базового класса перестает быть обязательным. Некоторым программистам больше нравится первый вариант, поскольку механизм наследования гарантирует реализацию предполагаемого интерфейса. Другие довольствуются тем, что если требуемые операции не поддерживаются типом аргументов шаблона, ошибка все равно будет выявлена на стадии компиляции. С технической точки зрения второй вариант реализует более слабую проверку типов, чем первый, но для программиста (и программы) эффект Как обычно, решение с применением шаблонов получается более компактным: : C09:Interfaces2.cpp Неявное наследование интерфейса с применением шаблонов linclude <iostream> linclude <sstream> linclude <stmng> using namespace std: class Able { int myData: public: Ablednt x) { myData = x: } void print(ostream& os) const { os myData: } int toIntO const { return myData: } string toStringO const { ostringstream os: OS myData: return os.strO:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |