C++ Vector of Objects, are they all named temp? - c++

New to C++ OOP, I recently learned about classes and objects. I created a straightforward class and menu-driven program that adds a temp object to a vector of movies. I have a quick question that I can't quite understand.
Am I just pushing multiple "temp" objects into the vector?
In my head i'm visualizing this as vector my_movies = {temp, temp, temp}; and continously adding 'temp' objects until the user is done. Is this the right way to picture it?
#include <iostream>
#include <vector>
using namespace std;
class Movie
{
private:
string name;
public:
string get_name() { return name; }
void set_name(string n) { name = n; }
};
void menu() {
cout << "1. Add movie" << endl;
cout << "2. Show Movies" << endl;
cout << "3. Quit" << endl;
}
int getChoice(int &choice) {
cout << "Enter you choice: ";
cin >> choice;
return choice;
}
int main() {
vector<Movie> my_movies;
int choice = 0;
string name;
do {
menu();
getChoice(choice);
switch (choice) {
case 1: {
Movie temp;
cout << "Set user name: ";
cin >> name;
temp.set_name(name);
my_movies.push_back(temp);
break;
}
case 2: {
for (auto &mv : my_movies)
cout << mv << endl;
break;
}
}
} while (choice != 3);
return 0;
}

In your case, when you are calling push_back it will copy your "temp" object, which is a local object on the stack. It will be copied into a new object which is stored on the heap, held by the vector object. The vector will store these as an array internally (the default vector with the default allocator etc).
It's also possible to "move" the object (under C++11 and later), if you understand the difference, but doing push_back(std::move(temp)), which generally gives better performance. In your case it would avoid copying the string member "name", and move it instead, avoiding a new allocation for the string inside the Movie in the vector.
See here for more details on push_back
Appends the given element value to the end of the container.
The new element is initialized as a copy of value.
https://en.cppreference.com/w/cpp/container/vector/push_back
If you are just talking about the name of the movie, it will be what ever is entered from cin. Objects don't have names themselves. The local variable name "temp" is just what you see when you write the code, but is just used to tell the compiler which object is being used - the object itself doesn't have a name form the compilers perspective.

Related

How to initialize array of objects with user defined values and take input from user?

#include <iostream>
using namespace std;
class car{
string owner;
string car_num;
string issue_date;
car(string o, string cn, string id)
{
owner = o;
car_num = cn;
issue_date = id;
}
void getInfo()
{
cout << "Car's Owner's Name : " << owner << endl;
cout << "Cars' Number : " << car_num << endl;
cout << "Car's Issue Date : " << issue_date << endl;
}
};
int main()
{
int n;
cout << "Enter total number of cars stored in your garage : \n";
cin >> n;
car c1[n]; //incomplete code due to the issue
return 0;
}
Here I want to take the total car numbers from user. And also want to take the car properties from user by using a loop. But how Can I do that while using a constructor?
My advice is not to over use the constructor. It supposed to construct, and really should only construct. In your case, you don't even really need a constructor.
Instead add a new function to do initialization.
Traditional is to use an operator >> which is often an external function.
As for the loop...
car c1[n]; //incomplete code due to the issue
is not legal C++ (although it's allowed in C, and many compilers that are also C compilers)
It's better to use a vector. So...
vector<car> c1(n);
for (auto& c : c1)
cin >> c;
An advanced technique is to use a istream iterator, which will allow you to use algorithms like std::copy, calling the input operator for each member of the vector. However, it's really not required, just a "nicety"
car c1[n]; //incomplete code due to the issue
In fact, you have 2 issues here:
Variable-Length Arrays (VLA) are not allowed in standard C++. They
are optionally allowed in standard C, and are supported by some
C++ compilers as an extension.
You can't have an array of objects w/o default constructor (unless you fully initialize it).
Assuming you don't want to change the class (other than insert public: after the data members), the modern solution should use std::vector:
std::vector<car> c;
//incomplete part
for(int i = 0; i < n; i++){
std::string owner, car_num, issue_date;
//TODO: get the strings from the user here ...
c.emplace_back(owner, car_num, issue_date);
}
Use pointer array instead.e.g.
car* c1[n];
//incomplete part
for(int i = 0; i < n; i++){
//take input and process
c1[i] = new car(//put processed inputs here);
}
PS: I feel like I made a mistake somewhere but can't test it now. If it doesn't work, put a comment here.
You can use loop with std::cin, likefor(int i=0;i<n;++i){std::cin<<num;}. The 'n' I mentioned in the code can also be assigned by std::cin
int n;
std::cin>>n;
car* cars=new car[n];
for(int i=0;i<n;++i)
{
std::getline(cars[i].owner,std::cin);
// and something other you'd like to do, like to test its validity
}
#include <iostream>
using namespace std;
class car{
public:
string owner;
string car_num;
string issue_date;
void cars(string o, string cn, string id)
{
owner = o;
car_num = cn;
issue_date = id;
getInfo();
}
void getInfo()
{
cout << "Car's Owner's Name : " << owner << endl;
cout << "Cars' Number : " << car_num << endl;
cout << "Car's Issue Date : " << issue_date << endl;
}
};
int main()
{
int n;
string a,b,c;
cout << "Enter total number of cars stored in your garage : \n";
cin >> n;
car cas[n]; //incomplete code due to the issue
for(int i=0;i<n;++i)
{
cout<<"value1:";
cin>>a;
cout<<"value2:";
cin>>b;
cout<<"value3:";
cin>>c;
cas[i].cars(a,b,c);
}
return 0;
}

C++ executes blanks after class creation

I'm trying to learn C++ to help my sibling with their assignment. So I'm attempting the assignment. It's a simple program to load a dictionary test file with words, their type, and definition to an array of Word type objects. I was able to get started with a normal string array instead of an object array as requested. But as soon as I defined the Word class and the array the code builds without an issue. When I try to run the code the cursor simply blinks for a few seconds and returns to the normal terminal.
Am I doing something wrong with my Class constructor ??
#include <fstream>
#include <string>
using namespace std;
class Word {
public:
string WordEntry;
string Type;
string Definition;
//constructor
Word(string word, string type, string definition){
WordEntry=word;
Type=type;
Definition=definition;
}
};
int main(){
cout << "Test1";
Word *wordArray[318555];
int count=0;
string word, type, definition,blank;
cout << "TEST" << count << "\n";
ifstream file("dictionary2021 (1).txt");
if (file.is_open()){
cout << "File dictionary2021.txt has been opened \n";
while (!file.eof()){
getline(file,word);
getline(file,type);
getline(file,definition);
getline(file,blank);
wordArray[count]= new Word(word,type,definition);
count++;
}
file.close();
cout << "File dictionary2021.txt has " << count/3 << " entries\n";
}
cout << "TEST" << count << endl;
cout << cc;
int selection;
string input;
cout << "Function List - Please hit Enter after your selection \n";
cout << " 1. Word Search \n 2. Repetitive z search \n 3. Wild Card Search\n";
cout << "Selection:";
cin >> selection;
if(selection=1){
cout << "Enter word:\n";
cin >> input;
string str("a");
for (int i = 0; i < 12; i+3)
{
cout << "1";
if (input.compare(str)== 0)
{
cout << wordArray[i+1];
return 0;
}
cout << "2";
}
}
}```
Word* wordArray[318555]; is a huge value and we're talking about 2548440 bytes (or roughly 2.4MB). This might be too large for a single stack frame and can easily be inefficient.
What I suggest is to use std::vector to store the word array and use std::vector<>::push_back() method to insert data to it.
Note: In your code snippet your not deallocating the Word object pointers once everything is done. Either explicitly delete those pointers using delete or use a smart pointer like std::unique_ptr.

Need Help Solving a Memory Leak

I am writing an inventory management system using dynamic arrays using objects. The strategy for this is NOT to use vectors, but allocate a dynamic array that 1 increment by 1 every time the client needs to add to the inventory. I am getting a "segmentation fault" error so I believe it is a memory leak.
I have tried rewriting it to match addresses but no luck. I think the buildItem function produces a temporary object, which is destroyed when the function ends. I don't know how to fix this though.
#include <iomanip>
#include <iostream>
#include <string>
using namespace std;
class InventoryObject {
private:
int itemNumber;
string description;
int qty;
float price;
public:
int getItemNum() { return itemNumber; }
string getDescription() { return description; }
int getQty() { return qty; }
float getPrice() { return price; }
void storeInfo(int p, string d, int q, float pr);
void showValues(InventoryObject &item);
};
// Function Implementation
void InventoryObject::storeInfo(int p, string d, int q, float pr) {
itemNumber = p;
description = d;
qty = q;
price = pr;
}
void InventoryObject::showValues(InventoryObject &item) {
cout << fixed << showpoint << setprecision(2) << endl;
cout << "Part Number : " << item.getItemNum() << endl;
cout << "Description : " << item.getDescription() << endl;
cout << "Quantity: : " << item.getQty() << endl;
cout << "Price : " << item.getPrice() << endl << endl;
}
// Function Prototypes for Client Program
InventoryObject buildItem();
void drawMenu();
void showValues(InventoryObject &);
void printInventory(int size);
int main() {
int size = 1;
int choice;
bool quit = false;
InventoryObject part;
InventoryObject *iArray = new InventoryObject[size];
drawMenu();
cin >> choice;
while (quit == false) {
if (choice == 1) {
InventoryObject item;
item = buildItem();
iArray[size] = item;
}
if (choice == 2) {
iArray[size].showValues(iArray[size]);
}
if (choice == 3) {
quit = true;
}
}
return 0;
}
// This function accepts the data from the client and creates a new
// InventoryObject object. The object is then supposed to be added to the
// dynamic array.
InventoryObject buildItem() {
InventoryObject *tempObject = new InventoryObject;
int itemNum;
string description;
int qty;
float price;
cout << "Enter data for the item you want to enter:\n\n";
cout << "Item Number: \n";
cin >> itemNum;
cout << "Description: \n";
cin.get();
getline(cin, description);
cout << "Quantity: \n";
cin >> qty;
cout << "Unit price: \n";
cin >> price;
tempObject->storeInfo(itemNum, description, qty, price);
return *tempObject;
}
void drawMenu() {
cout << "1. Add Inventory\n";
cout << "2. Display Inventory\n";
cout << "3. Quit Program\n";
}
I expect the object to be created, and put into the dynamic array. Then redraw the menu and interact with the client from there.
The main problem is that you are writing past the area of memory you allocated for your array (iArray).
Specifically, this line of code:
iArray[size] = item;
Should actually be:
iArray[size - 1] = item;
The above does not cause memory leak, but something else you do in your program does:
Your function buildItem does this when it returns the value of a pointer without first deleting the pointer.
To fix this, change
InventoryObject *tempObject = new InventoryObject;
to
InventoryObject tempObject;
And finally, remember to delete iArray before return 0; in main
delete[] iArray
Build your program with debug symbol (-g flag)
Run limit-c unlimited
Run your program until it crashes
Run gdb core where is the name of your program
In the gdb climate type bt.
You should now have a back trace of where your program is crashing
Daniel, here my observations:
The function buildItem must return a pointer of InventoryObject i mean: InventoryObject*. It is bease you are returning the pointer of the memory that was reserved at the moment of create it (InventoryObject *tempObject = new InventoryObject;), like this:
InventoryObject* buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
tempObject->storeInfo(itemNum, description, qty, price);
return *tempObject;
}
keep in mind that your main array (InventoryObject *iArray) contains an array of pointer.
The main problem that i see. In the main you are using an if block where there is a local variable called item (InventoryObject item). Let me explain something: Each instance of a class, like you InventoryObject item will be destroyed, release its memory, if its context is over.
So, in this case, the problem is that you was casting the pointer of at the moment of returning from the function buildItem (return *tempObject) and store its value into a local variable InventoryObject item which shall be release when the thread leaves the if, the context of the local variable.
Change your local variable InventoryObject item to handle a pointer: InventoryObject* item
if (choice == 1) {
InventoryObject* item;
item = buildItem();
iArray[size] = item;
}
I recommend you to read the chapters 7. Pointers, Arrays, and References and 17 Construction, Cleanup, Copy, and Move from the book The C++ Programming Language of Bjarne Stroustrup.
I have tried rewriting it to match addresses but no luck. I think the buildItem function produces a temporary object, which is destroyed when the function ends.
Your intuition is half true. There is something wrong with this function, but it has nothing to do with temporaries. It's more about you allocating an object you never destroy.
The problem is that you are trying to do something similar to what I was trying to do in this question, but not quite. Even if you were doing what I was doing, it still wouldn't be a good idea for reasons that lots of people already explained better than I could.
The point is is that the base of the issue is that is if you use new to allocate something, you must use delete to deallocate it, which you don't do here.
InventoryObject buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
return *tempObject;
}
You simply return the object by value without deleting it first. You aren't even returning it by reference which would be better (yet still probably bad: again, see my question). You are allocating an object and never deleting it. That's a leak.
You have two solutions: either return the pointer directly (if you really need a pointer) like this:
InventoryObject* buildItem() {
InventoryObject *tempObject = new InventoryObject;
//...
return tempObject;
}
Or, you can simply just return the object by value like this:
InventoryObject buildItem() {
InventoryObject tempObject;
//...
return tempObject;
}
Given the code sample you've shown, this is my recommendation of the two.
Note: I should mention that if for some reason you do need to return a pointer and have some factory method pattern with some sort of polymorphism in play, you also have the option of smart pointers too. That is what I ended up doing based on other people's suggestions from the other thread. I can personally recommend it. Basically, your code code would become something like this instead:
std::unique_ptr<InventoryObject> buildItem() {
std::unique_ptr<InventoryObject> tempObject = new InventoryObject; // C++11 way, I think
// C++14 and above has a function called make_unique you could use instead.
//...
return tempObject;
}
However, this is only useful if you have some sort of polymorphism going on as I did. Given the code sample you have posted so far, you really only need to return by value in your situation. I just thought I'd mention this other option too, just in case.
P. (P.) S. This answer is only about your suspected memory leak. That said, there are other helpful answers here that you should heed. Problems described in other answers could contribute to your problem and could really trip you up down the road (actually, could still trip you up now too) if you don't fix them, so I highly recommend taking their advice too.

Calling Main replaces vector object and overwrites data

I am learning c++ and I can't wrap my head around this problem. I have created a class object which hold information of a vehicle. And the class name is vehicle. Here is the class:
class vehicle {
private:
string vehicleNumber;
int vehicleType;
public:
//Default Constructor
vehicle();
//Overload Constructor - #params<vehicleNumber, vehicleType>
vehicle(string vehicleNo, int type){
vehicleNumber = vehicleNo;
vehicleType = type;
}
//Some stuff to test
string getNo(){
return vehicleNumber;
}
int getType(){
return vehicleType;
}
};
And now in my main I have a void method which then takes in user input and puts the object in a vector. So in my main:
//Ref
void storeVehicle(vector<vehicle>&);
void storeVehicle(vector<vehicle>& createNewVehicle){
string number;
int type;
cout << "Enter Vehicle Number: ";
cin >> number;
cout << "Enter the vehicle type: ";
cin >> type;
vehicle newV(number, type);
createNewVehicle.push_back(newV);
cout << endl;
}
//
Now here is the problem I am facing. Inside of my main method, I am creating the object. And this works perfectly find except if I call the main method again, it initializes the object again and I loose my previous data. I am not sure how to tackle this.
Here is the main method:
int main(){
vector<vehicle> newVehicle;
storeVehicle(newVehicle);
return main();
}
So here I am returning main() again so that it reruns. But when it does, i loose my previous vector which held the data. Now how can I keep the data and keep calling the storeVehicle method?
Edit
I also have a switch case which I am using to determine what the user chooses as an option. It may be to display the vehicle information or it maybe to add a new vehicle. In this case too, how can I add the vehicle without loosing previous data. Here is my switch which is inside another method:
void mainMenu(){
int option;
cin >> option;
switch(option){
case 1: {
vector<vehicle> newVehicle;
storeVehicle(newVehicle);
break;
}
default:
cout << "Wrong option";
}
}
So now in my main method, I am simply calling this switch method. Either way, I loose the data yes?
EDIT
I don't understand why people keep downvoting. Aren't we all here to learn? And yet I get a downvote. Sooner or later, stackoverflow decides to block me from asking questions.
Main isn't intended to be used like that, it's just an entry point for your application.
For what you're looking to do, you would be interested in looping. This way a certain section of your code can be ran repeatedly until a condition is met.
int main(){
vector<vehicle> newVehicle;
while(true)
{
storeVehicle(newVehicle);
}
return 0;
}
Take a look at how different loop types work. https://www.tutorialspoint.com/cplusplus/cpp_loop_types.htm
Also, the one in my example is an infinite loop, since the condition is always true.
EDIT The question was changed.
The problem is that each time you call the mainMenu function that the vector is recreated. Create the vector in your Main function and then pass it by ref into the mainMenu function, so that it can then be passed into your storeVehicle function.
int main()
{
vector<vehicle> newVehicle;
while (true)
{
mainMenu(newVehicle);
}
return 0;
}
void mainMenu(vector<vehicle>& createNewVehicle) {
int option;
cin >> option;
switch (option) {
case 1: {
storeVehicle(createNewVehicle);
break;
}
default:
cout << "Wrong option";
}
}
You'd have to add a header for this function to work.
#include <limits>
void storeVehicle(vector<vehicle>& createNewVehicle) {
string number;
int type;
cout << "Enter Vehicle Number: ";
cin >> number;
cout << "Enter the vehicle type: ";
cin >> type;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
vehicle newV(number, type);
createNewVehicle.push_back(newV);
cout << endl;
}

Is there a way to declare objects within a conditional statement?

I'm having trouble figuring out how to properly create an object depending on the user's choice.
In the program, I ask the user which they class they want to be--Knight or Wizard. I take input '1' or '2' to represent Knight and Wizard.
I made a switch statement, and within case 1, I declared an object Knight, and the same for Wizard.
I need to use these objects outside of the switch statement, but I can't. I tried to make a 'default' object by making 'Player player;' but because the Player class has a pure virtual function, I can't do that either.
How do I do this effectively?
This is what I have so far:
int main()
{
std::string plyrName;
int input;
bool foo = false;
std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');
std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;
while (input != 1 && input != 2)
{
if (foo == true)
std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
if (!(std::cin >> input))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\n";
std::cout << "Only integers are allowed.\n";
}
else
std::cout << "\n";
foo = true;
}
switch (input)
{
case 1:
{
Wizard player;
break;
}
case 2:
{
Knight player;
break;
}
}
std::cout << "\nHere is your player information summary.\n";
std::cout << player.classType();
system("pause");
return 0;
}
I need to access the player object after is has been created, because I want to output to the user which class they selected. Both Knight and Wizard classes have a function to output this.
EDIT: I have a follow up question. In the diagram, Knight & Wizard have a static variable 'special attack name'. How can I access this variable in the main function? The solution of using unique_ptr means that the pointer will point to the base class Player, thus not allowing access to the derived class members such as the static variable 'special attack name'. Do I have a flaw in my design?
In your case, because you want to accomplish polymorphism, you should go for pointers and references. Why? I would highly recommend this beautiful answer. Why doesn't polymorphism work without pointers/references?
So, should you go for a raw pointer, something like Player *?
In almost all scenarios, you should never ever go for raw pointers and especially, when it points to dynamic memory. Simply because any programming error or an exception might lead to delete getting skipped.
Therefore, I would highly recommend you to go for smart pointers introduced in C++11 like unique_ptr and shared_ptr which follow RAII pattern and guarantee deinitialization.
Here is an example of usage of unique_ptr in your case.
#include <memory>
using PlayerPtr = std::unique_ptr<Player>;
using KnightPtr = std::unique_ptr<Knight>;
using WizardPtr = std::unique_ptr<Wizard>;
int main()
{
...
PlayerPtr playerPtr = nullptr;
switch (input) {
case 1: {
playerPtr = KnightPtr(new Knight);
}
break;
case 2: {
playerPtr = WizardPtr(new Wizard);
}
break;
}
// use playerPtr outside.
}
Edit:
As rightly pointed out by HTNW, you must go for std::make_unique instead of using new. But remember, that is a C++14 concept. You must have compiler support for it.
if you create your variable inside the switch case scope it will get deleted as soon as you leave that scope leading you to UB so you declare it as a pointer so it can outlive the conditional statement ie : you declare it as the base class pointer before & you give it to where it points inside conditional statement
#include<memory>
int main()
{
std::string plyrName;
int input;
bool foo = false;
//create your player ptr
std::unique_ptr<Game_Object> player;
std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');
std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;
while (input != 1 && input != 2)
{
if (foo == true)
std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
if (!(std::cin >> input))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\n";
std::cout << "Only integers are allowed.\n";
}
else
std::cout << "\n";
foo = true;
}
switch (input)
{
case 1:
{ // initialize it and it would work perfectly as you intend
player = std::make_unique<WIZARD>();
break;
}
case 2:
{ //****
player = std::make_unique<KNIGHT>();
break;
}
}
std::cout << "\nHere is your player information summary.\n";
std::cout << player->classType();
system("pause");
return 0;
}
Someone correct me if I'm wrong, but objects are only valid within the scope they are created so once you are out of that switch statement those objects are not accessible anymore.
You should declare a GAME OBJECT class object outside the switch statement and then create either the wizard or the knight (as both classes inherit GAME OBJECT).