Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Наследование:его смысл на небольшой задаче 
:(
    Опции темы
ЛунныйОборотень
Дата 26.11.2012, 15:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 174
Регистрация: 12.11.2006

Репутация: нет
Всего: 1



Здравствуйте!
Читаю книгу Страуструпа по С++,главу про наследование.
И не могу понять,зачем это нужно?

В чем преимущество,по сравнению с обычным копипастом?
Меньше кода?

Еще вопрос: что в базовом классе лучше выносить на публичный доступ и что на закрытый?

Например,QTcpSocket?


Спасибо.Буду признателен за объяснение.
PM MAIL   Вверх
Arantir
Дата 26.11.2012, 20:12 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Цитата

Наследование:его смысл на небольшой задаче

Очень правильный вопрос. В небольшой задаче от него зачастую совершенно никакой пользы.
Суть его раскрывается на более крупных проектах.
Играли когда-нибудь в Lineage 2? В сети в свободном доступе есть исходники любительских сборок серверов на Java. Если хотите, можете просто на досуге покопаться в их исходниках. Копаться в открытых исходниках любительских сборок - это законно =) Сразу увидите, зачем нужны такие вещи, как интерфейсы, абстрактные классы, наследование...

Объясняю я не очень хорошо, но попробую.
Для чего-то некрупного в голову приходит пару случаев, в которых полезно наследование:
1. Логически объединить некие сущности в классы, где родительский класс является обобщением, а дочерний - частным случаем.
Код

класс Многоуголник
{
    количество углов;
    массив длин ребер;
    площадь()
    {
        какая-то жуть-замутная формула =)
    }
}

класс Прямоугольник : Многоугольник
{
    площадь()
    {
        первая сторона *  вторая сторона // ширина на высоту
    }
}
класс Квадрат : Прямоугольник
{
    
    площадь()
    {
        первая сторона ^ 2 // длина стороны, возведенная в 2 степень
    }
}
Итого такие свойства, как количество углов и массив ребер мы содержим в базовом классе, а в дочерних - всего-лишь формулы, например, требующие от компьютера меньше действий/вычислений в каждом частном случае.

2. Наследование от интерфейсов. Гарантирует, что программисту придется написать все требуемые методы какого-то класса (который вы, например, взяли у коллеги). То есть программа не будет вылетать в экран смерти из-за недоглядки =)
Код

интерфейс Сравниваемое
{
    сравнить(Сравниваемое что-то)
    {
        если это > что-то 
            1
       если же  это < что-то 
            -1
       в конце-концов
           0
    }
}

класс Сортировщик
{  
    отсортировать(массив Сравниваемоего)
    {
        если это.сравнить(что-то) < 0
            передвинуть это выше
        в противном случае
           передвинуть это вниз
    }
}

Таким образом мы гарантируем, что сортировщик будет сортировать только то, что можно сравнить (у чего обязательно определена функция сравнения). иначе же мы получаем ошибку компиляции проги.

3. Расширение имеющихся классов, которые мы не можем изменять непосредственно.
Код

класс Системный
{
    сделать1() {}
    сделать2() {}
    сделать3() {}
}

класс ЭкстраСистемный : Системный
{
    сделать3()
    {
        базовый.сделать3();
        и_еще_сделать_что-то_мое();
    }
}


Ну как-то так. Но всех тонкостей сходу не объяснишь =)

Это сообщение отредактировал(а) Arantir - 26.11.2012, 20:22


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
ЛунныйОборотень
Дата 27.11.2012, 09:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 174
Регистрация: 12.11.2006

Репутация: нет
Всего: 1



Хм,все таки попробую:) а то так и не вкушу наследование:))

Еще такой вопрос- вот у меня есть сокет в базовом классе,во всех производных классах он тоже нужен.
В каждом классе коннект к разным адресам.
Нужно ли его выносить в базовый класс,а в производных можно переопределить на нужный адрес?
Или в каждом классе завести свой сокет?

Я к тому,что может ли наследование сокета вызвать логическую ошибку?

Например:

Код

Базовый сокет= 192.169.1.1

Тот_же_сокет_в_проиводном_классе=192.169.1.2



