Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Общие вопросы > Наследование:его смысл на небольшой задаче |
Автор: ЛунныйОборотень 26.11.2012, 15:03 |
Здравствуйте! Читаю книгу Страуструпа по С++,главу про наследование. И не могу понять,зачем это нужно? В чем преимущество,по сравнению с обычным копипастом? Меньше кода? Еще вопрос: что в базовом классе лучше выносить на публичный доступ и что на закрытый? Например,QTcpSocket? Спасибо.Буду признателен за объяснение. |
Автор: Arantir 26.11.2012, 20:12 | ||||||||
Очень правильный вопрос. В небольшой задаче от него зачастую совершенно никакой пользы. Суть его раскрывается на более крупных проектах. Играли когда-нибудь в Lineage 2? В сети в свободном доступе есть исходники любительских сборок серверов на Java. Если хотите, можете просто на досуге покопаться в их исходниках. Копаться в открытых исходниках любительских сборок - это законно =) Сразу увидите, зачем нужны такие вещи, как интерфейсы, абстрактные классы, наследование... Объясняю я не очень хорошо, но попробую. Для чего-то некрупного в голову приходит пару случаев, в которых полезно наследование: 1. Логически объединить некие сущности в классы, где родительский класс является обобщением, а дочерний - частным случаем.
2. Наследование от интерфейсов. Гарантирует, что программисту придется написать все требуемые методы какого-то класса (который вы, например, взяли у коллеги). То есть программа не будет вылетать в экран смерти из-за недоглядки =)
Таким образом мы гарантируем, что сортировщик будет сортировать только то, что можно сравнить (у чего обязательно определена функция сравнения). иначе же мы получаем ошибку компиляции проги. 3. Расширение имеющихся классов, которые мы не можем изменять непосредственно.
Ну как-то так. Но всех тонкостей сходу не объяснишь =) |
Автор: ЛунныйОборотень 27.11.2012, 09:32 | ||
Хм,все таки попробую:) а то так и не вкушу наследование:)) Еще такой вопрос- вот у меня есть сокет в базовом классе,во всех производных классах он тоже нужен. В каждом классе коннект к разным адресам. Нужно ли его выносить в базовый класс,а в производных можно переопределить на нужный адрес? Или в каждом классе завести свой сокет? Я к тому,что может ли наследование сокета вызвать логическую ошибку? Например:
Можно ли так делать? |
Автор: Arantir 28.11.2012, 01:10 | ||
Между первым и вторым вариантами нет никакой разницы... Вы немного неправильно понимаете суть наследования. Наследование - это всего-лишь инструмент. Такая себе "удобность" для программиста (как и все остальные преимущества ООП). Если постараться, можно что угодно и без ООП сделать, так вот чисто вручную. И наоборот, ООП можно добавить даже в языки, на него не ориентированные, например, в JavaScripts, тоже вручную. То, что оно в С++ вшито сразу в компилятор - это и есть "удобность". А так, ничто не ограничивает ваше воображение =) Будущее за агентно-ориентированным программированием. Языков для него еще нет, но использовать его концепции на любом языке ничто не мешает. Наследованием полей - это, грубо говоря, автоматическое дописывание этих полей всем дочерним классам (на самом деле просто идет обращение к родительскому полю, но сути это не меняет). Если в каждом классе значение поля гарантированно отличается от значения этого поля базового класса, то писать его в базовом классе просто-таки нет смысла. В любом случае при создании подключения вы должны будете использовать какой-то сокет. Думаю, при этом сразу заметите его отсутсвие, если в функцию подключения будет нечего вписать =) Как минимум компилятор выдаст вам ошибку о неинициализированной переменной. А если вы впишите его в базовый и потом забудете переопределить в дочернем, то дочерний класс может абсолютно незаметно для вас подсоединяться не к тому что надо сокету, а к тому, который он взял у базового класса. |
Автор: baldina 28.11.2012, 10:07 | ||||||||
разница есть, причем принципиальная. второй вариант это как раз копипаст, а не наследование. ЛунныйОборотень, если сокет это неотъемлемое свойство всех подклассов базового, значит это свойство базового класса. В этом суть наследования: базовый класс объединяет в себе все общие признаки. Ваш пример будет выглядеть так:
если классификация проведена правильно, то не может даже простой отказ от копипаста достаточное основание))) остальные преимущества заключаются в свойствах ООП, связанных с наследованием - это проверка типа (подклассы являются экземплярами суперкласса) и полиморфизм.
в публичный выносятся те функции, которые определяют логический интерфейс класса. а технические подробности (переменные, а также функции, связанные с реализацией) должны быть закрыты. |
Автор: Arantir 28.11.2012, 10:56 | ||
Я имел ввиду результат. Принципиальная... есть такое выражение "дело принципа" =) Мое мнение состоит в том, что ООП нужно, когда прямо чувствуется, что с ним проще и быстрее, чем без него. А так в каждом классе, что переопределять, что определять эту переменную... Только одна практическая разница, о чем выше упоминал, что без ее определения она у родителя будет взята. |
Автор: baldina 28.11.2012, 11:27 |
почему бы на ассемблере не написать? с тем же результатом... |
Автор: Arantir 28.11.2012, 11:39 |
Примерно потому, почему пишут на Java место C++ =) |
Автор: ЛунныйОборотень 28.11.2012, 13:14 | ||||||
Пожалуй,в каждом классе сделаю по сокету-чтобы не путаться. Могу я задать следующий вопрос? Если да, то в процессе написания сделал абстрактный класс с чисто виртуальным методом func1() И в производном классе теперь его пробую переопределить.Но,возможно,делаю это неправильно. Абстрактный Класс
В Производном классе
В хедере производного класса я написал прототип этой функции. У меня закрадывается смутное подозрение,что в результате базовая функция не используется вообще,даже как чисто виртуальная.То есть это две разные функции,не связанные наследованием. Это так? А если бы я и там и там написал бы getValue()? Только начал вплотную заниматься наследованием,в меру сил читаю Страуструпа,но все-таки вопросы остаются. Спасибо большое за ответы. |
Автор: baldina 28.11.2012, 13:46 | ||||||||
сигнатуры функций (число и тип аргументов, возвращаемого значения) должны совпадать, иначе они считаются различными функциями.
пример с виртуальными функциями
зря. зачем делать лишнюю работу? если использовать только наследование (без полиморфизма), базовый класс является как-бы шаблоном для производного. классы - для компилятора, в процессе выполнения нет никаких классов, лишь объекты. переменная типа производного класса включает в себя свои переменные и функции, а также переменные и функции базовых классов. |
Автор: ЛунныйОборотень 28.11.2012, 14:20 |
То есть,определяя базовый класс и потом вызывая его через производный ,мы создаем непересекающиеся множества. И если,допустим ,результатом выполнения этой функции будет какое то значение,записанное в переменную в базовом классе, эта переменная будет как бы в параллельных мирах?)) То же самое и с сокетом?Название и определение в одном месте(базовом классе),а значения и параметры этого сокета будут разными для каждого класса? |
Автор: Arantir 28.11.2012, 14:44 | ||
ЛунныйОборотень, если переменной нет в дочернем классе, она берется из базового. Если она есть в дочернем классе - берется из дочернего. Переменная дочернего класса и переменная базового класса - это две разные переменные. Они не в параллельных мирах, они просто 2 разные переменные в оперативной памяти. К переменной базового класса можно получить доступ (если она public/protected, конечно же) в любое время через псевдокласс parent (например, parent::some_val), который автоматически представляет родительский класс. Непересекающиеся множества - это 2 объекта одного класса. А дочерний класс может всяко-разно взаимодействовать с базовым. Переопределенные функции тоже можно вызвать в их оригинале через parent. Представь, что имя переменной состоит из nameespac'а, класса и самой переменной. Типа так: "programm::myclass:var". Тогда заметно, что нет никаких параллельных миров. Есть только служебные словечки, ограничивающие доступ одним объектам к другим. А так - все "рядом".
Нет. Если определение только в базовом, то везде будет значение базового, одинаковое. При этом определять заново переменную не обязательно. Но если надо разные значения, то следует определить переменную с тем же именем в дочернем классе, тогда компилятор будет в первую обращаться к переменной дочернего класса. |
Автор: baldina 28.11.2012, 15:06 | ||||||
так ТС запутается еще больше. нет никакого взаимодействия дочернего класса с базовым. содержимое базового класса целиком входит в производный класс. технически они рядом, синтаксически мы в некоторых случаях должны указывать, что используется переменная или функция, определенная в базовом классе. рассмотрим вот такую структуру данных: { 1, { 2, {3} } }. она может описываться, например, так:
это - композиция. если по смыслу задачи baz не просто включает в себя данные bar (и foo), а является расширением понятия bar (и foo), можно использовать наследование.
с точки зрения хранимых данных оба описания эквивалентны. использование наследования нам дает, во-первых, более короткую запись, во-вторых, возможность обращаться с объектами типа baz как с объектами типа bar и foo без явного преобразования типов. |