Другие языки программирования и технологии

Вопрос про полиморфизм в С++.

Чет затупил однако. Думал он работает примерно по принципам наследования, но походу не так.
К примеру есть класс с виртуальным деструктором и виртуальными методами. У этого класса есть подкласс, у подкласса еще подкласс и так далее. Пусть они называются в алфавитной последовательности для простоты: A -> B -> C -> D -> ...где класс A будет являться базовым.
Если я создам класс С, то я смогу иметь доступ ко всем методам классов A, B и C, ну условно говоря так:
C ccc;
ccc.A::f(); // Доступ к методу f класса A из которого могу получить доступ к приватным и защищенным полям класса А.
ccc.B::f(); // Доступ к методу f класса B из которого могу получить доступ к приватным и защищенным полям класса B.
ccc.f(); // Доступ к методу f класса C из которого могу получить доступ к приватным и защищенным полям класса C.
С этим все понятно, думал с полиморфизмом будет также, но написав условно говоря
A & aaa = ccc;
Получается, что я не имею возможностей получить доступ к полям промежуточных классов стоящих между базовым и приведенным, то есть в данном случае я не смогу уже обратиться к методу класса B, то есть следующая запись является не рабочей:
aaa.B::f();
При всем при этом я все таки получаю доступ к методу промежуточного класса на стадии создания объекта указав вызов виртуальной функции из конструктора (что конечно не хорошо), но все же.
Получается экземпляр есть, а доступа к промежуточным классам нет? или возможно синтаксис выглядит иначе? Если да, то как вызвать метод класса B в данном случае?
ОБ
Олег Бирюков
10 755
Смысл полиморфизма заключается в том, чтобы обрабатывать разнотипные данные.

#include <corecrt_math_defines.h>;
#include <iostream>
#include <string>
using namespace std;
class Shape {
public:
Shape()
: name_("Unknown"s) { }
virtual double area()const = 0;
virtual double perimeter()const = 0;
string name()const { return name_; }
virtual ~Shape() { }
protected:
string name_;
};
class Circle : public Shape {
public:
Circle() = delete;
Circle(const double radius)
: radius_(radius) { name_ = "Circle"s; }
virtual double area()const { return M_PI * radius_ * radius_; }
virtual double perimeter()const { return 2.0 * M_PI * radius_; }
string name()const { return name_; }
private:
double radius_;
};
class Rectangle : public Shape {
public:
Rectangle() = delete;
Rectangle(const double width, const double height)
: width_(width), height_(height) { name_ = "Rectangle"s; }
virtual double area()const { return width_ * height_; }
virtual double perimeter()const { return (width_ + height_) * 2.0; }
string name()const { return name_; }
private:
double width_;
double height_;
};
class Triangle : public Shape {
public:
Triangle() = delete;
Triangle(const double a, const double b, const double c)
: a_(a), b_(b), c_(c) { name_ = "Triangle"s; }
virtual double area()const {
double p = perimeter() / 2.0;
return sqrt(p * (p - a_) * (p - b_) * (p - c_));
}
virtual double perimeter()const { return a_ + b_ + c_; }
string name()const { return name_; }
private:
double a_, b_, c_;
};
int main() {
const Circle circle(2.7);
const Rectangle rectangle(4.3, 3.8);
const Triangle triangle(1.7, 2.6, 1.9);
const auto limit = 3U;
// по указателю на базовый класс создаём массив объектов производных классов
const Shape* box[limit];
box[0U] = &circle;
box[1U] = &rectangle;
box[2U] = &triangle;
// выводим результат
for (const auto &item : box)
cout
<< item->name() << ": S = "
<< item->area() << "; P = "
<< item->perimeter() << '\n';
system("pause");
}
Mamathonov Farruh
Mamathonov Farruh
86 693
Лучший ответ
Олег Бирюков Это стандартное по сути применение технологии. Просто переопределение методов в подклассах. Но в общем то я понял... Доступ будет только к базовому и ссылающемуся классу но не более того. Память классов между ссылающимся и базовым по сути впустую будет резервироваться. вот что было мне странно....
Ты можешь сделать downcast до низлежащего класса, например. Необязательно (а на мой взгляд, ещё и некрасиво) использовать для этого скоуп, или как эта конструкция - ".B::" - называется.
Олег Гаврилов
Олег Гаврилов
51 164
Олег Бирюков Иногда полезно если нужно вызвать из переопределенного метода еще и базовый метод или вышестоящего класса (но в основном базового)...

А что за downcast, я о таком не слышал?
Засунь данные родителей в protected
Олег Бирюков Условно говоря вот такой вот пример начеркал:
https://pastebin.com/S7S21dAE
A & aaa = ccc; //то есть вы создаете ссылку на базовый класс и хотите получить метод производного класса? Я вас правильно понимаю? Ну как бы это не так работает насколько я понимаю.
Основная логика класса это то что производный класс это разновидность базового класса. Например в игре есть класс unit. Это базовый класс и имеет виртуальный метод draw() и данные health.
Олег Бирюков Да, но только не класса С и А, а хочу получить метод промежуточного класса B как будто бы это наследование.
когда ты пишешь new С (); то создается объект С (соответственно выполняются конструкторы).
Потом ты его рассматриваешь как объект класса А (Он им также является). При этом хоть объект и
существует, но тип ссылки об этом ничего не знает и соответственно прямого доступа у тебя нет, но ты можешь получить его выполнив небезопасное приведение к В или С. В Оперативной памяти объект хранится так что его можно читать условно сверху вниз. Сначала информация о базовых классах, потом о производных и так далее. Тип ссылки указывает также на то сколько памяти нужно прочитать.
Dima Gorbaluyk
Dima Gorbaluyk
11 032
Олег Бирюков вы про reinterpret_cast имеете ввиду? Ес-но к классу B можно привести, но это не совсем то, чего мне бы хотелось бы... щас попробую поколдовать конечно...