У меня есть задание объявить класс Pet, класс Dog, класс Fish и класс Bird. У разных питомцев одинаковые методы, но некоторые характеристики разные. Я не хотел писать одно и то же три раза, только с тремя разными статистиками каждый раз.
Поэтому я научил меня создавать класс Pet, я создаю файл шаблона, который требует название вида и характеристики, используя директиву define препроцессора. Но я получаю эту ошибку во время компиляции:
C2100 — недопустимое косвенное обращение ProjectName c: \ program files (x86) \ microsoft visual studio \ 2017 \ enterprise \ vc \ tools \ msvc \ 14.12.25827 \ include \ xtree 1146
У меня есть следующие файлы:
Pet.h
#pragma once
#include <string>
#include <map>
class Pet {
protected:
std::string Name;
unsigned int Vim;
unsigned int PositiveEffectSize;
std::map<int, unsigned int> NegativeEffectsSize;
public:
enum EffectSize { small, big };
Pet(std::string name, unsigned int vim);
Pet() {};
std::string GetName() const;
unsigned int GetVim() const;
void InflictPositiveEffect();
void InflictNegativeEffect(EffectSize effectSize);
};
PetInstanceTemplate.h
#include "Pet.h"#ifndef _SpeciesName
#error "SpeciesNotDefined"#endif
class _SpeciesName : Pet {
protected:
unsigned int PositiveEffectSize = _PositiveEffectSize;
std::map<int, unsigned int> NegativeEffectsSize;
public:
_SpeciesName(std::string name, unsigned int vim) : Pet(name, vim) {
this->NegativeEffectsSize.insert(0, _NegativeEffectSizeSmall);
this->NegativeEffectsSize.insert(1, _NegativeEffectSizeBig);
}
_SpeciesName() {};
};
Dog.h, но файл заголовка других домашних животных похож
#pragma once
#undef _SpeciesName
#undef _PositiveEffectSize
#undef _NegativeEffectSizeSmall
#undef _NegativeEffectSizeBig
#define _SpeciesName Dog
#define _PositiveEffectSize 3
#define _NegativeEffectSizeSmall 0
#define _NegativeEffectSizeBig 10
#include "PetInstanceTemplate.h"
Есть ли способ заставить его работать таким образом? И каковы другие методы уменьшения избыточности определения класса?
Заранее спасибо 🙂
Вы бы не получили много, если бы все методы остались прежними, но вы могли бы использовать списки инициализации и обращаться ко всему через Pet *
class Pet {
public: //Pet's constructor can be protected, if you only want derived classes to create them
Pet(std::string initName="", std::string initSound="", std::string initCoat="");
virtual ~Pet(){} //Only needed if deleting derived class through Pet*
std::string getName(){ return name; }
private:
std::string name;
std::string sound;
std::string coat;
};
class Bird : public Pet {
public:
Bird();
~Bird() override {} //Overrides virtual ~Pet, if used
private:
int wingSpan;
};
Тогда в реализации:
Pet::Pet(std::string initName, std::string initSound, std::string initCoat)
: name(initName), sound(initSound), coat(initCoat){}
Bird::Bird()
: Pet::Pet("Bird","Chirp","Feathers"), wingSpan(6){}
Я понял, что то, что я хотел сделать, не имеет большого смысла. Поэтому я согласился на это:
я удалил unsigned int PositiveEffectSize
а также std::map<int, unsigned int> NegativeEffectSizes
из производного класса, если честно, я не знаю, зачем я его туда поместил … Я изменил конструктор класса Pet, поэтому мой конструктор производного класса просто вызывал конструктор базового класса.
Pet.h
#pragma once
#include <string>
#include <map>
class Pet {
protected:
std::string Name;
unsigned int Vim;
unsigned int PositiveEffectSize;
std::map<int, unsigned int> NegativeEffectsSize;
public:
static enum EffectSize { Small, Big };
Pet(std::string& name, unsigned int vim, unsigned int positiveEffectSize, unsigned int negativeEffectSizeSmall, unsigned int negativeEffectSizeBig);
Pet() {};
std::string GetName() const;
unsigned int GetVim() const;
void InflictPositiveEffect();
void InflictNegativeEffect(EffectSize effectSize);
};
Dog.h
#pragma once
#include "Pet.h"#define _PositiveEffectSize 3
#define _NegativeEffectSizeSmall 0
#define _NegativeEffectSizeBig 10
class Dog : Pet {
public:
Dog(std::string name, unsigned int vim) : Pet(name, vim, _PositiveEffectSize, _NegativeEffectSizeSmall, _NegativeEffectSizeBig) {}
};
Спасибо за помощь и идеи 🙂
Это может быть немного старой школы, но это сэкономит огромные деньги, так что имейте это в виду
Вы можете использовать шаблоны, но это может быть очень тяжело для чего-то, что останется статичным. Так что, если вы не планируете что-либо менять во время выполнения (без компиляции), вы можете просто объявить static. Основным преимуществом этого является то, что вы можете заранее знать, сколько данных у вас есть и сколько памяти вы собираетесь использовать. Это также выглядит очень чисто и действует как таблица для ввода данных. Недостатком, очевидно, является то, что вы должны объявлять все переменные во время компиляции. И если у вас в значительной степени разные методы, это может стать очень и очень уродливым в методологии. Вы можете исправить это, создав классы логики, которые обрабатывают операционные различия.
enum { dog = 0, cat, bird, ever_other_member, MAX_PETS }
static struct petinfo
{ const char * name, uint next, next2; char *etc; //all your vars
const char *getpetname : const(); void setpetname(const char *); //all the functions
//now if you dont conclude with a semicolon you can define an array which are the only instances of this object allow.
} pets[MAX_PETS] =
{
{ "dog", 4, 5, "this lines up with the variable order declared above" }
{ "cat", 3, 4, "" }
{ "bird", 10, 20, "" }
{ "other one", 0, 0 , "" }
}; //semicolon here ends the class
Затем в вашем файле cpp (или в любом другом месте) вы можете объявить свои функции выше.
const char *petinfo::getpetname : const() { return name; }
void petinfo::setpetname(const char *n) { name = n; }
Это также позволит вам объявить статические переменные, которые являются постоянными для всех ваших питомцев. Вы бы просто объявили их в вашем списке переменных как:
static const bool wild = false; //I think this is a poor example
Для доступа к этим данным вы просто:
pets[dog].getname();
или перебрать их.
for(int i = 0; i < MAX_PETS; i++) print(pets[i].getname());
Если у вас есть объявленный метод, например, talk, вы можете справиться с этим несколькими различными способами, но для простой реализации вы можете просто использовать систему case.
void speak(uint type)
{
switch(type)
{
case dog: print("bark"); break;
case cat: print("meow"); break;
case bird: print("chirp"); break;
}
}
Это главная слабость этого типа. Если у вас большая разница в функциональности по сравнению с большой разницей между переменными-членами, вам лучше использовать либо наследование, как вы делали выше, либо использование шаблонов. Основная сила этого метода в том, что если у вас много переменных с разной статистикой, но очень схожей функциональностью. Главным образом потому, что это намного меньше, чем при использовании шаблонов, и его очень легко читать и редактировать без огромного количества кода. Кроме того, если у всех ваших собак одинаковые базовые характеристики, это можно использовать в качестве отправной точки.
chasester