Можно ли так делать?
PM MAIL   Вверх
volatile
Дата 27.11.2012, 23:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(ЛунныйОборотень @  27.11.2012,  09:32 Найти цитируемый пост)
Например:
Базовый сокет= 192.169.1.1
Тот_же_сокет_в_проиводном_классе=192.169.1.2
Можно ли так делать? 


Здесь наследование не причём.  
Всё будет работать и без него. (если правильно код написан конечно)

1_объект_класса_Базовый_сокет = 192.169.1.1
2_объект_класса_Базовый_сокет = 192.169.1.2
 smile 


PM MAIL   Вверх
Arantir
Дата 28.11.2012, 01:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 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
}
PM   Вверх
baldina
Дата 28.11.2012, 10:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(ЛунныйОборотень @  27.11.2012,  09:32 Найти цитируемый пост)
Нужно ли его выносить в базовый класс,а в производных можно переопределить на нужный адрес?
Или в каждом классе завести свой сокет?


Цитата(Arantir @  28.11.2012,  01:10 Найти цитируемый пост)
Между первым и вторым вариантами нет никакой разницы... 

разница есть, причем принципиальная. второй вариант это как раз копипаст, а не наследование.

ЛунныйОборотень, если сокет это неотъемлемое свойство всех подклассов базового, значит это свойство базового класса.
В этом суть наследования: базовый класс объединяет в себе все общие признаки.
Ваш пример будет выглядеть так:
Код

class Service {
  private:
    Socket s;
  public:
    Service (const string& ip) : s(ip) {}
};

class LocalService : public Service {
  public:
    LocalService () : Service ("127.0.0.1") {}
};

Цитата(ЛунныйОборотень @  27.11.2012,  09:32 Найти цитируемый пост)
Я к тому,что может ли наследование сокета вызвать логическую ошибку?

если классификация проведена правильно, то не может

Цитата(ЛунныйОборотень @  26.11.2012,  15:03 Найти цитируемый пост)
В чем преимущество,по сравнению с обычным копипастом?

даже простой отказ от копипаста достаточное основание)))
остальные преимущества заключаются в свойствах ООП, связанных с наследованием - это проверка типа (подклассы являются экземплярами суперкласса) и полиморфизм.

Цитата(ЛунныйОборотень @  26.11.2012,  15:03 Найти цитируемый пост)
Еще вопрос: что в базовом классе лучше выносить на публичный доступ и что на закрытый?

в публичный выносятся те функции, которые определяют логический интерфейс класса. а технические подробности (переменные, а также функции, связанные с реализацией) должны быть закрыты.
PM MAIL   Вверх
Arantir
Дата 28.11.2012, 10:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Цитата(baldina @  28.11.2012,  09:07 Найти цитируемый пост)
разница есть, причем принципиальная. второй вариант это как раз копипаст, а не наследование.

Я имел ввиду результат. Принципиальная... есть такое выражение "дело принципа" =)
Мое мнение состоит в том, что ООП нужно, когда прямо чувствуется, что с ним проще и быстрее, чем без него.
А так в каждом классе, что переопределять, что определять эту переменную... Только одна практическая разница, о чем выше упоминал, что без ее определения она у родителя будет взята.


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
baldina
Дата 28.11.2012, 11:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Arantir @  28.11.2012,  10:56 Найти цитируемый пост)
Я имел ввиду результат

почему бы на ассемблере не написать? с тем же результатом...
PM MAIL   Вверх
Arantir
Дата 28.11.2012, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: 1
Всего: 55



Примерно потому, почему пишут на Java место C++ =)


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
ЛунныйОборотень
Дата 28.11.2012, 13:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 174
Регистрация: 12.11.2006

Репутация: нет
Всего: 1



Цитата(baldina @  28.11.2012,  10:07 Найти цитируемый пост)
LocalService 


Цитата(baldina @  28.11.2012,  10:07 Найти цитируемый пост)
код C++

class Service {
  private:
    Socket s;
  public:
    Service (const string& ip) : s(ip) {}
};
class LocalService : public Service {
  public:
    LocalService () : Service ("127.0.0.1") {}
};



Пожалуй,в каждом классе сделаю по сокету-чтобы не путаться.

Могу я задать следующий вопрос?
Если да, то в процессе написания сделал абстрактный класс с чисто виртуальным методом func1() 

И в производном классе теперь его пробую переопределить.Но,возможно,делаю это неправильно.

Абстрактный Класс
Код

virtual void getValule()=0;


В Производном классе 

Код


void Proizv_class::getValue(int i)
{
return i;
}

В хедере производного класса я написал прототип этой функции.
У меня закрадывается смутное подозрение,что в результате базовая функция не используется вообще,даже как чисто виртуальная.То есть это две разные функции,не связанные наследованием.

Это так? А если бы я и там и там написал бы getValue()?

Только начал вплотную заниматься наследованием,в меру сил читаю Страуструпа,но все-таки вопросы остаются.

Спасибо большое за ответы.
PM MAIL   Вверх
baldina
Дата 28.11.2012, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(ЛунныйОборотень @  28.11.2012,  13:14 Найти цитируемый пост)
У меня закрадывается смутное подозрение,что в результате базовая функция не используется вообще,даже как чисто виртуальная.

сигнатуры функций (число и тип аргументов, возвращаемого значения) должны совпадать, иначе они считаются различными функциями.
Код

// две _разные_ функции bar()
struct Foo {
  int bar(int);
  int bar(float);
};


пример с виртуальными функциями
Код

struct Shape {
  virtual void draw() = 0;
};

struct Circle : public Shape {
  void draw() { cout << "i'm circle\n"; }
};
struct Rect : public Shape {
  void draw() { cout << "i'm rectangle\n"; }
};

int main () {
  Shape *shapes[] = { new Circle, new Rect };
  for (int i=0; i < 2; ++i)
    shapes[i]->draw();
}


Цитата(ЛунныйОборотень @  28.11.2012,  13:14 Найти цитируемый пост)
Пожалуй,в каждом классе сделаю по сокету-чтобы не путаться.

зря. зачем делать лишнюю работу? если использовать только наследование (без полиморфизма), базовый класс является как-бы шаблоном для производного.
классы - для компилятора, в процессе выполнения нет никаких классов, лишь объекты. переменная типа производного класса включает в себя свои переменные и функции, а также переменные и функции базовых классов.
PM MAIL   Вверх
ЛунныйОборотень
Дата 28.11.2012, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 174
Регистрация: 12.11.2006

Репутация: нет
Всего: 1



То есть,определяя базовый класс и потом вызывая его через производный ,мы создаем непересекающиеся множества.
И если,допустим ,результатом выполнения этой функции будет какое то значение,записанное в переменную в базовом классе,

эта переменная будет как бы в параллельных мирах?))

То же самое и с сокетом?Название и определение в одном месте(базовом классе),а значения и параметры этого сокета будут разными для каждого класса?
PM MAIL   Вверх
Arantir
Дата 28.11.2012, 14:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 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
}
PM   Вверх
baldina
Дата 28.11.2012, 15:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Arantir @  28.11.2012,  14:44 Найти цитируемый пост)
А дочерний класс может всяко-разно взаимодействовать с базовым. Переопределенные функции тоже можно вызвать в их оригинале через parent.

так ТС запутается еще больше. нет никакого взаимодействия дочернего класса с базовым. 
содержимое базового класса целиком входит в производный класс. технически они рядом, синтаксически мы в некоторых случаях должны указывать, что используется переменная или функция, определенная в базовом классе.

рассмотрим вот такую структуру данных: { 1, { 2, {3} } }. 
она может описываться, например, так:
Код

struct foo {
  int v0;
};

struct bar {
  foo f;
  int v1;
};

struct baz {
  bar b;
  int v2;
};

int main () {
  baz z;
  z.b.f.v0 = 3;
  z.b.v1 = 2; 
  z.v2 = 1; 
}


это - композиция. если по смыслу задачи baz не просто включает в себя данные bar (и foo), а является расширением понятия bar (и foo),
можно использовать наследование.

Код

struct foo {
  int v0;
};

struct bar : public foo {
  int v1;
};

struct baz : public bar {
  int v2;
};

int main () {
  baz z;
  z.v0 = 3; // foo::v0
  z.v1 = 2; // bar::v1
  z.v2 = 1; 
}

с точки зрения хранимых данных оба описания эквивалентны. использование наследования нам дает, во-первых, более короткую запись, во-вторых, возможность обращаться с объектами типа baz как с объектами типа bar и foo без явного преобразования типов.

Это сообщение отредактировал(а) baldina - 28.11.2012, 15:09
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0946 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.