简体   繁体   中英

Passing struct to function without typing every member (C++ no pointers)

So I think more of the inexperienced programmers will be roaming around with this question. I can pass a struct to a function successfully like the snippet below:

void printMonster(MonsterStats slime, MonsterStats spider, MonsterStats orc, MonsterStats ogre, MonsterStats dragon, int charLevel)

However you could imagine if the struct MonsterStats had 120 members which all needed to be passed to printMonster my code will become absolute spaghetti!
So the question is, how can I pass this struct into my function without typing every single member into the function parameter?

For inquiries about how much I "should" know about c++ at this point, I have arrived at the learncpp.com chapter 4 comprehensive quiz . I went a little out of scope of the assignment but I've done nothing I shouldn't be able to do by now.

full below (working but lengthy):

#include <iostream>


struct MonsterStats
{
    std::string type;
    std::string name;
    int health;
    int level;
};



void printMonster(MonsterStats slime, MonsterStats spider, MonsterStats orc, MonsterStats ogre, MonsterStats dragon, int charLevel)
{

    if(charLevel>=slime.level&&charLevel<spider.level)
        std::cout << "This " << slime.type << " is named " << slime.name << " and has " << slime.health << " health.\n";
    else if(charLevel>=spider.level&&charLevel<orc.level)
        std::cout << "This " << spider.type << " is named " << spider.name << " and has " << spider.health << " health.\n";
    else if(charLevel>=orc.level&&charLevel<ogre.level)
        std::cout << "This " << orc.type << " is named " << orc.name << " and has " << orc.health << " health.\n";
    else if(charLevel>=ogre.level&&charLevel<dragon.level)
        std::cout << "This " << ogre.type << " is named " << ogre.name << " and has " << ogre.health << " health.\n";
    else if(charLevel>=dragon.level)
        std::cout << "This " << dragon.type << " is named " << dragon.name << " and has " << dragon.health << " health.\n";
}

int main()
{
    using namespace std;

    MonsterStats ogre{"Ogre","Grumble",345, 16};            //Type, Name, Health, Level
    MonsterStats dragon{"Dragon","Spyro", 890, 21};         //Type, Name, Health, Level
    MonsterStats orc{"Orc","Rakanishu", 165, 11};           //Type, Name, Health, Level
    MonsterStats spider{"Giant Spider", "Arachnid", 80, 6}; //Type, Name, Health, Level
    MonsterStats slime{"Slime", "Blurp", 35, 1};            //Type, Name, Health, Level

    std::string getChoice="y";
    int charLevel;
    cout << "Please enter the level of your character to proceed to battle: ";
    cin >> charLevel;
    if(charLevel>0)
    {
        while(getChoice=="y")
        {
            printMonster(slime, spider, orc, ogre, dragon, charLevel);

            cout << "Do you want to fight the monster? (y/n)";
            cin >> getChoice;
            if(getChoice=="y")
            {
                cout << "You destroyed the monster and gained an experience level!\n\n";
                charLevel+=1;
            }
            else
                cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
        }
    }
    else
        cout << "Please create a character in order to play the game.";
    return 0;
}

Thanks in advance for the answers :)

What you tend to call "members" (eg ogre , spider , etc...) are in fact instances of the MonsterStats structure. So variables.

In C++, the "members" are the variables or functions that are in the structure (eg name , health , level , etc...).

Of course, using many instances like in your snippet, and retype a lot of time the same things would be a nightmare. Fortunately, in chapter 5 you'll see the loops and in chapter 6 you'll learn about arrays and vectors to handle a bundle of instances.

This would look like:

#include <iostream>
#include <string>
#include <vector>

struct MonsterStats
{
   ...
};
...
int main()
{
    using namespace std;

    vector<MonsterStats> monsters { 
                            {"Ogre","Grumble",345, 16}, 
                            {"Dragon","Spyro", 890, 21}, 
                            {"Orc","Rakanishu", 165, 11}, 
                            {"Giant Spider", "Arachnid", 80, 6}, 
                            {"Slime", "Blurp", 35, 1} }; 

    ... 
    printMonster(monsters, charlevel); 
    ...
}

And you would then access to the i -th item with monsters[i] .

Once you start using a standard library container you could make use of the standard library to sort and search:

void printMonster(std::vector<MonsterStats>& monsters, int charLevel)
{
    MonsterStats mystats{"","",0,charLevel};
    auto comp = [](const auto& i, const auto& j) {return i.level <= j.level; };
    std::sort(monsters.begin(), monsters.end(), comp);
    auto monster_iterator = std::lower_bound(monsters.begin(), monsters.end(), mystats, comp);
    auto monster = *(monster_iterator - 1);
    std::cout << "You have level " << charLevel << '\n';
    std::cout << "This " << monster.type << " is named " << monster.name << " and has " << monster.health << " health.\n";
}

Here is the full program :

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

struct MonsterStats
{
    std::string type;
    std::string name;
    int health;
    int level;
};

void printMonster(std::vector<MonsterStats>& monsters, int charLevel)
{
    MonsterStats mystats{"","",0,charLevel};
    auto comp = [](const auto& i, const auto& j) {return i.level <= j.level; };
    std::sort(monsters.begin(), monsters.end(), comp);
    auto monster_iterator = std::lower_bound(monsters.begin(), monsters.end(), mystats, comp);
    auto monster = *(monster_iterator - 1);
    std::cout << "You have level " << charLevel << '\n';
    std::cout << "This " << monster.type << " is named " << monster.name << " and has " << monster.health << " health.\n";
}

