Runtime error when vector in class constructor? - c++

Hi I am unable to use a vector in the constructor. I'm trying to parse a vector that contains [x,y] coordinates into the object.
The errors I've gotten are runtime error and bad alloc.
Is there something I'm missing?
Do I have to use dynamic memory allocation?
ShapeTwoD(Parent Class of Child Class Square):
class ShapeTwoD {
protected:
string name;
bool containsWarpSpace;
vector<string> vect;
private:
public:
ShapeTwoD() {}
ShapeTwoD(string name, bool containsWarpSpace, vector<string> vect) {
this->vect = vect;
this->name = name;
this->containsWarpSpace = containsWarpSpace;
}
Class Square that is a child of ShapeTwoD:
class Square : public ShapeTwoD {
public:
Square() : ShapeTwoD(name, containsWarpSpace, vect) {
this->vect = vect;
this->name = name;
this->containsWarpSpace = containsWarpSpace;
}
~Square() {}
};
Main Function:
vector<string> temp;
string merge;
for (int i = 0; i < 4; i++) {
cout << "Please enter x-ordinate of pt " << i + 1 << " :";
cin >> x;
cout << "Please enter y-ordinate of pt " << i + 1 << " :";
cin >> y;
merge = x + ", " + y;
temp.push_back(merge);
}
Square obj;
obj.setName(shape);
obj.setCoord(temp);
if (specialtype == "ws") {
obj.setContainsWarpSpace(true);
}
else if (specialtype == "ns") {
obj.setContainsWarpSpace(false);
}
myvector.push_back(obj);
temp.clear();
cout << "\nRecords successfully stored. Going back to main menu ...\n"
<< endl;
}

In your Square constructor, you are not passing any arguments:
Square() : ShapeTwoD(name,containsWarpSpace,vect){
^^^^^^^^^^^^^^^^^^^^^^^^^^^
That means that the name, containsWarpSpace and vect refer to the parent class fields, which haven't been initialized yet (because that's the job of the ShapeTwoD constructor). So you are taking uninitialized variables and passing them into the constructor to initialize those same variables. More explicitly what you are doing is
Square():ShapeTwoD(this->ShapeTwoD::name,
this->ShapeTwoD::containsWarpSpace, this->ShapeTwoD::vect){
You should either pass them in:
Square(string name, bool containsWarpSpace, vector<string> vect)
:ShapeTwoD(name,containsWarpSpace,vect) {
or pass a sensible default:
Square() : ShapeTwoD("", false, {}) {

The problem is:
merge = x + ", " + y;
The ", " is a const char[3] (a null terminated character array). As an array, it decays to a pointer (const char *), which is offset by x+y as a result of summation with int. The resultant pointer refers to an unknown memory location. Next null byte is not guaranteed to reside in accessible address range; even if such byte is in an accessible address, the output would not be meaningful; because you are triggering UB.
You can fix it like this:
merge = std::to_string(x) + ", " + std::to_string(y);
Regards,
FM.

Related

Assigning Vector Values

I'm working on a simple dice game project which requires me to instantiate several copies of a custom class.
vector <Player> playerList;
playerList.resize(totalNumPlayers); //totalNum is grabbed from cin
for (int x = 0; x < totalNumPlayers; x++)
{
playerList.at(x).setname("Player" + to_string(x + 1));
//playerList[x] = p;
playerList.at(x).printName();
cout << playerList[0].returnName() << " " << playerList[1].returnName() << " " << playerList[2].returnName() << endl;
}
Player Class:
//Declarations
string playerName;
int playerChips;
Player::Player()
{
//Default constructor for when player is created
playerChips = 3;
playerName = "DEFAULT_PLAYER_NAME";
}
void Player::setname(string var)
{
playerName = var;
}
string Player::returnName()
{
return(playerName);
}
void Player::printName()
{
cout << playerName << endl;
}
void Player::addChips(int x)
{
playerChips += x;
}
void Player::removeChips(int x)
{
playerChips -= x;
}
int Player::returnChips()
{
return(playerChips);
}
I've noticed that on every iteration during the original forloop, the playerList[x] value is always the same. For instance, if totalNumPlayers = 3, playerList[0], playerList[1], and playerList[2] are all effected by the setName line. Therefore, when I use cout for PL 1,2, and 3, it always prints
Player1, Player1, Player1
then
Player2, Player2, Player2
ect.
Why are the references to each index not unique to their own object?
The reason is simple. You have defined string playerName; in the global namespace (you have not given the complete structure of your source file) and therefore, whenever you invoke Player::setname, you modify this global variable and as a result, when you invoke Player::printName() in your for loop, you just read this variable that is shared among all instances of Player. To fix this, move this variable into the Player class:
class Player
{
private:
string playerName;
public:
Player();
void setname(string var);
string returnName();
string Player::returnName();
void printName();
void addChips(int x);
void printName();
// and the rest of your declarations
};

How to choose struct based on condition

I have two distinct 'struct' 'student' & 'employee'. I want to choose it according to string mentioned in the message.
struct student {
string name;
float score;
}
struct employee {
int id;
string dep;
}
I get message like; $ST,16,JOHN,A or $EM,16,IT
From the keyword like ST or EM i have to decide which structure to populate.
I managed to extract the type like ST or EM but when i write,
if (type == "ST")
student x;
else if (type == "EM")
employee x;
// long code goes on here to sort and populate `struct`
it says xis undefined. I know it is wrong but i can't solve this riddle.
How to choose struct based on the condition?
First of all, it looks as if your scope is wrong. So x may not be visible.
No clue how your code looks, but you could define a base class, a pointer before the conditional and assign memory via the conditional (I would not do that). Alternatively, you need to work within the right scope.
struct base {
//whatever
};
struct student : public base {
};
struct employee : public base {
};
base *ptr;
if (type == "ST")
ptr = new student;
else if (type == "EM")
ptr = new employee;
The if statement is equivalent to
if (type == "ST") {
student x;
} else if (type == "EM") {
employee x;
}
// other code accessing x
You can see there's a scope around student x and employee x. When the scope ends, object x goes out of scope too.
To populate the struct properly, you must process the struct inside the scope
if (type == "ST") {
student x;
// populate student
} else if (type == "EM") {
employee x;
// populate employee
}
If you want something common in your case. There is nothing except reading from string itself. You could create the object, then read from string in-place. OR this solution using (kind of) abstract factory pattern:
using namespace std;
// "Marker interface" common between the two structs.
// Virtual destructor to make this object polymorphic.
class ReadableFromString { public: virtual ~ReadableFromString() {} };
struct student: public ReadableFromString {
string name;
int score;
char grade;
};
struct employee: public ReadableFromString {
int id;
string dept;
};
// Abstract factory function that creates a ReadableFromString
// from a string.
shared_ptr<ReadableFromString> readFromString(const string &source) {
std::stringstream s(source);
std::stringbuf buf(ios_base::out);
string type;
s.get(buf, ',');
s.get(); // This is to skip the comma
type = buf.str();
if (type == "$ST") {
shared_ptr<student> studentObj = make_shared<student>();
s >> studentObj->score;
s.get(); // This is to skip the comma
buf = std::stringbuf(ios_base::out);
s.get(buf, ',');
studentObj->name = buf.str();
s.get(); // This is to skip the comma
studentObj->grade = s.get();
s.get(); // This is to skip the comma
return studentObj;
}
else if (type == "$EM") {
shared_ptr<employee> employeeObj = make_shared<employee>();
s >> employeeObj->id;
s.get(); // This is to skip the comma
buf = std::stringbuf(ios_base::out);
s.get(buf, ',');
employeeObj->dept = buf.str();
s.get(); // This is to skip the comma
return employeeObj;
}
return shared_ptr<ReadableFromString>(); // A null pointer
}
int main() {
string sources[] = { "$ST,16,JOHN,A", "$EM,16,IT"};
for(int i=0;i<2;i++) {
shared_ptr<ReadableFromString> firstOne = readFromString(sources[i]);
// Try to cast to student
if (dynamic_pointer_cast<student>(firstOne)) {
shared_ptr<student> studentObj = dynamic_pointer_cast<student>(firstOne);
cout << "Student: " << "\n";
cout << studentObj->name << "\n";
cout << studentObj->score << "\n";
cout << studentObj->grade << "\n";
} else if (dynamic_pointer_cast<employee>(firstOne)) {
// If not student, it could be an employee
shared_ptr<employee> employeeObj = dynamic_pointer_cast<employee>(firstOne);
cout << "Employee: " << "\n";
cout << employeeObj->id << "\n";
cout << employeeObj->dept << "\n";
} else {
cout << "Not student nor employee" << "\n";
}
}
return 0;
}

Pointer Function return value of Struct in Class [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 7 years ago.
I have been attempting to create a function getLocation() that utilizes a pointer to return the value of the struct Location declared in the Character class. I was curious as to the problem with my syntax (or my structure). Knowing that the asterisk * should refer to the value, why is it that my function using an ampersand string& Character::getInventory is able to return the value of that particular index (its return does not need to be converted)?
Trying Location& Character::getLocation() {return position; }
when run results in error C2679: binary '<<': no operator found
Nor
Location*
Which cannot be run as there is no conversion.
I read that the following is likely the most proper because it specifies the scope in which the structure resides, but still results in needing and returning a temporary.
Character::Location* const & Character::getLocation() {return &position; }
Any advice or input would be greatly appreciated, thanks in advance.
Below is my main.cpp, which of course will show the hexadecimal address for Location.
#include <iostream>
#include <string>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::string;
class Character {
private:
string name;
string inventory[4];
public:
struct Location {
int x; int y;
};
Location position;
public:
void Character::setName(string x) { name = x; }
string Character::getName() { return name; }
void Character::setLocation(int x, int y) {
position.x = x; position.y = y;
}
Location* Character::getLocation() {return &position; }
void Character::setInventory(string(&x)[4]) { for (int i = 0; i < 4; ++i) { inventory[i] = x[i]; } }
string& Character::getInventory(int itemNumber) { return inventory[itemNumber]; }
};
void showUser(Character Character);
int main() {
try {
string items[4] = { "Sword", "Shield", "Potion", "Cloak" };
Character CharacterI;
CharacterI.setName("Some Character");
CharacterI.setInventory(items);
CharacterI.setLocation(1, 30);
cout << "\n" << "Retrieving Character Info..." << "\n" << endl;
showUser(CharacterI);
}
catch (std::exception & e) {
cerr << "\nError : " << e.what() << '\n';
}
system("pause");
return 0;
}
void showUser(Character character) {
cout << "Name : " << character.getName() << endl;
cout << "Location : " << character.getLocation() << endl;
for (int i = 0; i < 4; ++i) {
cout << "Inventory " << i + 1 << " : " << character.getInventory(i) << endl;
}
}
Ok, I think I understand the question better now. The reason why getInventory can successfully return a reference while getLocation does not is because getLocation returns a reference to a temporary variable, which is not good. See the link in #NathanOliver's comment for details. Additionally, to paraphrase a previous comment by #Peter Schneider, an * in an expression dereferences a pointer to return a value, while in a declaration it signifies that a variable will be of pointer type. The two usages are more or less opposites of each other. Example:
int* p = new int; //Declares a pointer to int
int x = *p; //Dereferences a pointer and returns an int
What you need to do is create a member variable to hold the Character's location, then set/get from that variable instead of creating temporaries. You did this already for name and inventory, just keep using that same pattern.
Additionally, whenever you use the Location struct outside of the Character class scope, you need to fully-qualify it with Character::Location.
Example:
#include <iostream>
using namespace std;
class Character {
public:
struct Location {
int x;
int y;
};
Location loc;
void SetLocation(int x, int y) {loc.x = x; loc.y = y;}
Location& GetLocation() {return loc;}
};
int main ()
{
Character c;
c.SetLocation(1,42);
Character::Location l = c.GetLocation();
cout << l.x << endl << l.y << endl;
return 0;
}
Output:
1
42

Having trouble with c++ default constructor?

I already have a constructor in my .h and .cpp file which takes some arguments but I also need a default argument which I'm not sure how to make because the way I tried it compiles but I get errors when I run my testfile.
This is my .h file
public:
Class();
Class(const std::string &, const int)
void getInfo();
std::string listItem();
private:
std::string name;
int quantity;
This is my .cpp file
Class::Class()
: name(0), quantity(0)
{
Class::Class(const string &nam, const int quant)
: name(nam),quantity(quant)
{
}
void Class::getInfo()
{
cout << "Enter Name: ";
cin >> name
cout << "Enter quantity: ";
cin >> quantity;
}
string Class::listItem()
{
ostringstream outputString;
outputString << getName() << getQuantity();
return outputString.str();
}
And this is the part of my test causing trouble:
const int shortList = 2;
array<Class*, shortList> newList;
for (int i=0; i< 2; i++){
Class *p = new Class();
p->getInfo();
newList[i] = p;
}
cout << "newList contains: " << endl;
for (Class* p : newList)
cout << p->listItem() << endl;
I get : terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Is it a constructor issue or is it some syntax error?
The problem is in the default constructor's initialiser list:
name(0)
This attempts to initialise the string using the constructor taking a C-style string pointer, char const*, with a null pointer value. You then get a runtime error since you're not allowed to pass a null pointer to that constructor.
To initialise the string to be empty, either specify default initialisation (or, pedantically, value-initialisation, which amounts to the same thing for this type)
name()
or leave it out of the initialiser list.
Assuming there are no intentional typos in the above code, there are semi-colons missing in very key locations, as well as curly brace use looking incorrect and missing declarations in the header.
The new lines and/or characters are noted with a comment starting with // added ...
Starting with the header file:
class Class // added
{ // added
public:
Class();
Class(const std::string &, const int); // added semi-colon
void getInfo();
std::string listItem();
private:
std::string name;
int quantity;
}; // added closing curly brace and semi-colon
The .cpp source file:
Class::Class()
: name(""), quantity(0) // modified name initial value to the empty string
{
} // added curly brace
Class::Class(const string &nam, const int quant)
: name(nam),quantity(quant)
{
}
void Class::getInfo()
{
cout << "Enter Name: ";
cin >> name; // added semi-colon
cout << "Enter quantity: ";
cin >> quantity;
}
string Class::listItem()
{
ostringstream outputString;
outputString << getName() << getQuantity();
return outputString.str();
}
Later on the code that is causing fits is:
const int shortList = 2;
array<Class*, shortList> newList;
for (int i=0; i< shortList; i++){ // changed bounds check for i to the const int shortList
Class *p = new Class();
p->getInfo();
newList[i] = p;
}
cout << "newList contains: " << endl;
//
// changed to declare auto instead.
// As a pointer declaration, there is a chance the Class copy constructor is being called
// inside the loop body prior to the dereference. It should not be, but...
// In my opinion, it is much more likely that setting the name to a zero initial value
// in the Class() constructor is the real problem cause as Mike says above.
//
for (auto p : newList)
cout << p->listItem() << endl;

C++ string member variable not present in vector

I am creating a vector that contains pointers to a base class. In this vector I'm dynamically storing pointers to derived classes which contain some member variables, one of them being a string variable name.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
bool hasDirection = false;
bool hasDiameter = false;
int direction;
float diameter;
int starDimension = 0;
int animalDimension = 0;
int fishDimension = 0;
class MovingObject
{
protected:
std::string name;
int direction;
float diameter;
int dimension;
float movingSpeed;
public:
std::string getName(){ return name;};
int getDirection(){ return direction;};
float getDiameter(){ return diameter;};
float getMovingSpeed(){ return movingSpeed;};
int getDimension(){ return dimension;};
void setName(std::string v){ name = v;};
void setDirection(int d){ direction = d;};
void setDiameter(float f){ diameter = f;};
void setMovingSpeed(float s){ movingSpeed = s;};
void setDimension (int d){ dimension = d;};
virtual void PrintContents()=0;
};
static std::vector<MovingObject*> data;
class starObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ")";
}
};
class animalObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ")";
}
};
class fishObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ", [" << getDimension() << "], " << getMovingSpeed() << ")";
}
};
I later set all these member variables inside a main function. The problem is when I try to output the contents of the member variables, all of them show up except for the string name.
Now, I've checked to make sure that the string gets set before calling the PrintContent() method, and it shows that the value is in the vector. However, when I debug through the code, the value is no longer there, instead containing an empty string.
Could someone with better c++ knowledge explain to me why this is happening? This is the main class:
int main()
{
std::string type;
Reader reader;
while (!std::cin.eof())
{
try
{
std::string type;
std::cin >> type;
if (type =="int")
{
reader.ReadDirection();
}
else if (type =="float")
{
reader.ReadDiameter();
}
else if (type == "string")
{
std::string name;
std::cin >> name;
if (hasDirection && hasDiameter)
{
int dimension;
if (diameter > 0 && diameter < 10)
{
//fish
fishObject fish;
fish.setName(name);
fish.setDiameter(diameter);
fish.setDirection(direction);
dimension = fishDimension;
fishDimension += 50;
fish.setDimension(dimension);
fish.setMovingSpeed(0.1);
data.push_back(&fish);
}
else if (diameter >= 10 < 500)
{
//animal
animalObject animal;
animal.setName(name);
animal.setDiameter(diameter);
animal.setDirection(direction);
dimension = animalDimension;
animalDimension += 800;
animal.setDimension(dimension);
animal.setMovingSpeed(5.0);
data.push_back(&animal);
}
else if (diameter >=500)
{
//star
starObject star;
star.setName(name);
star.setDiameter(diameter);
star.setDirection(direction);
dimension = starDimension;
starDimension += 5000;
star.setDimension(dimension);
star.setMovingSpeed(30.0);
data.push_back(&star);
}
}
else
{
throw (IncompleteData(name));
}
}
}
catch (IncompleteData e)
{
std::cerr << "No diameter or direction given for object " << e.objectName << "\n";
}
}
The objects you push to the data vector are local because they are declared inside if/else blocks (see the declarations of fish and animal).
When you push the address of such an object to the vector, it will continue to point to the local object, which ceases to exist at the end of the local scope. You need to create objects that live beyond the local scope. One way of doing this is to create copies of the local objects on the heap and push those to the vector:
data.push_back(new fishObject(fish));
Of course this means that you get a memory leak unless you make sure you explicitly delete the elements of the vector some time before the end of the program. The usual recommendation to avoid having to think of this is to use a vector of std::unique_ptr<MovingObject> instead of a vector of naked pointers.