LINUX.ORG.RU

[С++][полиморфизм] цепочка наследований


0

1

Дорогие друзья,

Объясните пожалуйста феномен:

class A // base
{
public:
    virtual void method()
    {
        std::cout << "A";
    }
};

class B : public A // B->A
{
public:
    void method()
    {
        std::cout << "B";
    }
};

class C : public B // C->B->A
{
public:
    void method()
    {
        std::cout << "C";

    }
};
...
...
int main(...)
{
   ....
   A * a = new C;
   a->method(); // печатает C
}

.... не первый год использую плюсы в повседневности, но ... почему вызывается не B::method() ? Какая здесь логика?


Метод же виртуальный.

Если в каком либо из предков метод с учетом всех параметров виртуальный, то он виртуальный во всех потомках

namezys ★★★★ ()
Ответ на: комментарий от namezys

То есть, если например в классе A какой-то protected метод виртуальный, класс B его переопределяет, а разработчик класса C не затруднился просмотреть все protected методы класса B и A и определяет свой метод с таким же названием и сигнатурой, то он может ловить прикольные баги несколько месяцев?

LORd ()
Ответ на: комментарий от LORd

Если ты наследуешь от B, будь добр посмотри A, или пишу уже на своём VB и не выноси нормальным людям мозг.

erfea ★★★★★ ()
Ответ на: комментарий от LORd

Не нужно злоупотреблять наследованием, любая хорошая вещь, хороша в меру. Литр-два пива - хорошо, десять - плохо.

erfea ★★★★★ ()
Ответ на: комментарий от anonymous

> В нормальных языках существует для этого final

и еще override

kamre ★★★ ()

А зачем вообще ты в C определяешь method(), если в B он уже есть и ты ожидаешь того что он не виртуален?

KblCb ★★★★★ ()
Ответ на: комментарий от namezys

Обычно надо подразуменвать, что любой метод виртуален

так можно напороться на другие грабли :)

так что:

Думать надо.

shty ★★★★★ ()
Ответ на: комментарий от namezys

Видать завидуют, пытаются освоить, а мозг не так устроен. Привычка что ЯП думает за программиста не вышибаема впринципе.

erfea ★★★★★ ()
Ответ на: комментарий от archimag

Есть мнение, что как раз частью public интерфейса они и не должны быть.


class IA {
public:
  void f()
  { _f(); }

private:
  virtual void _f() = 0;
};

Одно и тоже, с этой точки зрения

namezys ★★★★ ()
Ответ на: комментарий от namezys

> ну барьер там поставить. что еще? больше не вижу особого

смысла скрывать виртуальные методы


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

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

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

Как-то так, но тема довольно тонкая, лучше Саттера на эту тему почитать )

archimag ★★★ ()

потому что знание ассемблера решает.

класс = структура с адресами виртуальных переменных(VTable) + сведения для линковщика где искать невиртуальные функции. экземпляр класса А включает в себя указатель на VTable если есть хотя бы малейшее подозрение на наличие виртуальных функций

#include <stdio.h>
class A {
public: 
	A() {}
	~A(){}
	virtual void f() {
		printf("1");
	}
};
class B : public A {
public:
	B() {}
	~B() {}
	void f() {
		printf("2");
	}
 	int a;
};
class C : public B {
public:
	C() {}
	~C() {}
	void f() {
		printf("3");
	}
};
class D {
public:
	D(){} 
	~D(){}
	int a, b, c, d;
};
int main()
{
	printf("sizeof(A)=%lu, sizeof(A*)=%lu\n", sizeof(A), sizeof(A*));
	printf("sizeof(B)=%lu, sizeof(B*)=%lu\n", sizeof(B), sizeof(B*));
	printf("sizeof(C)=%lu, sizeof(C*)=%lu\n", sizeof(C), sizeof(C*));
	printf("sizeof(D)=%lu, sizeof(D*)=%lu\n", sizeof(D), sizeof(D*));
	return 0;
}

запускаем:

$ ./test
sizeof(A)=4, sizeof(A*)=4
sizeof(B)=8, sizeof(B*)=4
sizeof(C)=8, sizeof(C*)=4
sizeof(D)=16, sizeof(D*)=4
VTablя для С переопределяется вся, и С несет в себе указатель на неё а не на VTablю для B

ckotinko ☆☆☆ ()
Ответ на: комментарий от archimag

> Как-то так, но тема довольно тонкая, лучше Саттера на эту тему почитать )

Действительно, тонкий. И о много чем можно поспорить. Мне такой подход не нравится, да и много кому

namezys ★★★★ ()
Ответ на: комментарий от ckotinko

> класс = структура с адресами виртуальных переменных(VTable)

Да ладно? А по русски хоть написать можно, и то это частный случай.

