Mihairu
@Mihairu
web-программист

Как получить значение поля дочернего класса родительским методом?

Здравствуйте. Изучаю c++, и столкнулся с проблемой, при которой я не могу получить значение переменной.

#include <iostream>
#include <string>
#include <iomanip>

using namespace std;

enum MagicType {
	fire,
	water,
	earth,
	air
};

struct MagicSpellInfo {
	public:
		string Name;
		string Description;
		MagicType Type;
		float MinDamage;
		float MaxDamage;
		float Distance;
		float Range;

		MagicSpellInfo() :
			Name(""), Description(""), MinDamage(0.0), MaxDamage(0.0), Distance(0.0), Range(0.0), Type(fire) {}

		MagicSpellInfo(string _name, string _dsc, float _mindmg, float _maxdmg, float _dst, float _rng, MagicType _type) : 
			Name(_name), Description(_dsc), MinDamage(_mindmg), MaxDamage(_maxdmg), Distance(_dst), Range(_rng), Type(_type) {}
};

class IMagicSpell {
	private:
		MagicSpellInfo SpellInfo;

	public:
		virtual ~IMagicSpell() {}
		virtual void Use() {}

		MagicSpellInfo GetInfo() {
			return this->SpellInfo;
		}

		void ShowDebugInfo() {
			cout << "Название: " << SpellInfo.Name << endl;
			cout << "Описание: " << SpellInfo.Description << endl;
			cout << "Тип: " << SpellInfo.Type << endl;
			cout << "Мин. урон: " << SpellInfo.MinDamage << endl;
			cout << "Макс. урон: " << SpellInfo.MaxDamage << endl;
			cout << "Расстояние: " << SpellInfo.Distance << endl;
			cout << "Радиус: " << SpellInfo.Range << endl;
		}
};

class Fireball: public IMagicSpell {
	private:
		MagicSpellInfo SpellInfo;

	public:
		Fireball() {
			SpellInfo.Name = "Огненный шар";
			SpellInfo.Description = "Мощный огненный шар, сметающий все на своем пути.";
			SpellInfo.Type = fire;
			SpellInfo.MinDamage = 30;
			SpellInfo.MaxDamage = 50;
			SpellInfo.Range = 40;
			SpellInfo.Distance = 120;
		}

		virtual ~Fireball() {}

		virtual void Use() {
			cout << "Boom!" << endl;
		}
};

int main() {
	setlocale(LC_ALL, "Russian");

	Fireball fireball;
	fireball.ShowDebugInfo();
	
	cin.get();
	return 0;
}


На выходе я получаю следующее:
Название:
Описание:
Тип: 0
Мин. урон: 0
Макс. урон: 0
Расстояние: 0
Радиус: 0


Хотя, как очевидно, мне нужно получить данные, указанные в конструкторе Fireball.
Подскажите, что не так?
  • Вопрос задан
  • 150 просмотров
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Метод IMagicSpell::ShowDebugInfo у тебя обращается к приватному полю IMagicSpell::SpellInfo. Это поле недоступно для наследников, т.к. является приватным.
Конструктор-же, Fireball::Fireball, у тебя работает с приватным полем Fireball::SpellInfo.
Поля IMagicSpell::SpellInfo и Fireball::SpellInfo являются разными, расположены в разных областях памяти объекта Fireball и в принципе никак не могут пересечься своими данными.
Размер твоего Fireball будет с два MagicSpellInfo плюс размер таблицы виртуальных символов.

От причины можно перейти к достижению желаемого поведения.
Самое главное - второй экземпляр MagicSpellInfo в иерархии - лишний. Fireball::SpellInfo является дублирующим, лишним полем, которое не позволяет добиться необходимой тебе функциональности. Ведь в обоих твоих классах тебе надо работать с одними данными, с полем IMagicSpell::SpellInfo.
Просто удаляем Fireball::SpellInfo.
Fireball
class Fireball: public IMagicSpell {
  public:
    Fireball() {
      SpellInfo.Name = "Огненный шар";
      SpellInfo.Description = "Мощный огненный шар, сметающий все на своем пути.";
      SpellInfo.Type = fire;
      SpellInfo.MinDamage = 30;
      SpellInfo.MaxDamage = 50;
      SpellInfo.Range = 40;
      SpellInfo.Distance = 120;
    }

    virtual ~Fireball() {}

    virtual void Use() {
      cout << "Boom!" << endl;
    }
};


Но теперь мы не скомпилируемся. Компилятор напишет что IMagicSpell::SpellInfo является приватным и недоступен для потомков. Исправить это тоже очень легко.
Есть три вида секций доступа к полям и функциям класса:
  1. private - приватный доступ, только для экземпляра текущего класса;
  2. protected - ограниченный доступ, только для экземпляров текущего класса и всех публичных наследников; доступ ограничивается при ограниченном или приватном наследовании;
  3. public - публичный доступ, для всех пользователей экземпляров класса.

Нам надо сменить права доступа к IMagicSpell::SpellInfo с приватного на ограниченный. В этом случае экземпляры Fireball смогут обращаться к IMagicSpell::SpellInfo как к своему ограниченному полю.
IMagicSpell
class IMagicSpell {
  protected:
    MagicSpellInfo SpellInfo;

  public:
    virtual ~IMagicSpell() {}
    virtual void Use() {}

    MagicSpellInfo GetInfo() {
      return this->SpellInfo;
    }

    void ShowDebugInfo() {
      cout << "Название: " << SpellInfo.Name << endl;
      cout << "Описание: " << SpellInfo.Description << endl;
      cout << "Тип: " << SpellInfo.Type << endl;
      cout << "Мин. урон: " << SpellInfo.MinDamage << endl;
      cout << "Макс. урон: " << SpellInfo.MaxDamage << endl;
      cout << "Расстояние: " << SpellInfo.Distance << endl;
      cout << "Радиус: " << SpellInfo.Range << endl;
    }
};


В таком виде ты сможешь добиться желаемой функциональности. Но куда более важно чтобы тебе было понятно, как именно ты этого добился.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@EShaRA
Получить значение поля дочернего класса родительским методом нельзя - так как родительский метод про поля дочернего класса ничего не знает;
В Вашем случае возможно объявить функцию ShowDebugInfo как virtual в родительском классе и реализовать (переопределить) этот метод в дочернем;
Однако проблема может быть глубже, дело в том, что в вашем дочернем классе фактически присутствуют два поля MagicSpellInfo SpellInfo - один в родительском и один в дочернем (поле родительского класса объявлено private и потому недоступно в дочернем); Возможно стоит объявить MagicSpellInfo SpellInfo protected в родительском классе, и удалить дубликат из дочернего;
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы