I am having a problem, where I need to declare an object for a class based on user input. The problem is that the scope of the object is stuck in the switch statement, and I was wondering if there was a way to make it public.
//ask the user to choose the class of the first fighter
cout << "Welcome to the fighting arena! Would you like the first competitor to be a FIGHTER <1>, a WIZARD <2>, a ROGUE <3>, or a RANGER <4>?" << endl;
cin >> competitor1;
cout << "And what is the name of the competitor?" << endl;
cin >> name1;
//creates an object in the appropriate class and initializes it
switch (competitor1)
{
case 1:
{
Fighter Battler1(name1);
break;
}
case 2:
{
Wizard Battler1(name1);
break;
}
case 3:
{
Rogue Battler1(name1);
break;
}
case 4:
{
Ranger Battler1(name1);
break;
}
default:
cout << "Sorry please enter a valid number!" <<endl << endl;
break;
}
cout << Battler1.hp //this is undefined because of the scope
Yes everything is written inside the main fuction, I know the probelm is scope and just need a way to get around it.
I wouldn't use a switch case at all here, instead, an array of functors, that each create a different type of fighter and return a pointer to their base, i.e.
template<typename T>
std::unique_ptr<Champion> Create( ) { return std::make_unique<T>( ); }
std::function<std::unique_ptr<Champion>(void)> createFunctions [] = {
Create<Fighter>
,Create<Rogue>
,Create<Wizard>
,Create<Ranger>
};
Usage:
std::cin >> competitor1;
std::unique_ptr<Champion> Battler1 = createFunctions[competitor1];
std::cout << Battler1->hp;
You cannot do it as you described. You will need some kind of polymorphism: an object that will change it's behavior depending on it's type.
The most straightforward way to do it would be to use a variant:
using Battler = std::variant<Fighter, Wizard, Rogue, Ranger>;
auto battler1 = [&]() -> std::optional<Battler> {
switch (competitor1)
{
case 1:
return Fighter{name1};
case 2:
return Wizard{name1};
case 3:
return Rogue{name1};
case 4:
return Ranger{name1};
default:
cout <<"Sorry please enter a valid number!" << endl;
return std::nullopt;
}
}();
// if a valid number has been chose.
if (battler1) {
cout << std::visit([](auto& battler) { return battler.hp; }, *battler1);
}
Note that you need an up to date compiler, or using boost::variant instead. This solution might also not be the most scalable as you'll need to effectively update the Battler alias when adding a new battler type.
Also, you could use polymorphism through pointers and vtables. It require more changes to your program, but might be more appropriate in some cases:
struct Battler {
virtual int getHp() const = 0;
virtual ~Battler() = default;
};
struct Fighter : Battler {
int getHp() const override {
return hp;
}
};
struct Wizard : Battler {
int getHp() const override {
return hp;
}
};
// ... all other classes of Battler
Then, change you switch case accordingly:
std::unique_ptr<Battler> battler1;
switch (competitor1)
{
case 1:
battler1 = std::make_unique<Fighter>(name1);
break;
case 2:
battler1 = std::make_unique<Wizard>(name1);
break;
case 3:
battler1 = std::make_unique<Rogue>(name1);
break;
case 4:
battler1 = std::make_unique<Ranger>(name1);
break;
default:
cout << "Sorry please enter a valid number!" <<endl << endl;
break;
}
// if a valid number has been chose
if (battler1) {
cout << battler1->getHp();
}
If you could make the object (Battler1) global, what datatype would it be? Instance of class Fighter, Wizard, Rogue or Ranger?
The solution to your problem is this: Create a parent class (let's say, Character) from which all the others (Fighter...) will inherit: class Fighter : Character etc. And then, create the objects dynamically:
Character *battler;
switch (...) {
case 1:
battler = new Fighter(name);
break;
case 2:
battler = new Wizard(name);
break;
}
// don't forget to free the memory when not needed anymore
delete battler;
Related
If I have an enum class like so
enum class Enum {
A = 1,
B = 2,
};
I was under the impression that the compiler guarantees that instances of Enum must be either A or B. But I learned that the following is possible
auto instance = Enum{};
cout << (instance == Enum::A) << endl;
cout << (instance == Enum::B) << endl;
cout << static_cast<int>(instance) << endl;
And it prints 0 in all cases. Further I used to think having a switch-case like this is exhaustive
switch (instance) {
case Enum::A:
// do something
break;
case Enum::B:
// do something
break;
}
but apparently it's not, how can I handle a value-initialized instance above?
the enum with value 0 is not much different from any other non-presented value (like 3), you can just handle it with default
switch (instance) {
case Enum::A:
// do something
break;
case Enum::B:
// do something
break;
default:
/* do something with non-presented value*/;
}
also note when used as flag, it's pretty common not all value combination have a name.
switch (instance) {
case Enum(0):
// all flag unset
break;
case Enum::A:
// do something when A
break;
case Enum::B:
// do something when B
break;
case Enum::A | Enum::B : // assume operator | exist
// do something when AB
break;
default:
/* do something with non-presented value*/;
}
Here is an abstract class that I wrote:
class Month
{
public:
//factory method
static Month* choose_month(int choice); //static function
virtual void birthstone() = 0; //virtual function
virtual void month() = 0; //virtual function
virtual ~Month()
{
std::cout << "Deleting the virtual destructor" << std::endl;
};
};
Month* Month::choose_month(int choice)
{
switch (choice)
{
case '1':
return new January;
break;
case '2':
return new February;
break;
//these cases go all the way to December
default:
break;
}
}
Then I create twelve derived classes, one for each month of the year. I will include only two of said classes for simplicity:
class January : public Month
{
public:
void birthstone()
{
std::cout << "Garnet" << std::endl;
}
void month()
{
std::cout << "January" << std::endl;
}
//destructor
~January()
{
std::cout << "Deleting the object" << std::endl;
}
};
class February : public Month
{
public:
void birthstone()
{
std::cout << "Amethyst" << std::endl;
}
void month()
{
std::cout << "February" << std::endl;
}
//destructor
~February()
{
std::cout << "Deleting the object" << std::endl;
}
};
In my main function I use a random number generator to the select a derived class, so that I can access the data in its member functions:
std::vector<Month*> stone;
std::srand(static_cast<unsigned>(time(0))); // Seed the random generator
//for-loop
for (int i = 0; i < 6; i++)
{
stone.push_back(Month::choose_month(random(12)));
}
The problem happens when I try to access a member function of any class stored in my vector. I keep getting an access violation error:
//displaying the elements inside the container
for (std::vector<Month*>::iterator iter = stone.begin(); iter != stone.end(); iter++)
{
(*iter)->birthstone();
}
I really can't see where the error is, and wonder if someone could please point out whats going wrong? Thank you.
case '1' doesn't do what you expect. '1' is not 1, it's a char with integal value 49 (ASCII code). But random(12) will return an int between 0 and 11. It means for the switch statement in Month::choose_month(), the default case will always be executed.
Change the case statements to case 1: and case 2: and so on. (Might should from case 0: to case 11:?) If you expect it to be a char, change the parameter's type and change the code calling it too.
And the default statement doesn't return anything. You should confirm to return some valid value for this case, or reconsider the design about it (adding asserts or throwing exception for this case, or eliminate the case).
BTW: random is not C++ standard facilities. You might use std::rand or C++11's random number generation facilities.
char x;
bool tf;
void IsNumber(char x)
{
switch (x)
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
bool tf(true);
break;
default:
bool tf(false);
break;
}
}
int main(int argc, const char * argv[])
{
using namespace std;
cout << "Test if a character is a number: " << endl;
char x;
cin >> x;
IsNumber((char) x);
if (bool tf = true)
cout << "True" << endl;
if (bool tf = false)
cout << "False" << endl;
return 0;
}
I am getting an error after the default: saying I can't redefine the variable. Also, it says, at the bottom, that I the variable tf isn't a variable. I am really new to C++, I only know python can someone help me out?
New code giving me an error:
#include <iostream>
bool tf;
tf = true;
bool IsNumber(char x)
{
switch (x)
{
case '1':
You're trying to declare two variables with the same name in the same scope. The switch statement itself defines a scope, but each case clause does not. If you want a scope within a case clause, then you'll have to provide one yourself:
case '0':
{
bool tf(true);
break;
}
Although it's not clear what good that is; you're declaring a local variable then ignoring it. Perhaps you meant to set the global variable:
case '0':
tf = true;
break;
so you could then check it with
if (tf) // NOT if (bool tf = true) - that also declares a new local variable
or, less freakily, perhaps you want to return a value:
bool IsNumber(char x) {
switch (x) {
case '0':
// and so on
return true;
default:
return false;
}
}
and test it with
if (IsNumber(x)) {
cout << "True" << endl;
} else {
cout << "False" << endl;
}
or, if verbosity isn't your thing
cout << (IsNumber(x) ? "True" : "False") << endl;
or even
cout << boolalpha << IsNumber(x) << endl;
You should probably take some time to read an introductory book until you're comfortable with the basics such as scopes, declarations and definitions. Here are some recommendations.
You haven't assigned anything to tf, for comparison operators we want =='s instead of just the ='s.
if (tf == true) {
}
You can only define a variable one you can assign it as many times as you need.
bool tf = false
means I am define a variable and assigning it
if I just want to assign it later
tf = false
if I want to do a conditional test
if (tf){do stuff}
You have x defined at the very top as a global variable, and then try and define it again in main. You have to pick one or the other, or rename one. What you probably want to do is get rid of the one in main.
You also have your bools compared as = instead of how they should be like ==.
Edit: You actually don't need the comparison in there at all.
if(tf){
cout << "True\n"
}
else cout << "False\n"
That will see if tf is true and output true and if not, output false.
I have an assignment to learn how to use boost::variant. I'm trying to create a function that asks the user for a shape type to create. Then create the requested shape and assign it to the variant and return it. I'm using a switch to accomplish this, but I'm getting a runtime error with the default statement.
I also get a warning from the compiler: "warning C4715: 'ShapeVariant' : not all control paths return a value"
How do I just print a string if the user enters an invalid selection?
Thanks!
#include "boost/variant.hpp"
typedef boost::variant<Point,Line,Circle> ShapeType;
ShapeType ShapeVariant()
{
cout << "Please select a Shape Type\n1: Point\n2: Line\n3: Circle\n\nSelection: ";
int choice;
cin >> choice;
switch(choice)
{
case 1: return Point(); break;
case 2: return Line(); break;
case 3: return Circle(); break;
default: cout << "Invalid selection." << endl; break;
}
}
Instead of printing a string from the function, you could instead throw an exception that the caller catches and prints the reason why the exception was thrown.
switch(choice)
{
case 1: return Point(); break;
case 2: return Line(); break;
case 3: return Circle(); break;
default: throw std::runtime_error( "Invalid selection." );
}
In the caller:
try {
auto result = ShapeVariant();
} catch( std::exception const& e ) {
std::cout << e.what() << std::endl;
}
You cannot do this in C++, running such code will result in undefined behaviour in the invalid case. If the selection is really that invalid, throw an exception. You could also look into boost::optional which is designed for cases like this one.
I am attempting to learn C++ (currently only know PHP and some C#) and have run into my first issue.
I am trying to call a class inside a switch, then use that defined variable after the switch. However, I get the error described in the title.
#include <iostream>
#include <string>
using namespace std;
class Hero {
protected:
int hHealth,hStamina,hExp;
string hName;
public:
void Create(string);
string GetName() {
return this->hName;
}
};
class Wizard:public Hero {
public:
void SetStats(string hName) {
this->hName = hName;
this->hHealth = 40;
this->hStamina = 80;
}
};
int main() {
string hName;
int hClass;
cout << "Welcome to Ryan's Dungeons & Dragons Adventure!\n\n";
cout << "Enter your Heroes name\n";
cout << "Name: ";
cin >> hName;
cout << hName << ", please select your class\n";
cout << "(1) The Wizard\n";
cout << "(2) The Warrior\n";
cout << "(3) The Rogue\n";
cout << "(4) The Priest\n";
cout << "Class: ";
cin >> hClass;
switch (hClass) {
case 1:
Wizard _hero;
break;
}
cout << _hero->GetName();
system("PAUSE");
return 0;
}
The error in question occurs on the line:
cout << _hero->getName();
where it says _hero is undefind.
_hero is defined only within the scope of that switch statement. You need to create objects in the same or higher up scope that you'll be using them.
One way you can get around this is define a pointer to Hero before the switch (initializing to null) and then set it to a value inside the switch. For instance:
Wizard *_hero = NULL;
switch (hClass) {
case 1:
_hero = new Wizard();
break;
}
}
if (_hero) {
cout << _hero->GetName();
}
You're also using the -> on a class value (as opposed to a pointer to one). Scope issues aside, you probably intended to write _hero.GetName(). Inside your class, -> is right however since this is a pointer to your object.
switch (hClass) {
case 1:
Wizard _hero;
break;
} // <-- _hero is deallocated at this point
cout << _hero->GetName();
The scope of _hero is limited to the switch statement.
I don't think that even works in C#... what you want is a pointer that's going to be initialized in the switch statement:
Hero* _hero = 0;
switch(hClass){
case 1: _hero = new Wizard;
break;
}
// use _hero ...
// at the end, delete it
delete _hero;
Though, you now most likely need a virtual destructor and virtual functions. Read up on them, they're a powerful OO feature. But you probably know about them from C#.
You said you know some C# and php, which I do not. I just want to know how would this have behaved in C#.
Creating an object inside some scope and using it outside the scope. Like: {int a;} a = 0;
In C++ its an issue.
switch (hClass) {
case 1:
Wizard _hero;
break;
}
//At this no _hero is present. _hero is out of its scope
The _hero object is restricted to the scope of that switch block. What you want is probably this:
Hero* _hero;
switch (hClass) {
case 1:
_hero = new Wizard();
break;
}
cout << _hero->GetName();