C++ vector of struct allocated on stack - c++

If we have a vector of struct pointer MyInfo* (allocated on heap). Then we can check vec[i] == NULL to know whether there is a struct in the vec[i], like this, if (vec[i] != NULL) //then do some processing
However, if we allocate MyInfo on stack instead of on heap, then we have vector<MyInfo> as shown below. I guess each vec[i] is initialized by the struct default constructor. How do you check whether vec[i] contains a non-empty struct similar to above NULL pointer case, like if (vec[i] contains valid struct) //then do some processing
My code is below
#include <iostream> // std::cout
#include <string>
#include <vector>
using namespace std;
struct MyInfo {
string name;
int age;
};
int main () {
vector<MyInfo> vec(5);
cout << "vec.size(): " << vec.size() << endl;
auto x = vec[0];
cout << x.name << endl; //this print "" empty string
cout << x.age << endl; //this print 0
return 0;
}

There are some options you can use. The first and easiest one, is to define a value to each (or for one) of your struct's variables, that will point that the struct is not initialized yet. In this case, age should be large or equal to 0, to be logicly straight. So, you can initialize it to -1, like this:
struct MyInfo {
string name;
int age = -1;
};
// Or
struct MyInfo {
string name;
int age;
MyInfo() : name(""), age(-1) {} // Use constructor
};
Now, in your main function, it will print in the age the value -1. Also, you can see the empty of the name variable as a sign for it too.
Another way might be using flag and get/set operations to indicate when the variables are initialize:
struct MyInfo {
private:
std::string _name;
int _age;
bool age_initialize = false;
bool name_initialize = false;
public:
void name(const std::string &name_p) { _name = name_p; name_initialize = true; }
void age(int age_p) { _age = age_p; age_initialize = true; }
void init(int age_p, const std::string &name_p) { age(age_p); name(name_p); }
bool is_initialize() { return name_initialize && age_initialize; }
int age() { return _age; }
std::string name() { return _name; }
};
int main() {
std::vector<MyInfo> vec(5);
std::cout << "vec.size(): " << vec.size() << std::endl;
auto x = vec[0];
std::cout << x.is_initialize() << std::endl; //this print 0
std::cout << x.name() << std::endl; //this print "" empty string
std::cout << x.age() << std::endl; //this print 0
return 0;
}
You can also throw an exception when calling int age() of std::string name() function, if those values are not initialize yet:
struct MyInfo {
private:
/* ... */
public:
/* ... */
int age() {
if (!age_initialize) throw std::runtime_error("Please initialize age first.");
return _age;
}
std::string name() {
if (!name_initialize) throw std::runtime_error("Please initialize name first.");
return _name;
}
};

Related

C++: question on placement new in a union using Stroustrup example

I'm using the following example from Stroustrup C++ 4th Ed. Page 218. My question is regarding the destructor.
Questions:
How does placement new(&s) string{ss} allocate room for the new string when it's my understanding that union size is fixed to the largest member? Is string s not a 0 element string? If so, how does the placement new create a larger string if there is not enough space in the union?
#include <iostream>
#include <string>
using namespace std;
class Entry2 { // two alternative representations represented as a union
private:
enum class Tag { number, text };
Tag type; // discriminant
union { // representation
int i;
string s; // string has default constructor, copy operations, and destructor
};
public:
struct Bad_entry { }; // used for exceptions
string name;
Entry2(int n) : type{Tag::number}, i{n} { };
Entry2(string ss) : type{Tag::number} { new(&s) string{ss}; };
~Entry2();
Entry2& operator=(const Entry2&); Entry2(const Entry2&);
// ...
int number() const; string text() const;
void set_number(int n);
void set_text(const string&); // ...
};
Entry2::~Entry2()
{
if (type==Tag::text)
s.~string();
}
int Entry2::number() const
{
if (type!=Tag::number) throw Bad_entry{};
return i;
}
string Entry2::text() const
{
if (type!=Tag::text) throw Bad_entry{};
return s;
}
void Entry2::set_number(int n)
{
if (type==Tag::text) {
s.~string();
type = Tag::number;
}
i = n;
}
void Entry2::set_text(const string& ss)
{
if (type==Tag::text)
s = ss;
else {
new(&s) string{ss};
type = Tag::text;
}
}
Entry2& Entry2::operator=(const Entry2& e)
{
if (type==Tag::text && e.type==Tag::text) {
s = e.s; // usual string assignment
return *this;
}
if (type==Tag::text)
s.~string(); // explicit destroy (§11.2.4)
switch (e.type) {
case Tag::number:
i = e.i;
break;
case Tag::text:
new(&s) string{e.s};
type = e.type;
}
return *this;
}
int main(int argc, char *argv[])
{
Entry2 e0(0);
cout << e0.number() << endl;
try {
e0.text();
} catch (...) {
cout << "caught except" << endl;
}
e0.set_text("abcd");
cout << e0.text() << endl;
return 0;
}
No, the destructor should not always do this. Remember that in a union, only one of the fields is actually active at any one time. If the std::string member of the union isn't active, then calling its destructor would be a Bad Thing (cause undefined behavior) because there wasn't a string there to destroy. Instead, we instead only call the destructor on that std::string member if at some previous point in time we activated the std::string there.
Hope this helps!

How to insert to std::map in C++11?

I am trying to insert a set of pair values into a std::map in c++11. However, the values don't seem to insert into the std::map. Please do go over my code about the same. I appreciate any and all help.
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<cstdlib>
#include<utility>
#include<ctime>
#include "print.h"
class ReportCard
{
private:
std::map<std::string, double> m_report_card;
public:
std::map<std::string, double> getReportCardInstance() { return m_report_card; }
};
class Student
{
private:
int m_roll_no;
std::string m_name;
ReportCard m_reportCard;
public:
Student(int inRollNo, const std::string& inName) :
m_roll_no(inRollNo), m_name(inName)
{}
std::string getName() { return m_name; }
int getRollNo() { return m_roll_no; }
ReportCard getReportCard() { return self.m_reportCard; }
int getReportCardSize() { return m_reportCard.getReportCardInstance().size(); }
};
class Driver
{
private:
std::vector<Student> student_list;
std::vector<Student> temp;
public:
void studentTestPopulate()
{
student_list.push_back(Student(1, "Tim"));
student_list.push_back(Student(2, "Matt"));
student_list.push_back(Student(100, "Luke"));
student_list.push_back(Student(68, "Lissy"));
student_list.push_back(Student(20, "Tony"));
student_list.push_back(Student(33, "Joseph"));
student_list.push_back(Student(14, "Sid"));
student_list.push_back(Student(15, "Roby"));
student_list.push_back(Student(44, "Rohan"));
student_list.push_back(Student(11, "Kevin"));
student_list.push_back(Student(19, "George"));
}
void reportCardPopulate()
{
for (auto& student : student_list)
{
std::cout << student.getName() << std::endl;
student.getReportCard().getReportCardInstance().insert(std::make_pair<std::string, double>("Math", generateMark));
//This is the function that does not work. No marks are printed!!
for (auto& mark : student.getReportCard().getReportCardInstance())
{
std::cout << mark.first << " " << mark.second;
}
//student.getReportCard().getReportCardInstance().insert(std::make_pair("Science", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("Geography", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("French", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("History", generateMark));
}
}
void showAllStudentDetails()
{
for (auto& student : student_list)
{
std::cout << student.getName() << std::endl;
std::cout << student.getRollNo() << std::endl;
std::cout << "REPORT CARD : " << student.getReportCardSize() << std::endl << std::endl;
for (auto& mark : student.getReportCard().getReportCardInstance())
{
std::cout << mark.first << std::endl;
std::cout << mark.second << std::endl;
}
}
}
};
int main()
{
srand(time(NULL));
Driver driver;
driver.studentTestPopulate();
driver.reportCardPopulate();
//driver.showAllStudentDetails();
}
The reportCardPopulate() function is supposed to insert pairs of values into a report_card map. However, the insert function doesn't seem to work.
When we try to print the values within the reportCardPopulate() function, it doesn't print anything. When I try to print the size of the map, it prints 0. When I printed the size using sizeof() it prints the same size before and after the insertion.
The following functions
std::map<std::string, double> getReportCardInstance() { ... }
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ReportCard getReportCard() { ... }
//^^^^^^^^
returns the copy of std::map<std::string, double> and ReportCard class respectively. Therefore, whatever you insert here
student.getReportCard().getReportCardInstance().insert(std::make_pair<std::string, double>("Math", generateMark));
does on the copies of the above, hence the original member in ReportCard(i.e. m_report_card) will never get be updated. After the call of above line, the copies will be destroyed and expecting it to work make no sense.
Secondly, shown code is wrong, because in c++ you should have used this not self
ReportCard getReportCard()
{
return self.m_reportCard;
//^^^^ --> should be `return this->m_reportCard;`
// or simply `return m_reportCard;`
}
Correcting the above, and returning the member by reference will make the code work.
(See live online)
std::map<std::string, double>& getReportCardInstance()
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
return m_report_card;
}
ReportCard& getReportCard()
//^^^^^^^^
{
return m_reportCard;
}
That being said, your ReportCard and Student classes will expose the members, if you do the above. Which is not a good design. If those are meant only for the internal uses of Driver class, you could keep them as private properties of Driver class.
#include <vector>
#include <map>
#include <string>
#include <iostream>
class Driver /* final */ // -> optional
{
private: // Student is private for Driver class
class Student
{
// type alias is enough for the map
using ReportCard = std::map<std::string, double>;
private:
int m_roll_no;
std::string m_name;
ReportCard m_reportCard;
public:
Student(int inRollNo, const std::string& inName)
: m_roll_no{ inRollNo }, m_name{ inName }
{}
// make the member functions const if they are not modifing the members
const std::string& getName() const { return m_name; }
int getRollNo() const { return m_roll_no; }
ReportCard& getReportCard() { return m_reportCard; }
std::size_t getReportCardSize() const { return m_reportCard.size(); }
};
private:
std::vector<Student> student_list;
std::vector<Student> temp;
public:
void studentTestPopulate()
{
// construct the `Student` in-place using `std::vector::emplace_back`
student_list.emplace_back(1, "Tim");
student_list.emplace_back(2, "Matt");
student_list.emplace_back(100, "Luke");
student_list.emplace_back(68, "Lissy");
student_list.emplace_back(20, "Tony");
student_list.emplace_back(33, "Joseph");
student_list.emplace_back(14, "Sid");
student_list.emplace_back(15, "Roby");
student_list.emplace_back(44, "Rohan");
student_list.emplace_back(11, "Kevin");
student_list.emplace_back(19, "George");
}
void reportCardPopulate()
{
for (auto& student : student_list)
{
std::cout << student.getName() << "\n";
student.getReportCard().emplace(student.getName(), 12.0);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// use `std::map::emplace` for constructing `ReportCard` in-place
for (auto& mark : student.getReportCard())
{
std::cout << mark.first << " " << mark.second << "\n";
}
}
}
// ... other members
};
int main()
{
Driver driver;
driver.studentTestPopulate();
driver.reportCardPopulate();
}
#include <iostream>
#include <map>
class ReportCard
{
//private: this is the default anyway for a class
public: //made to be able to print the internals below.
std::map<std::string, double> m_report_card;
public:
/* this returns an instance of the std::map. The map is copied and
returned, so any modifications will not affect m_report_card
std::map<std::string, double> getReportCardInstance()
{
return m_report_card;
}
if you want to do this, return std::map<std::string, double>&.
std::map<std::string, double>& getReportCardInstance()
{
return m_report_card;
}
*/
// better solution is to have a method to add the report
void add_report(const std::string& first,double second)
{
m_report_card[first] = second;
}
};
int main() {
ReportCard rc;
rc.add_report("Percy",1.0);
rc.add_report("Pig",2.0);
for(auto internal_report_card : rc.m_report_card)
{
std::cout << internal_report_card.first << ", "
<< internal_report_card.second << std::endl;
}
return 0;
}
Demo