namezys ★★★★ ()
Ответ на: комментарий от ckotinko

обавь в А int i; и после компиляции получишь

$ ./test 
sizeof(A)=8, sizeof(A*)=4 
sizeof(B)=12, sizeof(B*)=4 
sizeof(C)=12, sizeof(C*)=4 
sizeof(D)=16, sizeof(D*)=4

размер пустого класса никогда не равен нулю, обычно он равен sizeof(int) или sizeof(long). а размер не пустого класса зависит только от раскладки переменных и наличия указалеля на Vtable на борту

ckotinko ☆☆☆ ()

.... не первый год использую плюсы в повседневности, но ... почему вызывается не B::method() ? Какая здесь логика?

Что значит "не первый год использую"? Не первый год на полке книжка по плюсам стоит? Или не первый год встречаешь в интернетах упоминание C++?

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

И, главное, почему-то тебе не пишут "иди в школу, неуч"... это меня даже больше удивляет, чем тот факт, что ты неуч.

Nightmare ()
Ответ на: комментарий от Nightmare

почему-то тебе не пишут «иди в школу, неуч»... это меня даже больше удивляет

это всё оттого, что мы очень, очень хорошие

jtootf ★★★★★ ()
Ответ на: комментарий от Nightmare

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

Можно всю жизнь при переопределении виртуальных методов писать явно virtual. Сам я удивился, что компилятор не выдал ошибки.

И, главное, почему-то тебе не пишут «иди в школу, неуч»... это меня даже больше удивляет, чем тот факт, что ты неуч.

Ну зачем использовать настолько темные стороны языка? Какой нормальный человек может о них знать?

ival ★★ ()
Ответ на: комментарий от ival

Можно всю жизнь при переопределении виртуальных методов писать явно virtual.

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

Nightmare ()
Ответ на: комментарий от ival

Ну зачем использовать настолько темные стороны языка? Какой нормальный человек может о них знать?

Я вот не понял, это ирония такая? Или ты серьёзно сейчас?

Nightmare ()
Ответ на: комментарий от Nightmare

Я вот не понял, это ирония такая? Или ты серьёзно сейчас?

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

Кроме С++ есть и другие языки, в которых тоже есть виртуальные функции. Что проще, всегда все писать явно простыми конструкциями, или помнить по десятку вариантов поведения для полсотни ситуаций?

ival ★★ ()
Ответ на: комментарий от ival

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

Сидел штоле?

Кроме С++ есть и другие языки, в которых тоже есть виртуальные функции. Что проще, всегда все писать явно простыми конструкциями, или помнить по десятку вариантов поведения для полсотни ситуаций?

Ну ведь ты как-то слова, которыми сейчас пишешь, помнишь. Китайцы как-то стопятьсот иероглифов помнят. А тут, оказывается, полсотни ситуаций - большая проблема...

Nightmare ()
Ответ на: комментарий от Nightmare

> Сидел штоле?

О, Петросян появился. Бабища у тебя страшная кстати, постыдился бы ее на аватарку пихать.

Ну ведь ты как-то слова, которыми сейчас пишешь, помнишь. Китайцы как-то стопятьсот иероглифов помнят. А тут, оказывается, полсотни ситуаций - большая проблема...

Красноглазые задроты вроде тебя может и помнят все закоулки C++, на деле 90% из них используется очень редко.

anonymous ()
Ответ на: комментарий от LORd

а разработчик класса C не затруднился просмотреть все protected методы класса B и A

Хороший стиль – объявлять метод как виртуальный и в B тоже, чтобы тому, кто от B наследуется, не было нужды заглядывать в A. На скомпилированный код это не влияет, а вот читать будет проще.

Miguel ★★★★★ ()
Ответ на: комментарий от anonymous

О, Петросян появился. Бабища у тебя страшная кстати, постыдился бы ее на аватарку пихать.

Когда заканчиваются аргументы, ведёшь себя, как быдло (переходишь на личности)?

Красноглазые задроты вроде тебя может и помнят все закоулки C++, на деле 90% из них используется очень редко.

Почему тугоголовые пареньки всё, что выходит за рамки их маленького мирка, называют задротством? Что это? Зависть?

И, да, я повторюсь, это не «закоулок» C++, это банальная вещь, которую должен знать каждый программист (программирующий на C++).

Nightmare ()

> не первый год использую плюсы в повседневности

Я так понимаю, что C++ использовался, как «C на стероидах». Иначе я не вижу этому объяснения.

DELIRIUM ☆☆☆☆☆ ()
Ответ на: комментарий от LORd

> На самом деле использовался он только совместно с Qt

Same shit, вот только почему-то мне это не помешало знать о таком поведении языка.

rival ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.