Object member variable updating in function but not persisting - c++

I am fairly new to c++ and building a simple battleship game. I have a function that initializes the ship types and lengths by taking in a vector of ship objects. I have confirmed that the type and ship length are updating inside the function but the changes are not persisting in the main function. Any help would be greatly appreciated.
#include <iostream>
#include <vector>
#include "ships.h"
#include "board.h"
#include "player.h"
using std::vector;
void initialize_ships(vector<ship> &newFleet)
{
vector<string> ship_types = { "carrier","battleship","submarine","cruiser","destroyer" };
for (auto ii = 0; ii != newFleet.size(); ++ii)
{
newFleet[ii].type = ship_types[ii];
newFleet[ii].setLength();
if (newFleet[ii].type == "carrier")
{
std::cout << "Carrier in function has type: " << newFleet[ii].type << std::endl;
}
}
}
int main()
{
ship player_carrier, player_battleship, player_submarine, player_cruiser, player_destroyer;
vector<ship> player_fleet = {player_carrier, player_battleship, player_submarine, player_cruiser, player_destroyer };
initialize_ships(player_fleet);
std::cout << "Carrier in main has type: " << player_carrier.type << std::endl;
system("pause");
return 0;
}
#ifndef SHIPS_H
#define SHIPS_H
#include <iostream>
#include <string>
using std::string;
struct ship
{
// ships member variables
int len;
char dir;
int health;
int row;
int col;
char symb;
string type;
void setLength()
{
if (type == "carrier")
{
len = 5;
}
else if (type == "battleship")
{
len = 4;
}
else if (type == "cruiser")
{
len = 3;
}
else if (type == "submarine")
{
len = 3;
}
else if (type == "destroyer")
{
len = 2;
}
else
{
std::cout << "Invalid ship type!" << std::endl;
}
}
};
#endif // SHIPS_H

Accidentally making copies is a common reason for changes to be “lost”. For this particular situation, where you want to have objects in a container and have names for those objects (as if they were members of a class), the sensible thing to do is to make references to objects in the container:
vector<ship> player_fleet(5);
ship &player_carrier=player_fleet[0],
&player_battleship=player_fleet[1],
&player_submarine=player_fleet[2],
&player_cruiser=player_fleet[3],
&player_destroyer=player_fleet[4];
Be careful not to cause the vector to reallocate, since that would invalidate your references. (Of course, if it never changes size, ship player_fleet[5]; would suffice.)

Related

Create Multiple Objects in a loop c++