Changing a value of an element in an object that is stored in a vector in another object through an external function in C++

So made a class called ‘Item’, and the object of that class will have a 100% condition at the start, the Player stores items (with name “apple” in this case) whenever I tell him to. In the degradeT function I want to pass the whole vector containing the items that the player has picked up by far and then change the condition of each Item in that vector by -1 through the chCond function.
first error:
initial value of reference to non-const must be an lvalue
second error:
'void degradeT(std::vector<Item,std::allocator<_Ty>> &)': cannot convert argument 1 from 'std::vector<Item,std::allocator<_Ty>>' to 'std::vector<Item,std::allocator<_Ty>> &'
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
using std::cout; using std::cin; using std::endl;
using std::string; using std::vector; using std::to_string;
class Item {
private:
string name; // Item name
float condition; // Item condition
bool consumable; // Is the item consumable
public:
Item() {}
Item(string a, float b, bool c) { name = a; condition = b; consumable = c; }
Item(string a, bool c) { name = a; condition = 100.f; consumable = c; }
string getName() {
return name;
}
float getCond() {
return condition;
}
bool isCons() {
return consumable;
}
void chCond(float a) { // Change Item condition
condition += a;
}
};
//-----------------------
class Player {
private:
vector<Item> plItems; // Item container
public:
Player() {}
void pickUpItem(Item a) { // Adding Items to inventory
plItems.push_back(a);
cout << a.getName() << " added to inventory!\n";
}
void checkItemConds() { // Checking condition of all items
for (unsigned int a = 0, siz = plItems.size(); a < siz; a++) {
cout << plItems[a].getName() << "'s condition is: " << plItems[a].getCond() << "%\n";
}
}
Item returnItem(unsigned int a) { // Return a specific Item
return plItems[a];
}
int getCurInvOcc() { // Get cuurent inventory occupation
return plItems.size();
}
vector<Item> getPlItems() { // Return the vector (Item container)
return plItems;
}
};
//-------------------------
void degradeT(vector<Item>& Itemss); // Degrade item after some time
//-------------------------
int main()
{
Player me; // me
string inp; // input
int num = 1; // apple 1, apple 2, apple 3...
while (inp != "exit") {
cin >> inp;
if (inp == "addApple") {
Item apple(("apple " + to_string(num)), true);
me.pickUpItem(apple);
num++;
}
if (inp == "checkItemConds") {
me.checkItemConds();
}
if (inp == "timeTick") {
// This doesn't have anything to do with time I just want to test the function manually
degradeT(me.getPlItems());
}
}
system("PAUSE");
return 0;
}
void degradeT(vector<Item> &Itemss) {
for (unsigned int a = 0, siz = Itemss.size(); a < siz; a++) {
Itemss[a].chCond(-1);
cout << Itemss[a].getName() << endl;
}
}
I'm not sure what your question is, but your error is related to the function void degradeT(vector<Item> & Itemss).
This functions expects a reference but you are passing an r-value. You can either return a reference with getPlItems() or pass an l-value to degradeT.

Return struct array from function c++

i am trying pass a struct array from a function. i searched a lot but was unable to find a way to this. below is the code i am tring.
struct menuItemType
{
int itemNo;
string menuItem;
double price;
};
void getData(menuItemType *menuList[10])
{
menuList[0]->itemNo = 111;
menuList[0]->menuItem = "Apple";
menuList[0]->price = 2.00;
....
menuList[0]->itemNo = 120;
menuList[0]->menuItem = "Chocolate";
menuList[0]->price = 5.00;
}
int main()
{
/* i know that i can't return a array. but i want to get the menuList[10] values here.
not sure which code i have to use..*/
}
Your void getData(menuItemType *menuList[10]) does not return anything. Instead, it fills the data in the memory pointed by input parameter.
int main()
{
menuItemType data[10];
getData(&data);
std::cout << data[9].menuItem << std::endl; // Chocolate
}
However, why are you insisting on using low level arrays? Use std::vector instead.
std::vector<menuItemType> getData()
{
std::vector<menuItemType> data;
data.push_back({111, "Apple", 2.00});
...
data.push_back({120, "Chocolate", 5.00});
return std::move(data);
}
int main()
{
std::vector<menuItemType> data = getData();
std::cout << data[9].menuItem << std::endl; // Chocolate
}
It will print Chocolate, because I assume there is a typo in your code.

Free memory after remove_if

In the following example I remove from list some elements in the range for which the application of pr2 to it return true.
m_list.remove_if(pr2(*tmp_list));
It seems to me it is necessary to delete this objects, which was removed above, becase when I create it I use "new" (new CRectangle()). How I can do this? I don't know which (and how much) elements will be remove after remove_if.
// test_cconnection.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <conio.h>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
class CDrawObject
{
public:
virtual ~CDrawObject()
{
cout << "Drop CDrawObject: " << id_ << endl;
}
int getId() const
{
return id_;
}
virtual void draw()
{
}
protected:
static int id;
int id_;
};
class CRectangle : public CDrawObject
{
public:
CRectangle()
{
id_ = id++;
}
~CRectangle()
{
cout << "Drop CRectangle: " << id_ << endl;
}
virtual void draw()
{
cout << "CRectangle, id: " << id_ << endl;
}
};
class CMarker : public CDrawObject
{
CDrawObject* obj;
public:
CMarker(CDrawObject* obj_)
{
obj = obj_;
}
~CMarker()
{
cout << "Delete marker of object with id: " << obj->getId() << endl;
}
CDrawObject* getObject() const
{
return obj;
}
virtual void draw()
{
cout << "CMarker of oject with id: " << obj->getId() << endl;
}
};
int CDrawObject::id = 0;
// predicate for compare objects with int id
class pr : public std::unary_function<CDrawObject*, bool>
{
private:
int id_;
public:
pr(int id): id_(id) {}
bool operator()(CDrawObject* arg) const
{
return (arg->getId() == id_);
}
};
// predicate for check objects with type CMarker and
// compare with CDrawObject* obj
class pr2 : public std::unary_function<CDrawObject*, bool>
{
private:
CDrawObject* obj_;
public:
pr2(CDrawObject* obj)
{
obj_ = obj;
}
bool operator()(CDrawObject* arg) const
{
if (dynamic_cast<CMarker*>(arg))
return ((dynamic_cast<CMarker*>(arg))->getObject() == obj_);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
list<CDrawObject*> m_list;
list<CDrawObject*>::iterator i_list, tmp_list;
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CMarker(*tmp_list));
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CMarker(*tmp_list));
// print on screen items of m_list
for (i_list = m_list.begin(); i_list != m_list.end(); ++i_list)
(*i_list)->draw();
// get an iterator to the first element in the range with id_ = 2
tmp_list = find_if(m_list.begin(), m_list.end(), pr(2));
if (tmp_list != m_list.end())
{
// remove from list all elements with type CMarker
// and CDrawObject = tmp_list
m_list.remove_if(pr2(*tmp_list));
}
cout << endl << "--------" << endl;
// print on screen items of m_list
for (i_list = m_list.begin(); i_list != m_list.end(); ++i_list)
(*i_list)->draw();
_getch();
return 0;
}
Well you could:
HACKISH: delete the object in the predicate.
ANNOYING: Stay away from remove_if and implement everything it does on your own except add the delete.
BETTER: use RAII objects rather than raw pointers. Some sort of smart ptr in other words.
The way it's implemented at the moment, you won't be able to delete the memory that you allocated for those objects. In general, it takes some extra effort to perform memory cleanup when you have containers of pointers to dynamically allocated memory. Here's one way to do it:
// Assume there's a predicate function called ShouldRemove(int value);
list<int> my_list;
// initialization...
for (list<int>::iterator itr = my_list.begin(); itr != my_list.end(); ) {
if (ShouldRemove(**itr)) {
delete *itr;
itr = my_list.erase(itr);
} else {
++itr;
}
}
But as Noah Roberts pointed out, this is all much easier to deal with if you store your pointers as smart pointers that clean up after themselves.
Standalone remove_if never resizes a collection and returns an iterator pointing to the first object for which predicate is false.
It is therefore more appropriate for your task.