-3

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.

Adyn_G
  • 7
  • 3
  • 2
    @LogicStuff That is a terrible duplicate. class1 would make a lot more sense as a member variable of a class. Although if I'm honest nothing about this design makes sense. – Borgleader Sep 04 '15 at 20:03
  • 1
    Overuse of OOP in my opinion. - "character creation" is not an object, it's an action. – Chad Sep 04 '15 at 20:04
  • 1
    This question has a million answers. Here's one of them. http://stackoverflow.com/questions/10422034/when-to-use-extern-in-c and here's another: http://stackoverflow.com/questions/16348789/shared-vector-variables-among-multiple-c-files and here's another: http://stackoverflow.com/questions/22567654/c-global-variable-in-multiple-files – sunny Sep 04 '15 at 20:05
  • 1
    coming from java or C# maybe? as as aside: the switch in the main has no `break`s – Ryan Haining Sep 04 '15 at 20:05
  • 3
    @OP You would do best to pick up an introductory book to C++ because there are a lot of basic mistakes in this code (accessing function local variables inside of a function, no break in switch cases, and youre using classes incorrectly). – Borgleader Sep 04 '15 at 20:06
  • There must be a hundred ways to accomplish what the you're trying to do. – PC Luddite Sep 04 '15 at 20:08
  • 1
    I suggest reconsider this "design". You do basic procedural code, but pack it for unknown reasons into a class. Read the chapter about classes again, especially why classes were invented. You could simplify your code if you would put the contents if your class in a simple function which returns the selected value. Putting something like an blocking input in a constructor of a class is very bad practice as well. So again, read about classes, why they exist and how to use them. – Flovdis Sep 04 '15 at 20:08

2 Answers2

1

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!
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Oh man this looks exactly like something I need to learn. Are there any videos or lessons you can give to me to understand this more thoroughly? I can maybe read 50% of the syntax you coded and understand about 10% of it. I don't have a mentor to help me when I'm stuck in a rut or reviewing code but getting good guidance on what to learn next to make this game more automated and neater would be fantastic. Thanks for the reply! – Adyn_G Sep 05 '15 at 06:04
0
CharacterCreation::CharacterCreation()
{
int warrior, mage, rogue, priest;
int class1;

A constructor is very similar to any other kind of function. Just like regular functions, you can declare local variables which are not accessible outside the function. That's what you've done here.

Here's a simplified example of the same problem:

#include <iostream>

void foo() {
  int aNumber;
  std::cout << "Enter a number! ";
  std::cin >> aNumber;
}

int main() {
  std::cout << "You entered the number " << aNumber << "\n";
}

Computer programs can be really big and really complex, which makes them really hard to make sure they keep working as new features are added and bugs are fixed. So one of the most critical techniques to avoiding complexity, and to make sure programs are as easy as possible to work on, is to isolate every part of a program as much as possible from other parts of the program, and to tightly control how parts of programs interact with one another.

Languages support this in different ways. One of those ways is the concept of variable scope, or the region of a program that's allowed to interact with a variable. In particular C++ limits the scope of a variable in a function so that nothing outside that function can get at it.

When you do want to have different parts of a program interact, there are many, many ways to do this and it doesn't make much sense to try listing them. Each different method will be suited to different programming goals, so it really depends on what you're doing.

I don't know exactly what your goals are, but having a CharacterCreation class and taking user input in the constructor seems like a dubious design, and it's probably over-complicating things. That said, here's a way to make the program work according to the apparent intent:

// CharacterCreation.h
#ifndef CHARACTERCREATION_H
#define CHARACTERCREATION_H

class CharacterCreation {
  int classID;

public:
  CharacterCreation();
  int getClass();
};

#endif // CHARACTERCREATION_H

// CharacterCreation.cpp

#include "CharacterCreation.h"
#include <cstdlib>  // abort()
#include <iostream> // cout, cin, cerr

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

  std::cin >> classID;
  if (std::cin.fail()) {
    std::cerr << "Error: failed to get user input.\n";
    throw std::runtime_error("input failed");
  }

  if (classID < 1 || 4 < classID) {
    std::cerr << "Error: invalid user input.\n";
    throw std::runtime_error("invalid selection");
  }

  switch (classID) {
  case 1:
    std::cout << "Learned Sword Combat!\n\n";
    break;
  case 2:
    std::cout << "Learned the Arcane Arts!\n\n";
    break;
  case 3:
    std::cout << "Learned the Art of Assasination!\n\n";
    break;
  case 4:
    std::cout << "Learned the Art of the Divine!\n\n";
    break;
  default:
    abort();
  }
}

int CharacterCreation::getClass() { return classID; }

// main.cpp

#include "CharacterCreation.h"
#include <cstdlib>  // abort()
#include <iostream> // cout, cin, cerr

int main() {
  CharacterCreation cc;

  switch (cc.getClass()) {
  case 1:
    std::cout << "You chose warrior\n";
    break;

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

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

  case 4:
    std::cout << "You chose priest\n";
    break;
  default:
    abort();
  }
}

If I just wanted to write a program with the same output the I might instead write:

// main.cpp

#include <iostream>
#include <array>

struct CharacterClass {
  char const *name;
  char const *output;
};

int main() {
  std::array<CharacterClass, 4> classes = {
      {{"Warrior", "Learned Sword Combat!\n\nYou chose warrior"},
       {"Mage", "Learned the Arcane Arts!\n\nYou chose mage"},
       {"Rogue", "Learned the Art of Assasination!\n\nYou chose rogue"},
       {"Priest", "Learned the Art of the Divine!\n\nYou chose priest"}}};

  std::cout << "Choose a class:\n";
  for (int i = 0; i < classes.size(); ++i) {
    std::cout << "[" << i + 1 << "] " << classes[i].name << "\n";
  }
  int classID;
  std::cin >> classID;
  if (std::cin.fail()) {
    std::cerr << "Error: failed to get user input.\n";
    throw std::runtime_error("input failed");
  }

  if (classID < 1 || classes.size() < classID) {
    std::cerr << "Error: invalid user input.\n";
    throw std::runtime_error("invalid selection");
  }

  std::cout << classes[classID - 1].output << '\n';
}
bames53
  • 86,085
  • 15
  • 179
  • 244