#c
Вопрос:
У меня есть Base
класс, который затем наследуется Foo
и Foo
, в свою очередь, передается Bar
.
Вот файл заголовка base.h
для моего Base
класса:
#pragma once
class Base {
public:
void runExample();
private:
virtual void print();
};
И осуществление base.cpp
:
#include "base.h"
void Base::runExample() {
print();
}
void Base::print() {};
Вот файл заголовка foo.h
для моего Foo
класса:
#pragma once
#include "../base/base.h"
class Foo : public Base {
public:
Foo();
private:
void print();
const char* toPrint;
};
И осуществление foo.cpp
:
#include "foo.h"
Foo::Foo() {
toPrint = "Hello Foo";
}
void Foo::print() {
std::cout << toPrint << std::endl;
}
Вот файл заголовка bar.h
для моего Bar
класса:
#pragma once
#include "../foo/foo.h"
class Bar : public Foo {
public:
Bar();
private:
const char* toPrint; // "overrides" toPrint from parent class Foo?
};
И осуществление bar.cpp
:
#include "bar.h"
Bar::Bar() {
toPrint = "Hello Bar";
}
И, наконец, моя главная функция:
#include "../bar/bar.h"
int main() {
Bar bar = Bar();
bar.runExample();
}
Итак, у меня есть такого рода отношения между классами:
Bar
есть а Foo
, которое есть а Base
.
То, что я ожидал увидеть на выходе, было «Привет, бар», но на самом деле я вижу «Привет, Фу».
Если я «переопределю» print
метод, объявленный в Foo
in Bar
для печати toPrint
, то я получу ожидаемый результат, однако это, похоже, нарушает точку наследования; т. Е. Зачем мне нужно переопределять функциональность, когда она уже определена.
Я ожидаю, что при Bar.print()
вызове он использует Foos
реализацию, но фактическое поле toPrint
было «заменено» значением внутри Bar
реализации.
Я очень новичок в C , и я родом из Котлина. Простите меня, если это похоже на основы C 101, но я довольно смущен тем, почему это происходит. Может ли кто-нибудь указать мне правильное направление?
Комментарии:
1. Вы не можете «переопределять» переменные-члены.
Ответ №1:
В C элементы данных не могут быть «переопределены», как вы ожидаете, и ссылка на toPrint
них не будет динамически привязана к элементам данных с одинаковыми именами в подклассах.
С
class Bar : public Foo {
...
const char* toPrint; // "overrides" toPrint from parent class Foo?
};
вы вводите переменную-член Bar::toPrint
рядом с Foo::toPrint
, которая наследуется. Код
void Foo::print() {
std::cout << toPrint << std::endl;
}
всегда будет придерживаться toPrint
-члена в его области действия, Foo::toPrint
т. е.
Следующий код иллюстрирует это поведение:
struct Base {
const char* toPrint = "Base";
virtual void print() const { cout << toPrint << std::endl; }
};
struct Derived: public Base {
const char* toPrint = "Derived";
void printBoth() const { cout << "own:" << toPrint << "; inherited: " << Base::toPrint << std::endl; }
};
int main() {
Derived d;
cout << "call inherited method print:" << std::endl;
d.print();
cout << "call method printBoth, showing both data members:" << std::endl;
d.printBoth();
}
Выход:
call inherited method print:
Base
call method printBoth, showing both data members:
own:Derived; inherited: Base
Комментарии:
1. Да, это имеет смысл, но, если я введу
toPrint
Foo
защищенную переменную-член и удалю переопределениеtoPrint
fromBar
, то она действительно будет «переопределена» конструкторомBar
s2. Если вы удаляете
Bar::toPrint
, то вы наследуете элемент данныхFoo::toPrint
и можете получить к нему доступtoPrint
; вы не переопределяете его, вы просто присваиваете другое значениеFoo::toPrint
в конструктореBar
.3. Ага, я вижу, в этом есть смысл. Я думаю, что я довольно сильно пукаю мозгами, потому что у меня перегрузка информацией из-за изучения нового синтаксиса накладные расходы, связанные с тем, что все это совсем другое/новое по сравнению с Java/Kotlin land. Спасибо за твою помощь, Стефан!
4. вы даже можете сделать оба
toPrint
варианта общедоступными; компилятор не будет жаловаться, такFoo::toPrint
как отличается отBar::toPrint
. Никакого переопределения, никакого столкновения имен в элементах данных. См.Расширенный ответ.5. Это просто правила 🙂