Pokemon.h:
#pragma once
#include <string>
class Pokemon {
public:
std::string type;
double weight, height;
std::string Gender;
int evoLevel;
bool finalEvo;
int dexNum;
std::string name;
Pokemon(std::string name2, std::string type2, double weight2, double height2, std::string Gender2, int evoLevel2, bool finalEvo2, int dexNum2);
Pokemon();
};
Pokemon .cpp:
#include "Pokemon.h"
Pokemon::Pokemon(std::string name2, std::string type2, double weight2, double height2, std::string Gender2, int evoLevel2, bool finalEvo2, int dexNum2) {
name = name2;
type = type2;
weight = weight2;
height = height2;
Gender = Gender2;
evoLevel = evoLevel2;
finalEvo = finalEvo2;
dexNum = dexNum2;
}
//Default constructer
Pokemon::Pokemon() {
name = "Pichario";
type = "Death";
weight = 10;
height = 12;
Gender = "Male and Female";
evoLevel = 1;
finalEvo = true;
dexNum = 999;
}
main.cpp:
#include <iostream>
#include <string>
#include <vector>
#include "Pokemon.h"
int main() {
//Create Pokemon objects
Pokemon bulbasaur("Bulbasaur", "Grass and Poison", 15.2, 28, "Male and Female", 1, false, 1);
Pokemon ivysaur("Ivysaur", "Grass and Poison", 28.7, 39, "Male and Female", 2, false, 2);
Pokemon venusaur("Venusaur", "Grass and Poison", 220.5, 79, "Male and Female", 3, true, 3);
Pokemon charmander("Charmander", "Fire", 18.7, 24, "Male and Female", 1, false, 4);
Pokemon charmeleon("Charmeleon", "Fire", 41.9, 44, "Male and Female", 2, false, 5);
Pokemon charizard("Charizard", "Fire and Flying", 199.5, 67, "Male and Female", 3, true, 6);
Pokemon squirtle("Squirtle", "Water", 19.8, 20, "Male and Female", 1, false, 7);
Pokemon wartortle("Wartortle", "Water", 49.6, 39, "Male and Female", 2, false, 8);
Pokemon blastoise("Blastoise", "Water", 188.5, 63, "Male and Female", 3, true, 9);
Pokemon caterpie("Caterpie", "Bug", 6.4, 12, "Male and Female", 1, false, 10);
Pokemon metapod("Metapod", "Bug", 21.8, 28, "Male and Female", 2, false, 11);
Pokemon butterfree("Butterfree", "Bug and Flying", 70.5, 43, "Male and Female", 3, true, 12);
Pokemon weedle("Weedle", "Bug and Poison", 7.1, 12, "Male and Female", 1, false, 13);
Pokemon kakuna("Kakuna", "Bug and Poison", 22, 24, "Male and Female", 2, false, 14);
Pokemon beedrill("Beedrill", "Bug and Poison", 65, 39, "Male and Female", 3, true, 15);
Pokemon pidgey("Pidgey", "Normal and Flying", 4, 12, "Male and Female", 1, false, 16);
Pokemon pidgeotto("Pidgeotto", "Normal and Flying", 66.1, 43, "Male and Female", 2, false, 17);
Pokemon pidgeot("Pidgeot", "Normal and Flying", 87.1, 59, "Male and Female", 3, true, 18);
Pokemon rattata("Rattata", "Normal", 7.7, 12, "Male and Female", 1, false, 19);
Pokemon raticate("Raticate", "Normal", 40.8, 28, "Male and Female", 2, true, 20);
Pokemon spearow("Spearow", "Normal and Flying", 4.4, 12, "Male and Female", 1, false, 21);
Pokemon fearow("Fearow", "Normal and Flying", 83.8, 47, "Male and Female", 2, true, 22);
Pokemon ekans("Ekans", "Poison", 15.2, 79, "Male and Female", 1, false, 23);
Pokemon arbok("Arbok", "Poison", 143.3, 138, "Male and Female", 2, true, 24);
Pokemon pikachu("Pikachu", "Electric", 13.2, 16, "Male and Female", 1, false, 25);
Pokemon raichu("Raichu", "Electric", 66.1, 31, "Male and Female", 2, true, 26);
Pokemon sandshrew("Sandshrew", "Ground", 26.5, 24, "Male and Female", 1, false, 27);
Pokemon sandslash("Sandslash", "Ground", 65, 39, "Male and Female", 2, true, 28);
Pokemon nidoranf("Nidoran female", "Poison", 15.4, 16, "Female", 1, false, 29);
Pokemon nidorina("Nidorina", "Poison", 44.1, 31, "Female", 2, false, 30);
Pokemon nidoqueen("Nidoqueen", "Poison and Ground", 132.3, 51, "Female", 3, true, 31);
Pokemon nidoranm("Nidoran Male", "Poison", 19.8, 20, "Male", 1, false, 32);
Pokemon nidorino("Nidorino", "Poison", 43, 35, "Male", 2, false, 33);
Pokemon nidoking("Nidoking", "Poison and Ground", 136.7, 55, "Male", 3, true, 34);
Pokemon clefairy("Clefairy", "Fairy", 16.5, 24, "Male and Female", 1, false, 35);
Pokemon clefable("Clefable", "Fairy", 88.2, 51, "Male and Female", 2, true, 36);
Pokemon vulpix("Vulpix", "Fire", 21.8, 24, "Male and Female", 1, false, 37);
Pokemon ninetales("Ninetales", "Fire", 43.9, 43, "Male and Female", 2, true, 38);
Pokemon jigglypuff("Jigglypuff", "Normal and Fairy", 12.1, 20, "Male and Female", 1, false, 39);
Pokemon wigglytuff("Wigglytuff", "Normal and Fairy", 26.5, 39, "Male and Female", 2, true, 40);
Pokemon zubat("Zubat", "Poison and Flying", 16.5, 31, "Male and Female", 1, false, 41);
Pokemon golbat("Golbat", "Poison and Flying", 121.3, 63, "Male and Female", 2, true, 42);
Pokemon oddish("Oddish", "Grass and Poison", 11.9, 20, "Male and Female", 1, false, 43);
Pokemon gloom("Gloom", "Grass and Poison", 19, 31, "Male and Female", 2, false, 44);
Pokemon vileplume("Vileplume", "Grass and Poison", 41, 47, "Male and Female", 3, true, 45);
Pokemon paras("Paras", "Bug and Grass", 11.9, 12, "Male and Female", 1, false, 46);
Pokemon parasect("Parasect", "Bug and Grass", 65, 39, "Male and Female", 2, true, 47);
Pokemon venonat("Venonat", "Bug and Poison", 66.1, 39, "Male and Female", 1, false, 48);
Pokemon venomoth("Venomoth", "Bug and Poison", 27.6, 59, "Male and Female", 2, true, 49);
Pokemon diglett("Diglett", "Ground", 1.8, 8, "Male and Female", 1, false, 50);
Pokemon dugtrio("Dugtrio", "Ground", 73.4, 28, "Male and Female", 2, true, 51);
Pokemon meowth("Meowth", "Normal", 9.3, 16, "Male and Female", 1, false, 52);
Pokemon persian("Persian", "Normal", 70.5, 39, "Male and Female", 2, true, 53);
Pokemon psyduck("Psyduck", "Water", 43.2, 31, "Male and Female", 1, false, 54);
Pokemon golduck("Golduck", "Water", 168.9, 67, "Male and Female", 2, true, 55);
Pokemon mankey("Mankey", "Fighting", 61.7, 20, "Male and Female", 1, false, 56);
Pokemon primeape("Primeape", "Fighting", 70.5, 39, "Male and Female", 2, true, 57);
Pokemon growlithe("Growlithe", "Fire", 41.9, 28, "Male and Female", 1, false, 58);
Pokemon arcanine("Arcanine", "Fire", 341.7, 63, "Male and Female", 2, true, 59);
Pokemon poliwag("Poliwag", "Water", 27.3, 24, "Male and Female", 1, false, 60);
Pokemon poliwhirl("Poliwhirl", "Water", 44.1, 39, "Male and Female", 2, false, 61);
Pokemon poliwrath("Poliwrath", "Water and Fighting", 119, 51, "Male and Female", 3, true, 62);
Pokemon abra("Abra", "Psychic", 43, 35, "Male and Female", 1, false, 63);
Pokemon kadabra("Kadabra", "Psychic", 124.6, 51, "Male and Female", 2, false, 64);
Pokemon alakazam("Alakazam", "Psychic", 105.8, 59, "Male and Female", 3, true, 65);
Pokemon machop("Machop", "Fighting", 43, 31, "Male and Female", 1, false, 66);
Pokemon machoke("Machoke", "Fighting", 155.4, 59, "Male and Female", 2, false, 67);
Pokemon machamp("Machamp", "Fighting", 286.6, 63, "Male and Female", 3, true, 68);
Pokemon bellsprout("Bellsprout", "Grass and Poison", 8.8, 28, "Male and Female", 1, false, 69);
Pokemon weepinbell("Weepinbell", "Grass and Poison", 14.1, 39, "Male and Female", 2, false, 70);
Pokemon victreebel("Victreebel", "Grass and Poison", 34.2, 67, "Male and Female", 3, true, 71);
Pokemon tentacool("Tentacool", "Water and Poison", 100.3, 35, "Male and Female", 1, false, 72);
Pokemon tentacruel("Tentacruel", "Water and Poison", 121.3, 63, "Male and Female", 2, true, 73);
Pokemon geodude("Geodude", "Rock and Ground", 44.1, 16, "Male and Female", 1, false, 74);
Pokemon graveler("Graveler", "Rock and Ground", 231.5, 39, "Male and Female", 2, false, 75);
Pokemon golem("Golem", "Rock and Ground", 661.4, 31, "Male and Female", 3, true, 76);
Pokemon ponyta("Ponyta", "Fire", 66.1, 39, "Male and Female", 1, false, 77);
Pokemon rapidash("Rapidash", "Fire", 209.4, 67, "Male and Female", 2, true, 78);
Pokemon slowpoke("Slowpoke", "Water and Psychic", 79.4, 47, "Male and Female", 1, false, 79);
Pokemon slowbro("Slowbro", "Water and Psychic", 173.1, 63, "Male and Female", 2, true, 80);
Pokemon magnemite("Magnemite", "Electric and Steel", 13.2, 12, "Unknown", 1, false, 81);
Pokemon magneton("Magneton", "Electric and Steel", 132.3, 39, "Unknown", 2, true, 82);
Pokemon farfetchd("Farfetch'd", "Normal and Flying", 33.1, 31, "Male and Female", 1, true, 83);
Pokemon doduo("Doduo", "Normal and Flying", 86.4, 45, "Male and Female", 1, false, 84);
Pokemon dodrio("Dodrio", "Normal and Flying", 187.8, 71, "Male and Female", 2, true, 85);
Pokemon seel("Seel", "Water", 198.4, 43, "Male and Female", 1, false, 86);
Pokemon dewgong("Dewgong", "Water and Ice", 264.6, 67, "Male and Female", 2, true, 87);
Pokemon grimer("Grimer", "Poison", 66.1, 35, "Male and Female", 1, false, 88);
Pokemon muk("Muk", "Poison", 66.1, 47, "Male and Female", 2, true, 89);
Pokemon shellder("Shellder", "Water", 8.8, 12, "Male and Female", 1, false, 90);
Pokemon cloyster("Cloyster", "Water and Ice", 292.1, 59, "Male and Female", 2, true, 91);
Pokemon gastly("Gastly", "Ghost and Poison", 0.2, 51, "Male and Female", 1, false, 92);
Pokemon haunter("Haunter", "Ghost and Poison", 0.2, 63, "Male and Female", 2, false, 93);
Pokemon gangar("Gengar", "Ghost and Poison", 89.3, 59, "Male and Female", 3, true, 94);
Pokemon onix("Onix", "Rock and Ground", 463, 346, "Male and Female", 1, true, 95);
Pokemon drowzee("Drowzee", "Psychic", 71.4, 39, "Male and Female", 1, false, 96);
Pokemon hypno("Hypno", "Psychic", 166.7, 63, "Male and Female", 2, true, 97);
Pokemon krabby("Krabby", "Water", 14.3, 16, "Male and Female", 1, false, 98);
Pokemon kingler("Kingler", "Water", 132.3, 51, "Male and Female", 2, true, 99);
Pokemon voltorb("Voltorb", "Electric", 22.9, 20, "Unknown", 1, false, 100);
Pokemon electrode("Electrode", "Electric", 146.8, 47, "Male and Female", 2, true, 101);
Pokemon exeggcute("Exeggcute", "Grass and Psychic", 5.5, 16, "Male and Female", 1, false, 102);
Pokemon exeggutor("Exeggutor", "Grass and Psychic", 264.6, 79, "Male and Female", 2, true, 103);
Pokemon cubone("Cubone", "Ground", 14.3, 16, "Male and Female", 1, false, 104);
Pokemon marowak("Marowak", "Ground", 99.2, 39, "Male and Female", 2, true, 105);
Pokemon hitmonlee("Hitmonlee", "Fighting", 109.8, 59, "Male", 1, true, 106);
Pokemon hitmonchan("Hitmonchan", "Fighting", 110.7, 55, "Male", 1, true, 107);
Pokemon lickitung("Lickitung", "Normal", 144.4, 47, "Male and Female", 1, true, 108);
Pokemon koffing("Koffing", "Poison", 2.2, 24, "Male and Female", 1, false, 109);
Pokemon weezing("Weezing", "Poison", 20.9, 47, "Male and Female", 2, true, 110);
Pokemon rhyhorn("Rhyhorn", "Ground and Rock", 253.5, 39, "Male and Female", 1, false, 111);
Pokemon rhydon("Rhydon", "Ground and Rock", 264.6, 75, "Male and Female", 2, true, 112);
Pokemon chansey("Chansey", "Normal", 76.3, 43, "Female", 1, true, 113);
Pokemon tangela("Tangela", "Grass", 77.2, 39, "Male and Female", 1, true, 114);
Pokemon kangaskhan("Kangaskhan", "Normal", 176.4, 87, "Female", 1, true, 115);
Pokemon horsea("Horsea", "Water", 17.6, 16, "Male and Gender", 1, false, 116);
Pokemon seadra("Seadra", "Water", 55.1, 47, "Male and Female", 2, true, 117);
Pokemon goldeen("Goldeen", "Water", 33.1, 24, "Male and Female", 1, false, 118);
Pokemon seaking("Seaking", "Water", 86, 51, "Male and Female", 2, true, 119);
Pokemon staryu("Staryu", "Water", 76.1, 31, "Unknown", 1, false, 120);
Pokemon starmie("Starmie", "Water and Psychic", 176.4, 43, "Unknown", 2, true, 121);
Pokemon mrmime("Mr. Mime", "Fairy and Psychic", 120, 51, "Male and Female", 1, true, 122);
Pokemon scyther("Scyther", "Bug and Flying", 123.5, 59, "Male and Female", 1, true, 123);
//Create a vector with all of the pokemon
std::vector<Pokemon> pokemon = { bulbasaur, ivysaur, venusaur,
charmander, charmeleon, charizard,
squirtle, wartortle, blastoise,
caterpie, metapod, butterfree,
weedle, kakuna, beedrill,
pidgey, pidgeotto, pidgeot,
rattata, raticate,
spearow, fearow,
ekans, arbok,
pikachu, raichu,
sandshrew, sandslash,
nidoranf, nidorina, nidoqueen,
nidoranm, nidorino, nidoking,
clefairy, clefable,
vulpix, ninetales,
jigglypuff, wigglytuff,
zubat, golbat,
oddish, gloom, vileplume,
paras, parasect,
meowth, persian,
psyduck, golduck,
mankey, primeape,
growlithe, arcanine,
poliwag, poliwhirl, poliwrath,
abra, kadabra, alakazam,
machop, machoke, machamp,
bellsprout, weepinbell, victreebel,
tentacool, tentacruel,
geodude, graveler, golem,
ponyta, rapidash,
slowpoke, slowbro,
magnemite, magneton,
farfetchd,
doduo, dodrio,
seel, dewgong,
grimer, muk,
shellder, cloyster,
gastly, haunter, gangar,
onix,
drowzee, hypno,
krabby, kingler,
voltorb, electrode,
exeggcute, exeggutor,
cubone, marowak,
hitmonlee, hitmonchan,
lickitung,
koffing, weezing,
rhyhorn, rhydon,
chansey,
tangela,
kangaskhan,
horsea, seadra,
goldeen, seaking,
staryu, starmie,
mrmime,
scyther };
//Ask the user which way they would like to search
std::cout << "Welcome to the Pokedex! We have the first " << pokemon.size() + 4 << " pokemon indexed!" << std::endl;
std::cout << "Would you like to search by name, number, or type?" << std::endl;
std::string input = "";
std::cin >> input;
//make input uppercase
for (int p = 0; p < input.size(); p++) {
input[p] = toupper(input[p]);
}
//check for input
if (input == "NAME") {
//Ask for name
std::cout << "Enter the name of the pokemon" << std::endl;
std::cin >> input;
bool found = false;
//make name lowercase
for (int j = 0; j < input.length(); j++) {
input[j] = tolower(input[j]);
}
//make first letter uppercase
input[0] = toupper(input[0]);
//loop through vector
for (int i = 0; i < pokemon.size(); i++) {
//check if input is the same as a pokemon name
if (input == pokemon.at(i).name) {
std::cout << "The pokemon " << pokemon.at(i).name << " has been found!" << std::endl;
std::cout << pokemon.at(i).name << " is a " << pokemon.at(i).type << " type." << std::endl;
std::cout << pokemon.at(i).name << "'s weight and height are: " << pokemon.at(i).weight << " lbs and " << pokemon.at(i).height << " inches." << std::endl;
std::cout << pokemon.at(i).name << "'s gender is " << pokemon.at(i).Gender << std::endl;
if (pokemon.at(i).finalEvo) {
std::cout << pokemon.at(i).name << " is the final evolution in its line." << std::endl;
if (pokemon.at(i).evoLevel > 1) {
std::cout << pokemon.at(i).name << " is what " << pokemon.at(i - 1).name << " evolves into." << std::endl;
}
else {
std::cout << pokemon.at(i).name << " is the only pokemon in its evolution line" << std::endl;
}
}
else {
std::cout << pokemon.at(i).name << " evolves into " << pokemon.at(i + 1).name << std::endl;
if (pokemon.at(i).evoLevel > 1) {
std::cout << pokemon.at(i).name << " is what " << pokemon.at(i - 1).name << " evolves into." << std::endl;
}
}
found = true;
break;
}
}
//what to do if invalid pokemon is entered
if (found == false) {
std::cerr << "Pokemon not found!" << std::endl;
}
}
else if (input == "TYPE") {
//get input
std::string input = "";
std::cout << "Enter the type you would like!" << std::endl;
std::cin >> input;
//make input lowercase
for (int k = 0; k < input.size(); k++) {
input[k] = tolower(input[k]);
}
//make first letter uppercase
input[0] = toupper(input[0]);
//loop through vector
for (int i = 0; i < pokemon.size(); i++) {
//see if pokemon type contains the input
if (pokemon.at(i).type.find(input) != std::string::npos) {
std::cout << pokemon.at(i).name << " is a " << pokemon.at(i).type << " type!" << std::endl;
}
}
}
else {
std::cout << "Enter the number of the pokemon" << std::endl;
int input = 1;
std::cin >> input;
bool found = false;
std::cout << input << std::endl;
for (int i = 0; i < pokemon.size(); i++) {
//see if pokemon has same num as input
if (input == pokemon.at(i).dexNum) {
std::cout << "The pokemon " << pokemon.at(i).name << " has been found!" << std::endl;
std::cout << pokemon.at(i).name << " is a " << pokemon.at(i).type << " type." << std::endl;
std::cout << pokemon.at(i).name << "'s weight and height are: " << pokemon.at(i).weight << " lbs and " << pokemon.at(i).height << " inches." << std::endl;
std::cout << pokemon.at(i).name << "'s gender is " << pokemon.at(i).Gender << std::endl;
if (pokemon.at(i).finalEvo) {
std::cout << pokemon.at(i).name << " is the final evolution in its line." << std::endl;
if (pokemon.at(i).evoLevel > 1) {
std::cout << pokemon.at(i).name << " is what " << pokemon.at(i - 1).name << " evolves into." << std::endl;
}
else {
std::cout << pokemon.at(i).name << " is the only pokemon in its evolution line" << std::endl;
}
}
else {
std::cout << pokemon.at(i).name << " evolves into " << pokemon.at(i + 1).name << std::endl;
if (pokemon.at(i).evoLevel > 1) {
std::cout << pokemon.at(i).name << " is what " << pokemon.at(i - 1).name << " evolves into." << std::endl;
}
}
found = true;
break;
}
}
//Say pokemon wasnt
if (found == false) {
std::cerr << "Pokemon not found!" << std::endl;
}
}
return 0;
}
#1 楼
您的Pokemon类(声明)class Pokemon {
public:
std::string type;
double weight, height;
std::string Gender;
int evoLevel;
bool finalEvo;
int dexNum;
std::string name;
Pokemon(std::string name2, std::string type2, double weight2, double height2, std::string Gender2, int evoLevel2, bool finalEvo2, int dexNum2);
Pokemon();
};
这里有几件令人耳目一新的事情。首先,您的所有字段都是公开的。这表明您需要的是
struct
,而不是class
。在C ++中,它们的区别仅在于默认可见性。 class
默认为private
,struct
默认为public
。因此,让我们更改一下:struct Pokemon {
接下来,我们应该禁用
Pokemon()
。这样一来,就不会偶然创建口袋妖怪: Pokemon() = delete;
注意,这不是必需的,因为任何构造函数都会阻止编译器提供默认值。
更好的类型
在您的其他构造函数中,我们会稍微更改参数的名称及其类型:
Pokemon(Name name, Type type, Weight weight, Height height, Gender gender,
EvoLevel level, bool finalEvolution, PokedexID pid);
等等。这些到底是什么类型的?首先,它们是一个过大的杀伤力。其次,它们遵循C ++核心准则。您不想偶然使用神奇宝贝的性别作为名字吗?
处理神奇宝贝的类型
这使我们进入了枚举。
Type
和Gender
是这些的完美候选者: enum class BasicType {
Fire,
Grass,
Water,
Electro,
Poison,
...
};
我不确定口袋妖怪是否会具有两种以上的类型,但是正如@Eeevee所注意到的那样,它们是有序的。通过重载
Type
来实现此目的:class Type{
Type(BasicType main);
Type(BasicType main, BasicType sub);
void addSubType(BasicType);
bool hasType(BasicType) const;
};
操作员使我们能够使用
operator|
。请注意,这可能又是一个矫kill过正。但是,它将阻止您使用Type operator|(BasicType main, BasicType sub) { return Type(main, sub); }
Type operator|(Type a, BasicType sub) { return a.addSubtype(sub); }
相反,如果您使用
Pokemon("Bulbasaur", "Gras and Poison", ...);
,最终出现编译器错误,而不是沮丧的用户无法通过类型“草”找到“ Bulbasaur”。为了进行测试,可以方便地使用
Type::Fire | Type::Poison
:Pokemon("Bulbasaur", Type::Gras | Type::Poison, ...);
这是非常基本的,但是可以完成工作。实际存储类型的方式只是一个练习,但并不难。请注意,您可以向
operator&
添加任意方法或以Type
作为参数的函数,因此有足够的空间来容纳其他疯狂的科学实验功能。可以为性别做同样的事情,尽管这里稍微容易一些,因为只有一些,因此我们不需要Type
技巧或operator|
:这会阻止您编写Type operator&(Type a, BasicType b) { return a.hasType(b); }
Type operator&(BasicType a, Type b) { return b.hasType(a); }
将所有内容放在一起
对于其他类型,为简单起见,我们说
enum class Gender {
Male,
Female,
Both,
Unknown
};
因此,我们最终得到以下结构:
Pokemon("Bulbasaur", Type::Grass | Type::Poison, 15.2, 28, "Apache helicopter", ...);
注意,与您的代码相反,所有变量这里严格遵循camelCase,而您的代码包含
addSubType
。还要注意,在Gender
对象中,成员通常以class
为前缀,后缀为m_
或遵循其他命名约定。由于您想不带封装地使用它,而所有公共(例如_
)前缀或后缀都将使您的代码在此处难以阅读。“但是,”,我在这里说。 “现在参数与我的成员同名!”。那很好。如果有人在程序中使用
pokemon.type
,他们希望IDE以一种不错的方式显示参数名称,而没有2作为后缀。将我们带到构造函数的实现中:
typedef std::string Name;
typedef double Weight;
typedef double Height;
typedef unsigned int EvoLevel;
typedef unsigned int PokedexID;
啊哈。首先,如果您有一个与成员同名的参数,则可以使用
Pokemon::Pokemon(...)
进行区分,例如struct Pokemon {
Pokemon() = delete;
Pokemon(Name name, Type type, Weight weight, Height height, Gender gender,
EvoLevel level, bool isFinalEvolution, PokedexID pid);
Name name;
Type type;
Weight weight;
Height height;
Gender gender;
EvoLevel evolutionLevel;
bool isFinalEvolution;
PokedexID pid;
};
但是,我们在构造函数。因此,我们可以初始化我们的值。请注意,这对于POD并不是必需的,但是您的某些成员是类,例如
this->[membername]
(这是Name
)。这样会保存一份副本。我们最终得到:// your code
#include "Pokemon.h"
Pokemon::Pokemon(std::string name2, std::string type2, double weight2, double height2, std::string Gender2, int evoLevel2, bool finalEvo2, int dexNum2) {
name = name2;
type = type2;
weight = weight2;
height = height2;
Gender = Gender2;
evoLevel = evoLevel2;
finalEvo = finalEvo2;
dexNum = dexNum2;
}
请注意,这表明我们实际上并不需要定义构造函数,我们可以只使用通常的
std::string
初始化语法this->name = name2;
,但这取决于您的类型容易出错。
输出和DRY
您不遵循“请勿重复自己”的原则。您的代码包含两次打印口袋妖怪的功能。这很不好,因为一方面的更改可能也应该在另一方面进行,但会丢失。相反,如果要使用
struct
,请提供一个运算符: std::cout
和operator<<
。数据库
所以这是我最喜欢的代码。您的
Pokemon
:Pokemon(Name name, Type type, Weight weight, Height height, Gender gender,
EvoLevel level, bool isFinalEvolution, PokedexID pid)
: name(std::move(name)), type(type), weight(weight), height(height), gender(gender),
evolutionLevel(evolutionLevel), isFinalEvolution(isFinalEvolution), pid(pid) {
}
有两件事我在这里我不喜欢。首先,稍后在
Gender
中仅使用名称“ bulbasaur”或“ ivysaur”,因此会污染本地名称空间。取而代之的是,直接填充矢量:Pokemon bulbasaur = { "Bulbasaur", ... };
但这不是您应该这样做的方式。
到目前为止,我已经有120个左右,并且可以正常运行!太疯狂了假设您搞砸了皮卡丘(Pikachu)的重量,或者您想稍后再添加更多口袋妖怪。您将始终必须编辑程序并进行编译。那很糟。容易出错。而且这些错误很难(编译)解决。
相反,请将所有Pokemon放在一个外部文件中。然后,您可以轻松地从该文件将其读取到向量中(如果您编写流式运算符):
std::ostream & operator<<(std::ostream & out, const Pokemon & pokemon) {
out << pokemon.name << " is a " << pokemon.type << " type.\n"
<< pokemon.name << "'s weight and height are: "
<< pokemon.weight << " lbs and " << pokemon.height << " inches.\n";
<< pokemon.name << "'s gender is " << pokemon.gender << "\n";
if (pokemon.isFinalEvolution) {
out << pokemon.name << " is the final evolution in its line.\n";
}
...
return out;
}
除了将Pokemon数据放入外部文件并写入
Type
之外,这就是所有必要的事情。您无需更改main
文件,而是随其他口袋妖怪的每一代都变大。您甚至可以将其翻译为其他语言(例如“ Bisasam”,“ Bisaknosp”,“ Bisaflor”,...),但这一点还很远。其他内容
如果不需要索引,请使用基于范围的
std::vector<Pokemon> pokemon
循环而不是istream& operator>>(istream&, Pokemon&)
。另外,您多次遮盖了变量main
。别。此外,为追求更小的功能,您的Pokemon.txt
太大了。如果您使用的是C ++ 11,请更频繁地使用基于范围的
for
循环。如果您不想修改变量,请使用operator[]
。您的input
是一个完美的选择,因为您不想在以后进行更改。因为您需要为此访问整个Pokedex(Eevee可能会对单个Pokemon发生几次爆炸,看着您)。您可能应该多考虑一点。您可以存储先前的神奇宝贝的ID,或者使用无效的ID(如果不存在)。但这仍然是一个练习。根据查询的复杂程度来抽象类后面的查询,如Pokemon bulbasaur("Bulbasaur", "Grass and Poison", 15.2, 28, "Male and Female", 1, false, 1);
Pokemon ivysaur("Ivysaur", "Grass and Poison", 28.7, 39, "Male and Female", 2, false, 2);
Pokemon venusaur("Venusaur", "Grass and Poison", 220.5, 79, "Male and Female", 3, true, 3);
...
,这取决于查询的复杂程度。
因此,按照以上说明,我对您的
main
进行了部分重写。宠物小精灵从代码的笼子里消失了,取而代之的是漫游到其他的其他Pokeball文件中。如您所见,新的
for
小得多。无需滚动即可在屏幕上阅读。所有不同的功能都已提取到其他功能中,而这些功能本身又很小。有几件事可以进一步重构和重写,但这只是一个练习。
附录:规范
感谢对此评论的所有评论以及我在此评论的多个修订版中错过的所有Pokemon规范,我想这是添加两件事的好地方。
首先,在实现类之前,请问自己更多的问题:您可以轻松地测试(pokemon)类型吗?您经常为他们测试吗?那应该是它自己的功能吗?你测试名字吗?您需要标准化名称吗?不止一次?
其次,在代码中反映这些问题的答案,如果答案是否定的,则在注释/文档中反映出来。另外,请尝试使用更高级别的代码。上面的主要内容已经相当高级了,效果是我可以完全更改
const
和std::vector<Pokemon>
,而根本不需要触摸ostream& operator<<(ostream&,const Pokemon&)
。愿您的Pokedex完整。评论
\ $ \ begingroup \ $
完全不需要“ Pokemon()= delete;”。通常,带有两个以上参数的函数会被皱眉。我将删除所有构造函数。
\ $ \ endgroup \ $
–BЈовић
16年7月21日在12:15
\ $ \ begingroup \ $
集合不是神奇宝贝类型的合适数据类型-类型是有序的。该命令不会影响游戏机制,但始终保留在所有官方资源中,并具有一定的风味/传说影响。
\ $ \ endgroup \ $
–伊芙
16年7月21日在23:26
\ $ \ begingroup \ $
删除口袋妖怪(名称名称,类型类型,体重,身高,性别,EvoLevel等级,布尔isFinalEvolution,PokedexID pid)并使用{}。 (在尝试之前,请尝试一下)
\ $ \ endgroup \ $
– Y牛
16年7月22日在21:34
\ $ \ begingroup \ $
很棒的帖子,但是我不同意“不要使用std :: vector
\ $ \ endgroup \ $
– Voo
16年7月23日在9:58
\ $ \ begingroup \ $
@underscore如果您不交付应用程序的调试代码,那么这两项都不会对您有帮助。许多人认为他们在需要时就可以明确地进行边界检查,这基本上导致了每个不重要的C或C ++应用程序中的数百万次利用。是的,这会有性能成本,但是您知道它实际上有多大吗?即使在智能手机上,成本也低于1ns。这无关紧要,也不占全部代码的99.99%。
\ $ \ endgroup \ $
– Voo
16年7月24日在17:55
#2 楼
似乎只有几样东西可能需要改进。我想我会有一个“ evolvesTo”字段来给出它演变成的类型的dexNum
,并且(可能)使用诸如-1
这样的特殊值来表示它不会演变成其他任何东西。我可能还会为“ evolvesFrom”添加一个类似的字段。至少在我看来,直接对信息进行编码比根据记录的顺序更简洁。数据库
与其将所有原始数据嵌入到程序中,我至少考虑将其移动到外部文件中,然后在启动程序时将其读入。不必太复杂或特殊-例如,我考虑每行一个神奇宝贝,在每个字段之间都有一个制表符。
构造函数
通常,最好使用成员初始化器列表初始化成员,而不是在构造函数的主体中为其分配成员。当您执行此操作时,编译器可以整理出哪个是参数,哪个是成员,因此您无需在每个参数名称中添加
2
即可使它们保持直线:Pokemon::Pokemon(std::string name, std::string type, double weight, double height, std::string Gender, int evoLevel, bool finalEvo, int dexNum)
: name(name),
type(type),
weight(weight),
height(height),
Gender(Gender),
evoLevel(evoLevel),
finalEvo(finalEvo),
dexNum(dexNum),
{}
搜索
您有很多实例在数据库中进行搜索以根据各种属性来查找Pokemon。几乎所有这些都可以使用
std::find_if
进行实际搜索。auto p = std::find_if(pokemon.begin(), pokemon.end(),
[&](int dexNum) { return dexNum == input; });
if (p == pokemon.end())
std::cout << "Sorry, I can't find that Pokemon.\n";
else {
std::cout << "The Pokemon: " << p->name << " has been found.\n";
std::cout << p->name << " is a " << p->type << " type Pokemon\n";
// ...
}
如果要处理更多的数据(成千上万个项目)有更有效的数据搜索方式,例如为您要搜索的每个字段建立索引。如果您想尝试这种实现,Boost Multi Index库提供了这种类型的功能。
另一方面,如果您必须处理大量数据,则可能仍要使用某种数据库(例如SQLite)。
评论
\ $ \ begingroup \ $
+1为数据库建议。此外,表格设计:口袋妖怪本身的表格,类型,性别和进化路径。更多信息,请参见
\ $ \ endgroup \ $
– jose_castro_arnaud
16 Jul 19 '23:24
\ $ \ begingroup \ $
进化的建议是好的,但是有几种可能的进化。以伊芙为例。它可以演变成Vaporeon,Jolteon,Flareon,Espeon以及其他几种,因此evolutionTo必须是集合或类似集合。另一方面,evolveFrom要么为一,要么为无。
\ $ \ endgroup \ $
– Zeta
16年7月20日在5:36
\ $ \ begingroup \ $
当您只需要捕获输入时,似乎无法通过lambda中的引用捕获所有内容。
\ $ \ endgroup \ $
–cloakedlearning
16年7月20日在13:33
\ $ \ begingroup \ $
@cloakedlearning:即使使用默认捕获,lambda仍仅捕获lambda中使用的内容。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
16年7月20日在13:46
\ $ \ begingroup \ $
@cloakedlearning:如果需要详细信息,它们位于标准/草稿标准的[expr.prim.lambda] / 12中。大部分相关部分是:“具有复合捕获默认值的lambda表达式,如果复合语句:odr使用该实体,则隐式捕获该实体(即,此或变量),或在可能经过评估的表达式中命名实体[...]”。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
16年7月20日在15:33
#3 楼
我在您的代码中看到的最大问题是Eevee。Eevee具有分支演化。
我的解决方法是有一个带有进化的dex数的向量(如果想花哨的话,这将是一个包含进化细节的结构。
评论
\ $ \ begingroup \ $
以及进化所需的其他工具,例如水石。
\ $ \ endgroup \ $
– Zeta
16年7月20日在5:43
\ $ \ begingroup \ $
如果OP打算超越最初的151,这也很重要,因为第二代及以后开始引入无序的进化系。
\ $ \ endgroup \ $
– BJ Myers
16年7月20日在21:17
\ $ \ begingroup \ $
在提出这个问题之前,我一直在考虑Eeveelutions。现在,我已经将其硬编码到了代码中。有人说,如果是乔尔蒂昂(Jolteon),瓦波隆(Vaporeon)等。它说[宠物小精灵]是伊芙进化而成的。非常低效且混乱,但它可以工作:P。
\ $ \ endgroup \ $
–迪伦·布莱克(Dylan Black)
16年7月21日在0:06
\ $ \ begingroup \ $
Eevee不是唯一的分支进化。早在第二代,诸如Gloom,Polywhirl和Slowpoke之类的宠物小精灵就被赋予了分支进化。
\ $ \ endgroup \ $
–法老王
16年7月21日在4:31
\ $ \ begingroup \ $
更好的解决方案将是int从保证一对多关系的领域演变而来。这将使查找最终形式更加复杂,但是可以在每次程序运行时完成一次,并且索引只是一个简单的整数,因此只需眨眼。
\ $ \ endgroup \ $
– loa_in_
16年7月21日在13:03
#4 楼
所以你想成为口袋妖怪的主人吗?您有制造Pokedex的技能吗?Pokedex是一件复杂的机械。结果,该代码审查将更加侧重于概念方法。您的数据与其他数据的关系以及表示该数据的最佳方法。它不会涵盖实现细节,例如,实际数据是否来自文本文件,数据库,是否经过硬编码等。也不会涵盖如何最好地组织不同文件中的代码。所以...开始了...
创建Pokedex时,有很多事情要考虑,甚至简单到您要做的事情。因此,我们首先来看一下设计决策。
1:类型
口袋妖怪可以有(目前)十八种类型。这些类型定义明确且离散;也就是说,类型之间没有梯度,可用类型的集合不会动态变化。因此,类型最好用枚举表示。
enum class Type {
Fire,
Water,
Grass,
Electric,
Fighting,
Flying,
Poison,
Ground,
Ice,
Psychic,
Ghost,
Dragon,
Fairy,
Steel,
Dark,
Rock,
Normal,
Bug
};
类型和整数值之间也没有关联,因此枚举中的顺序无关紧要。
另外,您将需要在某个地方使用一个函数并返回字符串的函数,该函数易于理解。
std::string TypeToStr(Type type) {
switch(type) {
case Type::Fire:
return "Fire";
case Type::Water:
return "Water";
...
default:
return "ERROR"; //should never get here if you accounted for all types
}
2:商品
我们稍后会需要它。相信我
enum class Item {
WaterStone,
FireStone,
Thunderstone,
MoonStone,
LeafStone
};
以及一种获得人类可读名称的方法
std::string ItemToStr(Item item) {
switch(item) {
case Item::WaterStone:
return "Water Stone";
case Item::FireStone:
return "Fire Stone";
case Item::Thunderstone:
return "Thunderstone";
...
default:
return "ERROR"; //Should not get here if you accounted for all items
}
}
3:进化
大多数人都知道口袋妖怪可以进化。这些进化中有许多是线性的(克拉比只能进化成金勒)。但是有些分支发展了。伊夫(Ievee)在第一代中可以进化为乔尔登(Jelteon),弗拉瑞恩(Flareon)或伏波顿(Vaporeon)。那么该怎么办?即使Eevee是第一代中唯一会使用多个的数据,我们也可以为每个神奇宝贝存储三份进化数据。我认为那是浪费。我们可以将进化数据存储在向量中。更好,但是有一个聪明而简单的解决方案:倒退。
口袋妖怪不能从一种以上的基本形式进化而来。让我重复一遍:没有两个口袋妖怪会演变成同一件事。因此,我们可以将每个口袋妖怪的进化数据存储一次,而不是在进化数据中说“这是进入下一阶段所需的工作,这就是下一阶段的工作”,它会说“这就是上一阶段达到了这种形式,这就是上一个阶段。”
那么演化数据是什么样的?好吧,在第一代中,口袋妖怪可以通过三种方式进化:升级(达到一定水平),贸易和石头(拥有一定石头)。因此,我们有了方法,数据和表格。这就是代码中的样子:
class Evo {
public:
Evo(int from) :
fromID(from)
{}
virtual std::string ToStr() = 0;
int fromID;
};
class EvoTrade : public Evo {
public:
EvoTrade(int from) :
Evo(from)
{}
virtual std::string ToStr() {
return " by trading.";
}
};
class EvoLevel : public Evo {
public:
EvoLevel(int from, int lvl) :
Evo(from),
level(lvl)
{}
virtual std::string ToStr() {
return " by reaching level " + std::to_string(level) + ".";
}
int level;
};
class EvoStone : public Evo {
public:
EvoStone(int from, Item evo_stone) :
Evo(from),
stone(evo_stone)
{}
virtual std::string ToStr() {
return " by using a " + ItemToStr(stone) + ".";
}
Item stone;
};
口袋妖怪成年或成年的可能性不同。还有一些是无性别的(很多传奇人物和同上,还有一些设计只需要性别不可知论)。
6:神奇宝贝
最后我们可以在这里找到。好的。
口袋妖怪可以具有一种或两种类型(在敏捷中;在战斗中,有时它们可以具有三种)。有几种方法可以解决此问题。您可以包含一个Type变量,并且实际上使用了前面提到的位域,但是由于Pokemon最多只能包含两种类型,因此我认为这太过分了。相反,我建议仅使用两个Type变量。当然,这会带来额外的难题。
如果我们的口袋妖怪包含两个类型变量,那么如果口袋妖怪只有一种类型该怎么办?假设这是Voltorb,如您所知,它仅是Electric。第二个Type变量必须包含一个有效值,那么我们该怎么办?我们可以在枚举中引入第十九个类型,将第一个类型标记为None,但是还有另一种方法。游戏内部(最后一次检查)的方式是将两个变量都设置为同一类型。这就是我们将要采用的方法。
class Pokemon {
public:
std::string name;
double height, weight; //Metric
double gender_ratio; //Chance of Male, -1 means genderless
Evo* evo_data;
int dex_num;
Type type_1;
Type type_2;
std::function<std::vector<std::string>(int)> get_next_stages;
Pokemon(int dex_number, const std::string& new_name, double new_height, double new_weight, double male_chance) :
name(new_name),
height(new_height),
weight(new_weight),
gender_ratio(male_chance),
dex_num(dex_number),
evo_data(nullptr)
{}
~Pokemon() {
delete evo_data;
}
std::string ToStr() {
auto evolves_to = get_next_stages(dex_num);
std::string answer =
"Number: " + std::to_string(dex_num) +
"\nName: " + name;
if(type_1 == type_2)
answer += "\nType: " + TypeToStr(type_1);
else
answer += "\nTypes: " + TypeToStr(type_1) + '/' + TypeToStr(type_2);
answer += "\nHeight: " + std::to_string(height) + 'm' +
"\nWeight: " + std::to_string(weight) + "kg" +
"\nMale/Female: ";
if(gender_ratio < 0)
{
answer += "Genderless";
}
else
{
answer += std::to_string(gender_ratio) + '/';
answer += std::to_string(1 - gender_ratio);
}
for(auto str : evolves_to)
answer += '\n' + str;
return answer;
}
//These next methods are used to chain setups; they are much more
//flexible than defining constructors for every possible
//combination of data setup. And we need to go through methods
//to set this data up (at least, some of the data) because there
//are some things we want to do with them
Pokemon& set_types(Type type) {
type_1 = type_2 = type;
return *this;
}
Pokemon& set_types(Type type_a, Type type_b) {
type_1 = type_a;
type_2 = type_b;
return *this;
}
Pokemon& set_evo_data(Evo* evo) {
evo_data = evo;
return *this;
}
};
7:最后的说明
那么方法链接是什么样的?考虑上面的Pokemon类。
Pokemon p1(1, "Bulbasaur", 0.7, 6.9, 87.5)
.set_types(Type::Grass, Type::Poison);
Pokemon p2(5, "Charmeleon", 1.1, 19, 87.5)
.set_types(Type::Fire)
.set_evo_data(new EvoLevel(4, 16));
那么打印这些数据又如何呢?
//Assuming a collection of Pokemon
std::vector<Pokemon> mons;
//Fill out the data
//...
auto next_stages = [&mons](const int id){
std::vector<std::string> answer;
auto found = std::find_if(mons.begin(), mons.end(), [id](const Pokemon& mon){
return mon.evo_data != nullptr && mon.evo_data->fromID == id;
});
if(found != mons.end())
{
answer.push_back("Evolves into " + found->name + found->evo_data->toStr());
}
return answer;
};
//While filling out the data, put this function as the get_next_stages function
评论
\ $ \ begingroup \ $
为了得到事物的名称,为什么当您可以使用静态的std :: string数组时,为什么使用大量的switch语句?样板和计算量少得多,存储大小相同。只需确保枚举值从0开始并且不包含空格(如果这两个都不正确,就好像是代码味道),然后以相同顺序定义数组的元素(复制/粘贴枚举名称)作为注释,以使其变得更容易/可自行记录),然后使用枚举对其进行索引。
\ $ \ endgroup \ $
– underscore_d
16年7月24日在8:54
\ $ \ begingroup \ $
绝对是另一种方式。在第一代的这个项目中,只有18种类型和5个项目很重要,我看不出有什么大的不同。同样,如果希望将其扩展到其他语言或通过子项交易来支持子孙后代,我建议将该数据(至少是子项)存储在外部源中并以这种方式读取。
\ $ \ endgroup \ $
– Altainia
16年7月24日在13:44
\ $ \ begingroup \ $
@underscore_d您的解决方案可能比使用开关略短,但它也更脆弱。当有人重新排序枚举值或在中间添加/删除一个枚举值但不更改名称数组时会发生什么?糟糕,现在有一半的商品名称错误。
\ $ \ endgroup \ $
–法学家
16年7月24日在16:47
\ $ \ begingroup \ $
@bcrist真实且值得注意; +1。但是我在几个项目中都从这种方法获得了不错的成绩。它只需要了解一个人的处境,并且需要在两个地方进行更改。另外,我倾向于只定义一次枚举,而无需扩展它们。我想我的情况很简单...在一个有多个提交者的项目中,不能保证他们敏锐地意识到这一约束,并且可能会出现新的值-然后,请确保使用基于开关的方法是安全的。或者,对于看起来更好但必然较慢的代码,可以使用某种以枚举为键的映射。
\ $ \ endgroup \ $
– underscore_d
16年7月24日在16:53
#5 楼
V不错的代码。加分表示不使用标准容器做
using namespace std
使用标准容器
评论
东西,例如Pokemon类
type
,Gender
的成员。我强烈建议对成员变量使用特殊的命名约定。有些人不喜欢这样,但是我喜欢。因此,使用m_gender或gender_可以使函数中的代码更清晰,您知道要处理的数据我不认为您应该具有默认的构造函数。它毫无意义且不需要
将代码分解为功能块,LoadPokemons,PrintPoke,PromptUser等。这允许直接查找而不是线性搜索
请注意,您重复了字符串upper-> lower的代码,反之亦然。编写函数以执行此操作。
未来的想法。从文件中加载宠物小精灵数据,而不是将其编译到程序中。
评论
\ $ \ begingroup \ $
地图只允许在一个字段上搜索-他支持在三个字段上搜索(而且他永远不会有成千上万的神奇宝贝或其他类似东西来搜索)。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
16年7月19日在17:56
\ $ \ begingroup \ $
@JerryCoffin考虑到我们看到的口袋妖怪相当一致地添加了我不会讲得太早
\ $ \ endgroup \ $
–丹农
16年7月19日在18:00
\ $ \ begingroup \ $
@Dannnno:在大约20年的时间里,他们的口袋妖怪数量已经超过700。按照目前的速度,要达到一万只神奇宝贝大约需要275年。
\ $ \ endgroup \ $
–杰里·科芬(Jerry Coffin)
16年7月19日在18:22
\ $ \ begingroup \ $
我认为学习使用map是一种有用的练习,我必须承认我没有看过多键查找,因此OP应该使用将keyn映射到主键的索引映射。
\ $ \ endgroup \ $
–pm100
16年7月19日在18:23
\ $ \ begingroup \ $
@JerryCoffin如果再持续275年,我也不会感到惊讶 facetious>
\ $ \ endgroup \ $
–丹农
16年7月19日在18:25
评论
“ Pichario”不是真正的神奇宝贝。我会丢失默认的ctor。您的finalEvo和evoLevel逻辑不适用于Eeveelutions。 Jolteon(#135)不会演变为Flareon(#136),Eevee(#133)并不仅限于演变为Vaporeon(#134)。
我是唯一一个看到这样的问题并认为人们真的应该在某个数据库中设计架构而不是某种OO语言的类的人吗?如果我是你,我会将所有这些信息存储在数据库中,然后使用一些C ++ SQL客户端对这些数据进行处理。我意识到您只是在学习C ++,但这就是为什么我认为人们应该首先真正学习SQL的原因。
而不是手动输入整个pokédex,或者寻找已经为您完成工作的东西:) github.com/veekun/pokedex
@michaelsnowden +1,虽然作为一个学习项目,并且本质上是一个大表,但他可以处理文本文件并从中读取。