int main()
{
    std::vector<MonsterStats> monsters{
        {"Ogre","Grumble",345, 16},            //Type, Name, Health, Level
        {"Dragon","Spyro", 890, 21},           //Type, Name, Health, Level
        {"Orc","Rakanishu", 165, 11},          //Type, Name, Health, Level
        {"Giant Spider", "Arachnid", 80, 6},   //Type, Name, Health, Level
        {"Slime", "Blurp", 35, 1}};            //Type, Name, Health, Level

    std::string getChoice = "y";
    int charLevel;
    std::cout << "Please enter the level of your character to proceed to battle: ";
    std::cin >> charLevel;
    if(charLevel>0)
    {
        while(getChoice == "y")
        {
            printMonster(monsters, charLevel);

            std::cout << "Do you want to fight the monster? (y/n)";
            std::cin >> getChoice;
            if(getChoice == "y")
            {
                std::cout << "You destroyed the monster and gained an experience level!\n\n";
                charLevel += 1;
            }
            else
                std::cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
        }
    }
    else
        std::cout << "Please create a character in order to play the game.";
    return 0;
}

A cleaner program might be with a multiset (sorted container) using a custom comparator.

The comparator could have this form:

struct myComp {
    bool operator()(const MonsterStats& i, const MonsterStats& j) {
        return i.level < j.level;
    }
};

And the multiset could use it:

using myMultiset = std::multiset<MonsterStats, myComp>;

Then, searching can be simpler as the set is already sorted:

auto& monster = *--monsters.upper_bound(mystats);

Printing could use the << operator:

std::ostream& operator<<(std::ostream& os, const MonsterStats& monster)
{
     std::cout 
         << "Monster has level: " << monster.level << '\n' 
         << "This " << monster.type << " is named " << monster.name << " and has " << monster.health << " health.\n";
     return os;
}

Full program:

#include <iostream>
#include <string>
#include <set>

struct MonsterStats
{
    std::string type;
    std::string name;
    int health;
    int level;
};

struct myComp {
    bool operator()(const MonsterStats& i, const MonsterStats& j) {
        return i.level < j.level;
    }
};

using myMultiset = std::multiset<MonsterStats, myComp>;

std::ostream& operator<<(std::ostream& os, const MonsterStats& monster)
{
     std::cout 
         << "Monster has level: " << monster.level << '\n' 
         << "This " << monster.type << " is named " << monster.name << " and has " << monster.health << " health.\n";
     return os;
}

int main()
{
    myMultiset monsters{
        {{"Ogre","Grumble",345, 16},            //Type, Name, Health, Level
        {"Dragon","Spyro", 890, 21},           //Type, Name, Health, Level
        {"Orc","Rakanishu", 165, 11},          //Type, Name, Health, Level
        {"Giant Spider", "Arachnid", 80, 6},   //Type, Name, Health, Level
        {"Slime", "Blurp", 35, 1}}};            //Type, Name, Health, Level

    std::string getChoice = "y";
    std::cout << "Please enter the level of your character to proceed to battle: ";
    MonsterStats mystats{};
    std::cin >> mystats.level;
    if(mystats.level>0)
    {
        while(getChoice == "y")
        {
            auto& monster = *--monsters.upper_bound(mystats);
            std::cout 
                << "You have level " << mystats.level << '\n' 
                << monster
                << "Do you want to fight the monster? (y/n)";
            std::cin >> getChoice;
            if(getChoice == "y")
            {
                std::cout << "You destroyed the monster and gained an experience level!\n\n";
                ++mystats.level;
            }
            else
                std::cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
        }
    }
    else
        std::cout << "Please create a character in order to play the game.";
    return 0;
}

Or, if you include the operator< in the struct then you can simplify even further:

#include <iostream>
#include <string>
#include <set>

struct MonsterStats
{
    std::string type;
    std::string name;
    int health;
    int level;

    bool operator<(const MonsterStats& rhs) const {
        return level < rhs.level;
    }
};

using myMultiset = std::multiset<MonsterStats>;

std::ostream& operator<<(std::ostream& os, const MonsterStats& monster)
{
    std::cout
        << "Monster has level: " << monster.level << '\n'
        << "This " << monster.type << " is named " << monster.name << " and has " << monster.health << " health.\n";
    return os;
}

int main()
{
    myMultiset monsters{
        {{"Ogre","Grumble",345, 16},            //Type, Name, Health, Level
        {"Dragon","Spyro", 890, 21},           //Type, Name, Health, Level
        {"Orc","Rakanishu", 165, 11},          //Type, Name, Health, Level
        {"Giant Spider", "Arachnid", 80, 6},   //Type, Name, Health, Level
        {"Slime", "Blurp", 35, 1}}};            //Type, Name, Health, Level

    std::string getChoice = "y";
    std::cout << "Please enter the level of your character to proceed to battle: ";
    MonsterStats mystats{};
    std::cin >> mystats.level;
    if(mystats.level>0)
    {
        while(getChoice == "y")
        {
            auto& monster = *--monsters.upper_bound(mystats);
            std::cout
                << "You have level " << mystats.level << '\n'
                << monster
                << "Do you want to fight the monster? (y/n)";
            std::cin >> getChoice;
            if(getChoice == "y")
            {
                std::cout << "You destroyed the monster and gained an experience level!\n\n";
                ++mystats.level;
            }
            else
                std::cout << "You ran like Forest Gump and got out of the fight safely!\n\n";
        }
    }
    else
        std::cout << "Please create a character in order to play the game.";
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM