Search code examples
c++switch-statementextern

How to use variables across class files in C++?


I need to use variables I assign in one class into another. For example I have this bit of code. This is CharacterCreation.h followed by CharacterCreation.cpp

#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation
{
public:
    CharacterCreation();
};

#endif
#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation
{
public:
protected:
};

#endif

Here's CharacterCreation.cpp

#include <iostream>
#include "CharacterCreation.h"
#include <string>

CharacterCreation::CharacterCreation()
{
int warrior, mage, rogue, priest;
int class1;
int classID;

std::cout   << "Choose a class:\n"
            << "[1] Warrior\n"
            << "[2] Mage\n"
            << "[3] Rogue\n"
            << "[4] Priest\n" << std::endl;

std::cin    >> class1;

switch(class1)
{
    case 1:
        classID=1;
        std::cout   << "Learned Sword Combat!\n\n";
        break;


    case 2:
        classID=2;
        std::cout   << "Learned the Arcane Arts!\n\n";
        break;


    case 3:
        classID=3;
        std::cout   << "Learned the Art of Assasination!\n\n";
        break;

    case 4:
        classID=4;
        std::cout   << "Learned the Art of the Divine!\n\n"; 
        break;
}

}

And I need to use the class1 variable in main.cpp

#include <iostream>
#include <string>
#include "CharacterCreation.h"
int main()
{
    switch(class1)
        {
        case 1: std::cout << "You chose warrior\n";

        case 2: std::cout << "You chose mage\n";

        case 3: std::cout << "You chose rogue\n";

        case 4: std::cout << "You chose priest\n";
        }
}

This code is just an example of what I need, so don't worry about it not working. I just need the method of transferring my variables from CharacterCreation.cpp to main.cpp with them equaling the values I set in CharacterCreation.cpp taught to me.

I'm almost brand new to C++ so if you could ELIF whatever method you teach, that'd be great.


Solution

  • In contrast to the commentors saying there was an "overuse of OOP" - I think that's not the case.

    In fact, the code had all the hallmarks of someone new to programming. Case in point:

    • doing input/output from withing constructors
    • repetition of code (in general)
    • repeated switch on "class id" (specificly) <-- this is where the lack of object orientation showed IMO.

      Whenever you repeat switch on some kind of type identification, you really want Polymorphic Behaviour. You can model some classes with the different behaviours:

      class Character {
        public:
          virtual std::string name()  const = 0;
          virtual void acquireSkill() const = 0;
          virtual ~Character();
      };
      
      class Warrior : public Character { 
          virtual std::string name()  const override;
          virtual void acquireSkill() const override;
      };
      
      class Mage    : public Character { 
          virtual std::string name()  const override;
          virtual void acquireSkill() const override;
      };
      
      class Rogue   : public Character { 
          virtual std::string name()  const override;
          virtual void acquireSkill() const override;
      };
      
      class Priest  : public Character { 
          virtual std::string name()  const override;
          virtual void acquireSkill() const override;
      };
      

      Now you can just use it as follows:

      CharacterCreation factory;
      CharacterPtr character = factory.createCharacter(choice);
      
      std::cout << character->name() << "\n";
      character->acquireSkill();
      
    • The input needs validation. Good input error handling is teh hardz using C++'s standard library iostreams facilities. See the demo below for some ideas (which are beyond the scope of my explanations for now though).

    • The Creation class is likely intended as a kind of factory. So, let's make it so:

      using CharacterPtr = std::shared_ptr<Character>;
      
      class CharacterCreation {
      public:
          enum class Type { none, warrior, mage, rogue, priest };
          CharacterPtr createCharacter(Type type);
      };
      

      Note that the implementation of createCharacter still does not do the input of the choice!

      CharacterPtr CharacterCreation::createCharacter(Type type) {
          switch (type) {
              case Type::warrior: return std::make_shared<Warrior>();
              case Type::mage:    return std::make_shared<Mage>();
              case Type::rogue:   return std::make_shared<Rogue>();
              case Type::priest:  return std::make_shared<Priest>();
              case Type::none: // fall through
                  break;
          }
          throw std::range_error("Type"); // character type not implemented?
      }
      

      Note: the choice for shared_ptr was a bit arbitrary here. The point is, for polymorphic object behaviour you need to hold references the the base-type (implying you typically dynamically allocate the specific Character subclasses)

    Without further ado, the full sample in a single file:

    Live On Coliru

    #ifndef CHARACTERCREATION_H
    #define CHARACTERCREATION_H
    
    #include <memory>
    #include <string>
    
    class Character {
    public:
        virtual std::string name()  const = 0;
        virtual void acquireSkill() const = 0;
        virtual ~Character();
    };
    
    class Warrior : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Mage    : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Rogue   : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    class Priest  : public Character { 
        virtual std::string name()  const override;
        virtual void acquireSkill() const override;
    };
    
    using CharacterPtr = std::shared_ptr<Character>;
    
    class CharacterCreation {
    public:
        enum class Type { none, warrior, mage, rogue, priest };
        CharacterPtr createCharacter(Type type);
    };
    
    #endif
    
    #include <iostream>
    //#include "CharacterCreation.hpp"
    
    Character::~Character() { }
    
    CharacterPtr CharacterCreation::createCharacter(Type type) {
        switch (type) {
            case Type::warrior: return std::make_shared<Warrior>();
            case Type::mage:    return std::make_shared<Mage>();
            case Type::rogue:   return std::make_shared<Rogue>();
            case Type::priest:  return std::make_shared<Priest>();
            case Type::none: // fall through
                break;
        }
        throw std::range_error("Type"); // character type not implemented?
    }
    
    std::string Warrior::name() const  { return "Warrior"; } 
    std::string Mage::name()    const  { return "Mage";    } 
    std::string Rogue::name()   const  { return "Rogue";   } 
    std::string Priest::name()  const  { return "Priest";  } 
    
    void Warrior::acquireSkill() const  { std::cout << "Learned Sword Combat!\n\n";            } 
    void Mage::acquireSkill()    const  { std::cout << "Learned the Arcane Arts!\n\n";         } 
    void Rogue::acquireSkill()   const  { std::cout << "Learned the Art of Assasination!\n\n"; } 
    void Priest::acquireSkill()  const  { std::cout << "Learned the Art of the Divine!\n\n";   } 
    
    //// main.cpp
    #include <iostream>
    #include <string>
    //#include "CharacterCreation.hpp"
    
    namespace {
        template <typename T, typename Prompt, typename Validation>
            T input(std::istream& is, Prompt prompt, Validation valid)
            {
                T result;
                while (prompt(), !(is >> result) || !valid(result)) {
                    if (!is && is.eof())
                        throw std::runtime_error("End of file reading input");
    
                    is.clear();
                    is.ignore(10u << 20, '\n');
                }
    
                return result;
            }
    }
    
    int main() {
        auto choice = static_cast<CharacterCreation::Type>(
                input<int>(
                    std::cin, 
                    [] { std::cout << "Choose a character:\n"
                            << "[1] Warrior\n"
                            << "[2] Mage\n"
                            << "[3] Rogue\n"
                            << "[4] Priest\n"; },
                    [](int choice) { 
                        std::cout << "Validation(" << choice << ")\n";
                        return choice>=1 && choice <=4; 
                    }
                )
            );
    
        CharacterCreation factory;
        CharacterPtr character = factory.createCharacter(choice);
    
        std::cout << character->name() << "\n";
        character->acquireSkill();
    }
    

    Prints (when inputting '4'):

    Choose a character:
    [1] Warrior
    [2] Mage
    [3] Rogue
    [4] Priest
    Validation(4)
    Priest
    Learned the Art of the Divine!