I'm a beginner and was researching to find out if you can grab a name from an array to then put that name as the name of the object in a for loop.
The code a specifically created for this question is represented below:
#include <iostream>
using namespace std;
class YourMum{
public:
string name;
YourMum(string aName){
name = aName;
}
};
int main()
{
string names[5] = {"Jeremy_Clarkson", "Boris_Johnson", "Vladmir_Putin", "Peter_Griffin", "MeAndYourMum"};
for(int i = 0; i < 4; i++) {
YourMum names[i];
cout << names[i].name << endl;
cout << "You are great if you answer my question!";
}
return 0;
}
Okay, it's not clear what you're trying to do, but you've got a few things that aren't a good idea.
First, you defined string names[5] and then inside loop you hid names by making a second variable with the same name. While that's legal, it's a bad practice.
Second, you're using the second one (YourMum names) illegally:
You declared it as length i (using a non-standard feature of the compiler) but then reference element i. The elements of an array of length i range from 0..i-1, so [i] is one element past the end.
And you haven't initialized it anyway.
I'm not really clear what you're trying to do, so I can't even provide example code of how to do it.
The YourMum class you have shown is not default constructible, so YourMum names[size]; will not work. Every object of YourMum needs to be passed a parameter in order to create it. You can use placement-new for that, eg:
#include <iostream>
#include <type_traits>
using namespace std;
class YourMum{
public:
string name;
YourMum(string aName){
name = aName;
}
};
int main()
{
string names[5] = {"Jeremy_Clarkson", "Boris_Johnson", "Vladmir_Putin", "Peter_Griffin", "MeAndYourMum"};
std::aligned_storage<sizeof(YourMum), alignof(YourMum)>::type arr[5];
YourMum *mums = reinterpret_cast<YourMum*>(arr);
for(int i = 0; i < 5; i++) {
new (&mums[i]) YourMum(names[i]);
}
for(int i = 0; i < 5; i++) {
cout << mums[i].name << endl;
cout << "You are great if you answer my question!";
}
for(int i = 0; i < 5; i++) {
mums[i].~YourMum();
}
return 0;
}
Or, you can use std::vector instead:
#include <iostream>
#include <vector>
using namespace std;
class YourMum{
public:
string name;
YourMum(string aName){
name = aName;
}
};
int main()
{
string names[5] = {"Jeremy_Clarkson", "Boris_Johnson", "Vladmir_Putin", "Peter_Griffin", "MeAndYourMum"};
std::vector<YourMum> mums;
mums.reserve(5);
for(int i = 0; i < 5; i++) {
mums.emplace_back(names[i]);
}
for(int i = 0; i < 5; i++) {
cout << mums[i].name << endl;
cout << "You are great if you answer my question!";
}
return 0;
}
First of all, you should rename the name variable to something like _name or name_ for lisibility (to differenciate it from variables that aren't in the class), and you should also use the private section of your class to declare it and use getters/setters functions to edit it. Secondly, in your code you are declaring the object inside a loop, so it will get destroyed as soon as the loop iterates again, so if you want to create n objects in a loop and interact with them after you should either use a container class (like std::vector) or use a default constructor (which is better). Here is an updated version of your code:
#include <iostream>
#include <string>
class YourMum
{
public:
YourMum(void) : _name() { }
YourMum(std::string name) : _name(name) { }
std::string getName(void) const { return _name; }
void setName(std::string name) { _name = name; }
private:
std::string _name;
};
int main()
{
std::string names[5] = { "Jeremy_Clarkson", "Boris_Johnson", "Vladmir_Putin", "Peter_Griffin", "MeAndYourMum" };
YourMum mums[5];
for (int i = 0; i < 5; i++)
{
mums[i].setName(names[i]);
std::cout << mums[i].getName() << std::endl
<< "You are great if you answer my question!" << std::endl;
}
// this way you can still access mums here if you want to
return 0;
}
If you want to access to a class by it's name as index what you can also do is using std::map like this
#include <map>
int main()
{
std::string names[5] = { "Jeremy_Clarkson", "Boris_Johnson", "Vladmir_Putin", "Peter_Griffin", "MeAndYourMum" };
std::map <std::string, YourMum> mums;
for (int i = 0; i < 5; i++)
mums[names[i]] = YourMum(names[i]);
std::cout << mums["Jeremy_Clarkson"].getName() << std::endl;
return 0;
}
Try This
> for(int i = 0; i < 4; i++) {
> YourMum name(names[i]);
>
> cout << name.name << endl;
> cout << "You are great if you answer my question!";
> }

Trouble with how to declare (or define) a function in a header

I'm struggling with implementation of a function that I define in my header file of my c++ program. I think I am misunderstanding how it works, but most reading on the web doesn't spell it out clear enough for my peon brain to understand.
I'm trying to make a 'sort_name' function that sorts an array of private classes based on the c-string "name" when the function is invoked.
Unfortunately I keep running into errors when trying to use it.
Here's my courses_main.cpp's main function:
int main()
{
Course* courses[10] = {};
int selection;
int size = 0;
do
{
selection = menu();
if (selection == 1)
{
if (size < 10)
add(courses, size);
else
std::cout << "\nUnable to add more classes.";
}
else if (selection == 2)
{
edit(courses, size);
}
else if (selection == 3)
{
}
else if (selection == 4)
{
sort_name(courses, size);
for (int i = 0; i < size; i++)
{
courses[i]->display();
}
}
else if (selection == 5)
{
}
else if (selection == 6)
{
}
else if (selection == 7)
{
break;
}
else
{
std::cout << "\nInvalid selection.";
}
} while (selection != 7);
std::cout << "\nPress any key to exit.";
(void)_getch();
return 0;
}
Here is my courses_functions.cpp where I define the sort_name function:
void swap_ptrs(Course*& pt1, Course*& pt2) //Passes the pointers by reference
{
Course* tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
void Course::sort_name(Course* co_ptr[], int size) //has to be apart of the class (Course::) to have access to the name data
{
bool swap;
do
{
swap = false;
for (int i = 0; i < size - 1; i++)
{
if (strcmp(co_ptr[i]->name, co_ptr[i + 1]->name) > 0) //We're now comparing and swapping pointers
{
swap_ptrs(co_ptr[i], co_ptr[i + 1]);
swap = true;
}
}
} while (swap);
}
And here is my courses.h header where I define(?) the function:
#ifndef COURSE_H
#define COURSE_H
#include <iostream>
#include <conio.h>
#include <iomanip>
#include <stdio.h>
#include <string.h>
#include <ctime>
#include <fstream>
#include <cstdlib>
class Course
{
private:
char name[10] = "", grade;
int units;
public:
Course()
{
name;
grade;
units = 0;
}
void read() //Initializes course and collects information from user
{
std::cout << "\nEnter course name: ";
std::cin.getline(name, 10, '\n');
std::cout << "\nEnter number of units: ";
std::cin >> units;
std::cout << "\nEnter grade received: ";
std::cin >> grade;
std::cin.ignore();
}
void display() const //Displays course to user
{
std::cout << name << ' ' << units << ' ' << grade << std::endl;
}
~Course() //Destructor frees allocated dynamic memory
{
std::cout << "\nDeleting any dynamically created object";
}
void sort_name(Course* co_ptr[], int size);
};
#endif // COURSE_H
I'm not understanding much about classes outside of how they're extremely similar to structs, so any direction would be welcome thanks!
A better code organization would be to declare the functions in the .h file and implement them in the.cpp.
Here is a working sample without .cpp for simplification. Only Courses.h and main.
With .cpp your program should be somethong like this:
Course.h
#ifndef COURSE_H
#define COURSE_H
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; //<-- for test pusposes, you should use std:: scope
class Course
{
private:
string name;
int units, grade;
public:
Course(); //<-- the code you have inside the constructor, only units = 0,
// does somenthing, you should initialize all the members.
Course(string name);
void read();
void display() const;
~Course(); //<-- to dealocate dynamic memory you need to really dealocate it with delete.
string getName() const;
};
#endif // COURSE_H
And in your .ccp the implementation:
Course.cpp
#include "Course.h"
Course::Course(){ /*do stuff*/ }
Course::Course(string name) : name(name) { /*do stuff*/ } //<-- initializing name here
void Course::read() {/*do stuff*/ }
void Course::display() const {/*do stuff*/ }
Course::~Course() {/*do stuff*/ }
string Course::getName() const { return name; }
For sorting you don't need anything fancy you have sorting tools and data structures in the C++ libraries that make your job easy, like vector for objects container and sort for sorting.
Main
#include "Course.h"
bool sorting(Course course1, Course course2) { //conditional function for sort (#include <algorithm>)
return course1.getName() < course2.getName();
}
int main() {
vector<Course> courses = { Course("zed"), Course("albert")}; // adding courses
courses.push_back(Course("mary")); // adding some more
courses.push_back(Course("john"));
courses.push_back(Course("ana"));
courses.push_back(Course("charles"));
sort(courses.begin(), courses.end(), sorting); //<-- sorting
for (Course c : courses) {
cout << c.getName() << " ";
}
}
Output:
albert ana charles john mary zed

Problems writing/reading an vector of Objects to a file [duplicate]

This question already has answers here:
Need help regarding saving variables via fstream, do I need to use vector?
(2 answers)
Closed 8 years ago.
I got this problem, where I want to make a Devicelist, which I do with a vector of Devices. But I can't make it read/write correctly.
This is my functions:
#include "Devicelist.h"
bool Devicelist::AddDevice(const char *deviceName, char *type)
{
Device tempDevice(deviceName, type, ++id_);
Devicelist_.push_back(tempDevice);
return true;
}
bool Devicelist::deleteDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
{
Devicelist_.erase(Devicelist_.begin() + i);
return true;
}
else
{
cout << "No Device found with that Devicename." << endl;
return false;
}
}
}
bool Devicelist::SaveToFile()
{
//remove("Devices.dat");
ofstream SaveFile("Devices.dat", ios::out | ios::binary);
if (!SaveFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
SaveFile.write((const char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
SaveFile.close();
return true;
}
bool Devicelist::LoadFromFile()
{
ifstream LoadFile("Devices.dat", ios::in | ios::binary);
if (!LoadFile)
{
cerr << "File could not be opened." << endl;
return false;
}
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
LoadFile.close();
return true;
}
Device Devicelist::findDevice(const char *deviceName)
{
for (int i = 0; i < Devicelist_.size(); i++)
{
if (strcmp(deviceName, Devicelist_[i].getName()) == 0)
return Devicelist_[i];
else
cout << "Couldn't find device." << endl;
}
}
And this is my main:
#include "Devicelist.h"
void main()
{
Devicelist list;
list.AddDevice("Lampe3", "Lampe");
list.AddDevice("Lampe4", "Lampe");
list.SaveToFile();
Devicelist list2;
list2.LoadFromFile();
Device lampe = list2.findDevice("Lampe");
cout << lampe.getName() << endl;
cout << lampe.getID() << endl;
cout << lampe.getType() << endl;
}
Can anyone see what my problem is?
Thanks in advance!
Edit1:
My .h-file for Devicelist is:
#ifndef DEVICELIST_H
#define DEVICELIST_H
#include "Device.h"
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
class Devicelist
{
public:
Devicelist();
bool SaveToFile();
bool LoadFromFile();
bool AddDevice(const char *deviceName, char *type);
bool deleteDevice(const char *deviceName);
Device findDevice(const char *devicename);
private:
vector<Device> Devicelist_;
int id_;
};
#endif
This is my Device .h-file:
#ifndef DEVICE_H
#define DEVICE_H
#include <iostream>
#include <string>
#define DNAME_SIZE 33
using namespace std;
class Device
{
public:
Device(const char *devicename = "Default", char *type = "type", int id = 0);
const char *getName();
int getID();
int getType();
private:
char deviceName_[DNAME_SIZE];
int id_;
int type_;
};
#endif
And Device .cpp-file:
#include "Device.h"
Device::Device(const char *deviceName, char *type, int id)
{
strncpy_s(deviceName_, deviceName, DNAME_SIZE);
if (type == "Lampe")
type_ = 1;
else if (type == "Roegalarm")
type_ = 2;
else if (type == "Tyverialarm")
type_ = 3;
else
{
type_ = 0;
cout << "Type does not exists." << endl;
}
id_ = id;
}
const char *Device::getName()
{
return deviceName_;
}
int Device::getID()
{
return id_;
}
int Device::getType()
{
return type_;
}
The problem is when I try to read view what's saved in the file, this is the output:
http://imgur.com/P8WEAKq
The problem is here in the LoadFromFile function, I think:
for (int i = 0; i < Devicelist_.size(); i++)
LoadFile.read((char *)(&Devicelist_[i]), sizeof(Devicelist_[i]));
The DeviceList is empty, or has the wrong size.
You could write something similar as in AddDevice:
Device tempDevice();
LoadFile.read((char *)(&tempDevice), sizeof(tempDevice));
Devicelist_.push_back(tempDevice);
but the size (how many devices) of the file needs to be known.
As mentioned in a comment, this works only if Device doesn't have pointers to other objects for example. It should be a POD (Plain Old Data) object.
An elegant solution would be to implement streaming operators on Device
You will need to to have Device::StoreToFile write the number of devices before writing out each device. This will enable your Device::LoadFromFile to know how many device instances to read from the file.
I highly recommend you do not write a class or structure bit for bit to the file. If your class or struct has any advanced data structures, such as std::string or std::vector, you can't write them in binary. They may use pointers to memory, which will not be the same when you load them.
Here is a better solution:
1. Pass a buffer of uint8_t to the load and store methods.
2. The methods load from the buffer or store into the buffer (append).
3. The caller reads or writes the buffer to file.
4. The class also has a "size on file" method that returns the space occupied on a file, which may be different than the structure size.
Also search the web for "boost::serialization".

C++ Insertion Sort a vector

I'm trying to do an insertion sort on a vector of baseball pitchers I created yesterday with help from a previous post. I want to sort the pitchers in ascending order by ERA1. I have gotten the insertion sort to work in the past for a set of integers. I think I have a syntax error in my code for the insertion sort. Up until trying to add the insertion sort this program was working well. I get an error - expected unqualified id before [ token. Thanks in advance for any help.
#ifndef Pitcher_H
#define Pitcher_H
#include <string>
#include <vector>
using namespace std;
class Pitcher
{
private:
string _name;
double _ERA1;
double _ERA2;
public:
Pitcher();
Pitcher(string, double, double);
vector<Pitcher> Pitchers;
string GetName();
double GetERA1();
double GetERA2();
void InsertionSort(vector<Pitcher>&);
~Pitcher();
};
#endif
#include "Pitcher.h"
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;
Pitcher::Pitcher()
{
}
Pitcher::~Pitcher()
{
}
string Pitcher::GetName()
{
return _name;
}
Pitcher::Pitcher(string name, double ERA1, double ERA2)
{
_name = name;
_ERA1 = ERA1;
_ERA2 = ERA2;
}
double Pitcher::GetERA1()
{
return _ERA1;
}
double Pitcher::GetERA2()
{
return _ERA2;
}
#include "Pitcher.h"
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
void InsertionSort(vector<Pitcher> Pitchers&);
using namespace std;
int main()
{
vector<Pitcher> Pitchers;
cout << "Pitcher" << setw(19) << "Item ERA1" << setw(13) <<
"Item ERA2\n" << endl;
Pitcher h2("Bob Jones", 1.32, 3.49);
Pitchers.push_back(h2);
Pitcher h3("F Mason", 7.34, 2.07);
Pitchers.push_back(h3);
Pitcher h1("RA Dice", 0.98, 6.44);
Pitchers.push_back(h1);
for(unsigned i = 0; i < Pitchers.size(); ++i)
{
cout << setw(19);
cout << left << Pitchers[i].GetName() << "$" <<
setw(10) << Pitchers[i].GetERA1() <<
right << "$" << Pitchers[i].GetERA2() << "\n";
}
cout << endl;
//------------------------------------------------------
InsertionSort(Pitchers);
//Now print the numbers
cout<<"The numbers in the vector after the sort are:"<<endl;
for(int i = 0; i < Pitchers.size(); i++)
{
cout<<Pitchers[i].GetERA1()<<" ";
}
cout<<endl<<endl;
system("PAUSE");
return 0;
}
void InsertionSort(vector<Pitcher> &Pitchers)
{
int firstOutOfOrder = 0;
int location = 0;
int temp;
int totalComparisons = 0; //debug purposes
for(firstOutOfOrder = 1; firstOutOfOrder < Pitchers.size() ; firstOutOfOrder++)
{
if(Pitcher.GetERA1([firstOutOfOrder]) < Pitcher.GetERA1[firstOutOfOrder - 1])
{
temp = Pitcher[firstOutOfOrder];
location = firstOutOfOrder;
do
{
totalComparisons++;
Pitcher.GetERA1[location] = Pitcher.GetERA1[location - 1];
location--;
}while(location > 0 && Pitcher.GetERA1[location - 1] > temp);
Pitcher.GetERA1[location] = temp;
}
}
cout<<endl<<endl<<"Comparisons: "<<totalComparisons<<endl<<endl;
}
Here:
for(firstOutOfOrder = 1; firstOutOfOrder < Pitchers.size() ; firstOutOfOrder++)
{
if(Pitchers[firstOutOfOrder].GetERA1() < Pitchers[firstOutOfOrder-1].GetERA1())
{ //^^^your way was not right, should first access the object then
//access member function
temp = Pitcher[firstOutOfOrder];
//^^^should be Pitchers, similar errors below
location = firstOutOfOrder;
do
{
totalComparisons++;
Pitcher.GetERA1[location] = Pitcher.GetERA1[location - 1];
//^^^similar error as inside if condition
location--;
}while(location > 0 && Pitcher.GetERA1[location - 1] > temp);
//^^^similar error as inside if condition
Pitcher.GetERA1[location] = temp;
//^^similar error as in if condition and name error
}
}
Meanwhile, you put the InsertionSort declaration as a member of the Pitcher class
public:
.
.
void InsertionSort(vector<Pitcher>&);
and you also declare the same function inside main,
void InsertionSort(vector<Pitcher> Pitchers&);
//should be vector<Pitcher>& Pitchers
using namespace std;
int main()
the member function probably should be removed in your case. InsertionSort is not a responsibility of your Pitcher class.
Unless this is homework, you're better off using the build in sort from
<algorithm>

Is this a good way to store, iterate and delete pointers in an std::vector?

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <vector>
using namespace std;
struct delete_ptr
{
template<typename T>
void operator()(T*& t)
{
delete t;
t = 0;
}
};
struct is_null_ptr
{
template<typename T>
bool operator()(T*& t)
{
return t == 0;
}
};
struct A
{
static void removeDead(A*& a)
{
if(a and a->dead)
delete_ptr()(a);
}
static void killSome(A* a)
{
if(a and a->isDead() == false and rand()%100 == 0)
{
static int counter = 0;
cout << "Kill___" << ++counter << endl;
a->kill();
}
}
static void reviveSome(A* a)
{
if(a and a->isDead() and rand()%3 == 0)
{
static int counter = 0;
cout << "Revive___" << ++counter << endl;
a->revive();
}
}
A():dead(false)
{
}
virtual ~A()
{
static int counter = 0;
cout << "Dtor___" << ++counter << endl;
}
bool isDead(){return dead;}
void kill(){dead = true;}
void revive(){dead = false;}
bool dead;
};
int main()
{
srand(time(0));
vector<A*> as;
for(int i = 0; i < 200; ++i)
{
A* a = new A;
as.push_back(a);
}
for_each(as.begin(),as.end(),A::killSome);
for_each(as.begin(),as.end(),A::reviveSome);
for_each(as.begin(),as.end(),A::removeDead);
as.erase( std::remove_if(as.begin(),as.end(),is_null_ptr()),as.end());
cout << as.size() << endl;
for_each(as.begin(),as.end(),delete_ptr());
as.clear();
return 0;
}
It allocates them, and prints the right output but I'm not sure this is the right thing I'm doing. I was just trying to use pointers in a vector and delete them when a certain condition happens, without using boost or c++11.
So what do you think about it?
Since the only smart pointer present in the current STL (auto_ptr) cannot be used in containers, I would say your way is a good one under the given conditions.
You could think about implementing your own unique_ptr or shared_ptr however.
PS: There are many reasons to use pointers instead of the actual objects in a container, one is polymorphism. Another one is that the actual objects are already stored somewhere else (think of an index structure to already stored objects).