![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
ЛунныйОборотень |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 174 Регистрация: 12.11.2006 Репутация: нет Всего: 1 |
Здравствуйте!
Читаю книгу Страуструпа по С++,главу про наследование. И не могу понять,зачем это нужно? В чем преимущество,по сравнению с обычным копипастом? Меньше кода? Еще вопрос: что в базовом классе лучше выносить на публичный доступ и что на закрытый? Например,QTcpSocket? Спасибо.Буду признателен за объяснение. |
|||
|
||||
Arantir |
|
||||||||
Рыбак без удочки ![]() ![]() Профиль Группа: Участник Сообщений: 960 Регистрация: 18.11.2012 Репутация: 1 Всего: 55 |
Очень правильный вопрос. В небольшой задаче от него зачастую совершенно никакой пользы. Суть его раскрывается на более крупных проектах. Играли когда-нибудь в Lineage 2? В сети в свободном доступе есть исходники любительских сборок серверов на Java. Если хотите, можете просто на досуге покопаться в их исходниках. Копаться в открытых исходниках любительских сборок - это законно =) Сразу увидите, зачем нужны такие вещи, как интерфейсы, абстрактные классы, наследование... Объясняю я не очень хорошо, но попробую. Для чего-то некрупного в голову приходит пару случаев, в которых полезно наследование: 1. Логически объединить некие сущности в классы, где родительский класс является обобщением, а дочерний - частным случаем.
2. Наследование от интерфейсов. Гарантирует, что программисту придется написать все требуемые методы какого-то класса (который вы, например, взяли у коллеги). То есть программа не будет вылетать в экран смерти из-за недоглядки =)
Таким образом мы гарантируем, что сортировщик будет сортировать только то, что можно сравнить (у чего обязательно определена функция сравнения). иначе же мы получаем ошибку компиляции проги. 3. Расширение имеющихся классов, которые мы не можем изменять непосредственно.
Ну как-то так. Но всех тонкостей сходу не объяснишь =) Это сообщение отредактировал(а) Arantir - 26.11.2012, 20:22 -------------------- interface Жопа { // ATTENTION: has to be implemented by every class of the project for proper project work } |
||||||||
|
|||||||||
ЛунныйОборотень |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 174 Регистрация: 12.11.2006 Репутация: нет Всего: 1 |
Хм,все таки попробую:) а то так и не вкушу наследование:))
Еще такой вопрос- вот у меня есть сокет в базовом классе,во всех производных классах он тоже нужен. В каждом классе коннект к разным адресам. Нужно ли его выносить в базовый класс,а в производных можно переопределить на нужный адрес? Или в каждом классе завести свой сокет? Я к тому,что может ли наследование сокета вызвать логическую ошибку? Например:
Можно ли так делать? |
|||
|
||||
volatile |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2107 Регистрация: 7.1.2011 Репутация: 37 Всего: 85 |
Здесь наследование не причём. Всё будет работать и без него. (если правильно код написан конечно) 1_объект_класса_Базовый_сокет = 192.169.1.1 2_объект_класса_Базовый_сокет = 192.169.1.2 ![]() |
|||
|
||||
Arantir |
|
|||
Рыбак без удочки ![]() ![]() Профиль Группа: Участник Сообщений: 960 Регистрация: 18.11.2012 Репутация: 1 Всего: 55 |
Между первым и вторым вариантами нет никакой разницы... Вы немного неправильно понимаете суть наследования. Наследование - это всего-лишь инструмент. Такая себе "удобность" для программиста (как и все остальные преимущества ООП). Если постараться, можно что угодно и без ООП сделать, так вот чисто вручную. И наоборот, ООП можно добавить даже в языки, на него не ориентированные, например, в JavaScripts, тоже вручную. То, что оно в С++ вшито сразу в компилятор - это и есть "удобность". А так, ничто не ограничивает ваше воображение =) Будущее за агентно-ориентированным программированием. Языков для него еще нет, но использовать его концепции на любом языке ничто не мешает. Наследованием полей - это, грубо говоря, автоматическое дописывание этих полей всем дочерним классам (на самом деле просто идет обращение к родительскому полю, но сути это не меняет). Если в каждом классе значение поля гарантированно отличается от значения этого поля базового класса, то писать его в базовом классе просто-таки нет смысла. В любом случае при создании подключения вы должны будете использовать какой-то сокет. Думаю, при этом сразу заметите его отсутсвие, если в функцию подключения будет нечего вписать =) Как минимум компилятор выдаст вам ошибку о неинициализированной переменной. А если вы впишите его в базовый и потом забудете переопределить в дочернем, то дочерний класс может абсолютно незаметно для вас подсоединяться не к тому что надо сокету, а к тому, который он взял у базового класса. Это сообщение отредактировал(а) Arantir - 28.11.2012, 01:21 -------------------- interface Жопа { // ATTENTION: has to be implemented by every class of the project for proper project work } |
|||
|
||||
baldina |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 32 Всего: 101 |
разница есть, причем принципиальная. второй вариант это как раз копипаст, а не наследование. ЛунныйОборотень, если сокет это неотъемлемое свойство всех подклассов базового, значит это свойство базового класса. В этом суть наследования: базовый класс объединяет в себе все общие признаки. Ваш пример будет выглядеть так:
если классификация проведена правильно, то не может даже простой отказ от копипаста достаточное основание))) остальные преимущества заключаются в свойствах ООП, связанных с наследованием - это проверка типа (подклассы являются экземплярами суперкласса) и полиморфизм.
в публичный выносятся те функции, которые определяют логический интерфейс класса. а технические подробности (переменные, а также функции, связанные с реализацией) должны быть закрыты. |
||||||||
|
|||||||||
Arantir |
|
|||
Рыбак без удочки ![]() ![]() Профиль Группа: Участник Сообщений: 960 Регистрация: 18.11.2012 Репутация: 1 Всего: 55 |
Я имел ввиду результат. Принципиальная... есть такое выражение "дело принципа" =) Мое мнение состоит в том, что ООП нужно, когда прямо чувствуется, что с ним проще и быстрее, чем без него. А так в каждом классе, что переопределять, что определять эту переменную... Только одна практическая разница, о чем выше упоминал, что без ее определения она у родителя будет взята. -------------------- interface Жопа { // ATTENTION: has to be implemented by every class of the project for proper project work } |
|||
|
||||
baldina |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 32 Всего: 101 |
||||
|
||||
Arantir |
|
|||
Рыбак без удочки ![]() ![]() Профиль Группа: Участник Сообщений: 960 Регистрация: 18.11.2012 Репутация: 1 Всего: 55 |
Примерно потому, почему пишут на Java место C++ =)
-------------------- interface Жопа { // ATTENTION: has to be implemented by every class of the project for proper project work } |
|||
|
||||
ЛунныйОборотень |
|
||||
Бывалый ![]() Профиль Группа: Участник Сообщений: 174 Регистрация: 12.11.2006 Репутация: нет Всего: 1 |
Пожалуй,в каждом классе сделаю по сокету-чтобы не путаться. Могу я задать следующий вопрос? Если да, то в процессе написания сделал абстрактный класс с чисто виртуальным методом func1() И в производном классе теперь его пробую переопределить.Но,возможно,делаю это неправильно. Абстрактный Класс
В Производном классе
В хедере производного класса я написал прототип этой функции. У меня закрадывается смутное подозрение,что в результате базовая функция не используется вообще,даже как чисто виртуальная.То есть это две разные функции,не связанные наследованием. Это так? А если бы я и там и там написал бы getValue()? Только начал вплотную заниматься наследованием,в меру сил читаю Страуструпа,но все-таки вопросы остаются. Спасибо большое за ответы. |
||||
|
|||||
baldina |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 32 Всего: 101 |
сигнатуры функций (число и тип аргументов, возвращаемого значения) должны совпадать, иначе они считаются различными функциями.
пример с виртуальными функциями
зря. зачем делать лишнюю работу? если использовать только наследование (без полиморфизма), базовый класс является как-бы шаблоном для производного. классы - для компилятора, в процессе выполнения нет никаких классов, лишь объекты. переменная типа производного класса включает в себя свои переменные и функции, а также переменные и функции базовых классов. |
||||||||
|
|||||||||
ЛунныйОборотень |
|
|||
Бывалый ![]() Профиль Группа: Участник Сообщений: 174 Регистрация: 12.11.2006 Репутация: нет Всего: 1 |
То есть,определяя базовый класс и потом вызывая его через производный ,мы создаем непересекающиеся множества.
И если,допустим ,результатом выполнения этой функции будет какое то значение,записанное в переменную в базовом классе, эта переменная будет как бы в параллельных мирах?)) То же самое и с сокетом?Название и определение в одном месте(базовом классе),а значения и параметры этого сокета будут разными для каждого класса? |
|||
|
||||
Arantir |
|
|||
Рыбак без удочки ![]() ![]() Профиль Группа: Участник Сообщений: 960 Регистрация: 18.11.2012 Репутация: 1 Всего: 55 |
ЛунныйОборотень, если переменной нет в дочернем классе, она берется из базового. Если она есть в дочернем классе - берется из дочернего. Переменная дочернего класса и переменная базового класса - это две разные переменные. Они не в параллельных мирах, они просто 2 разные переменные в оперативной памяти.
К переменной базового класса можно получить доступ (если она public/protected, конечно же) в любое время через псевдокласс parent (например, parent::some_val), который автоматически представляет родительский класс. Непересекающиеся множества - это 2 объекта одного класса. А дочерний класс может всяко-разно взаимодействовать с базовым. Переопределенные функции тоже можно вызвать в их оригинале через parent. Представь, что имя переменной состоит из nameespac'а, класса и самой переменной. Типа так: "programm::myclass:var". Тогда заметно, что нет никаких параллельных миров. Есть только служебные словечки, ограничивающие доступ одним объектам к другим. А так - все "рядом".
Нет. Если определение только в базовом, то везде будет значение базового, одинаковое. При этом определять заново переменную не обязательно. Но если надо разные значения, то следует определить переменную с тем же именем в дочернем классе, тогда компилятор будет в первую обращаться к переменной дочернего класса. -------------------- interface Жопа { // ATTENTION: has to be implemented by every class of the project for proper project work } |
|||
|
||||
baldina |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 32 Всего: 101 |
так ТС запутается еще больше. нет никакого взаимодействия дочернего класса с базовым. содержимое базового класса целиком входит в производный класс. технически они рядом, синтаксически мы в некоторых случаях должны указывать, что используется переменная или функция, определенная в базовом классе. рассмотрим вот такую структуру данных: { 1, { 2, {3} } }. она может описываться, например, так:
это - композиция. если по смыслу задачи baz не просто включает в себя данные bar (и foo), а является расширением понятия bar (и foo), можно использовать наследование.
с точки зрения хранимых данных оба описания эквивалентны. использование наследования нам дает, во-первых, более короткую запись, во-вторых, возможность обращаться с объектами типа baz как с объектами типа bar и foo без явного преобразования типов. Это сообщение отредактировал(а) baldina - 28.11.2012, 15:09 |
||||||
|
|||||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |