So I am writing a C++ program that will mimic a library card catalog. I defined a struct for card and all the info on each card, as well as a working vector and iterator to access/print all variables on specified card using a global void function.
Now, I want to move that void function within a newly defined struct, Catalog that handles all the methods for dealing with library cards, like insert/push_back, search or remove/erase/pop_back. I also want my variables under card to be protected, as I'm constantly told it's good coding practice to have your class/struct variables to be private (I did protected for other classes inherited).
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
struct Card
{
public:
Card(string title, string name)
{
this->title = title;
this->name = name;
}
//protected:
string title = "Unknown";
string name = "Unknown";
};
vector<Card> test;
vector<Card>::iterator it;
void showCard(vector<Card> test)
{
for (it = test.begin(); it != test.end(); it++)
{
if (it->title != "Unknown")
{
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 /
T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
test.push_back(book1);
test.push_back(book2);
test.push_back(book3);
showCard(test);
getchar();
return 0;
}
I guess my question is, how could I call Catalog struct from main to then access the protected variables under Card in order to print the protected variables?
Couldn't be as simple as listing friend struct Card in catalog would it?
Edit: I played around and found that friend struct Catalog under Card was able to get rid of errors in the void function for the protected variables it tried accessing. I still am working to have main pass through catalog, though I had all objects in main defined as Card.
I suppose I could try a setCard() called in main, defined in Catalog where it uses vector to refer to the variables protected.
There are multiple ways to do it, and the right way depends on the context. Here are some possible solutions, from the easiest/hackiest to the most verbose/hardest (not an exhaustive listing):
1. Just make everything public
...
struct Card{
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
string title = "Unknown";
string name = "Unknown";
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->title != "Unknown"){
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
While that does solve the problem, it isn't a good solution. If you would ever want to change the name of member title to main_title you will have quite a pain in doing so, because you will have do edit every single occurrence of title and that can get messy quickly.
2. Make void showCard(vector<Card> test) a friend of struct Card
If void showCard(vector<Card> test) is friend of Card then it will have access to all protected and private members of Card as if they were public. This is a nice solution because only void showCard(vector<Card> test) would have access to these protected members.
Because you can only be friend of previously declared functions, you would need to forward declare the function void showCard(vector<Card> test) before the declaration of Card.
However, because void showCard(vector<Card> test) takes a vector<Card> argument, the class Card needs to be forward declared before the forward declaration of the function.
...
struct Card;
void showCard(vector<Card> test);
struct Card{
public:
friend void showCard(vector<Card> test);
Card(string title, string name){
this->title = title;
this->name = name;
}
protected:
string title = "Unknown";
string name = "Unknown";
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->title != "Unknown"){
printf("%s\n", it->title.c_str());
printf("%s\n", it->name.c_str());
}
}
}
3. Create getters and setters for Card
This is one of the canonical implementations. Every time you make a member private/protected you provide a get_member and a set_member methods for it.
That way everyone can access the member, however, they only can access it if they use those methods. You can even create getters/setters for members that don't exist (i.e. you compute them when you need them).
Since the code speaks more than words, here is an implementation:
...
struct Card{
protected:
string title = "Unknown";
string name = "Unknown";
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
string get_title(){
return this->title;
}
void set_title(string new_title){
this->title = new_title;
}
string get_name(){
return this->name;
}
void set_name(string new_name){
this->name = new_name;
}
};
...
void showCard(vector<Card> test){
for (it = test.begin(); it != test.end(); it++){
if (it->get_title() != "Unknown"){
printf("%s\n", it->get_title().c_str());
printf("%s\n", it->get_name().c_str());
}
}
}
If you ever would want to change the name of the member title to main_title, you would only have to edit get_title and set_title and all your code would keep working as if you didn't change it at all. You could even delete that member or do anything else (like fetching it from a database) because the only place where it's existence and name matters is inside get_title and set_title. Without getters and setters, you would need to edit every single occurrence of title in order to do that.
Getters and setters are also wonderful places to improve the const correctness of your code, making it more robust and efficient. A const-correct get/set pair would look something like this:
const string& get_title() const {
return this->title;
}
void set_title(const string& new_title){
this->title = new_title;
}
And a pair for a non-existent member would look like this:
#include <string>
#include <algorithm>
#include <iterator>
string get_title_and_name(){
// Concatenates the title and name
return this->title + " / " + this->name;
}
void set_title_and_name(string new_string){
// Splits the string between a title and a name
std::size_t split_point = 0;
split_point = new_string.find('/');
this->title = new_string.substr(0, split_point);
// We don't want to include the char '/' of
// the new_string in this->name, so use
// (split_point + 1) instead of split_point
this->name = new_string.substr(split_point + 1, new_string.size() - (split_point + 1));
}
While this solution may be more verbose than others, it is also more flexible.
A suggested solution
We could modify solution 3 by creating a new struct Catalog and putting void showCard(vector<Card> test) inside it. This isn't a usual solution, put it open the possibility for we to get rid of some global variables (global variables are almost always evil) and hide the fact that we are using a vector<Card> to keep Cards (we could use a hashmap instead of a vector and that would work as well, so that other code don't need to know which one we chose of the two).
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
// As in solution 3
struct Card {
protected:
string title = "Unknown";
string name = "Unknown";
public:
Card(string title, string name){
this->title = title;
this->name = name;
}
// Right now we only need getters,
// but we could have setters as well
// (the names are in camelCase to follow
// showCard() naming convention)
string getTitle(){
return this->title;
}
string getName(){
return this->name;
}
};
struct Catalog {
protected:
// This one was a global variable previously
// Also we don't specify a default value
// for it here, we will do that in the constructor
vector<Card> test;
public:
Catalog(){
// The start value of test will be a empty vector
this->test = vector<Card>();
}
// We moved void showCard(vector<Card> test) to here
void showCard(){
// This is a local variable now
vector<Card>::iterator it;
// For loop as in solution 3
for (it = this->test.begin(); it != this->test.end(); it++){
if (it->getTitle() != "Unknown"){
printf("%s\n", it->getTitle().c_str());
printf("%s\n", it->getName().c_str());
}
}
}
// A new method for adding cards,
// because external code shouldn't care
// about how we add or remove card or even
// if we store cards in this machine or in a web server
void addCard(Card card){
this->test.push_back(card);
}
};
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 / T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
Catalog catalog;
catalog.addCard(book1);
catalog.addCard(book2);
catalog.addCard(book3);
// We could even do something like
// catalog.addCard({ "My Horse and wally", "Jason Weber" });
// thankfully to the new addCard method.
// We wouldn't even need to declare book1, book2 and book3
// if we used it that way
catalog.showCard();
getchar();
return 0;
}
After you end writing your program, you may be interested in showing it on Code Review in order to receive insights about how other people would approach the same code and learn how people more experienced or with a different background would write such code.
#obidyne, welcome to StackOverflow. If your goal is to keep the members protected but still be able to show them off (as a formatted string), you could implement a public method showCard, rename your other function showCards and call the public method for each object of the vector.
Just an example (using your own code):
//#include <cstdio>
#include <iostream>
//#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
struct Card
{
public:
Card(string title, string name)
{
this->title = title;
this->name = name;
}
void showCard()
{
if (this->title != "Unknown")
{
printf("%s\n", this->title.c_str());
printf("%s\n", this->name.c_str());
}
}
protected:
string title = "Unknown";
string name = "Unknown";
};
vector<Card> test;
vector<Card>::iterator it;
void showCards(vector<Card> test)
{
for (it = test.begin(); it != test.end(); it++)
{
it->showCard();
}
}
int main()
{
Card book1 = { "Serpent in the heather / Kay Kenyon", "Kay Kenyon"};
Card book2 = { "USA and the Middle East since World War 2 /
T.G. Fraser.", "T.G. Fraser"};
Card book3 = { "My Horse and wally", "Jason Weber" };
test.push_back(book1);
test.push_back(book2);
test.push_back(book3);
showCards(test);
getchar();
return 0;
}
Related
I have a class Citydata, defined in .hh file
struct City_details {
string name;
int taxrate;
};
class Citydata {
public:
bool add_data(string id, string name, int taxrate)
//other member functions...
private:
unordered_map<id, City_details> info_map;
I have trouble implementing the add_data function in .cc file. Here is my try.
bool Citydata::add_data(string id, string name, int taxes) {
if ( info_map.find(id) == info_map.end()) {
City_details dataload;
dataload.name = name;
dataload.taxrate = taxes;
info_map[id] = dataload;
return true;
}
else return false;
}
When I test this, I cannot see any data added the way I wanted. Instead I see one completely empty entry (default empty values), and one entry with right id (the key) but no data added to the parameters. Each time I call add_data, it creates similar pair of one completely empty key-data member, then one with right id and otherwise empty data.
The original program is much longer than this, so problem might persist there too, but I wonder if my approach is flawed by design.
I have made some modifications that makes your program work.
First you should replace unordered_map<id, City_details> info_map; with unordered_map<std::string, City_details> info_map;
Second you had a missing semicolon ; for you add_data member function declaration inside the class which you might have missed while copy pasting the code here on Stackoverflow.
Third i have avoided the use of using namespace std;.
main.cpp
#include <iostream>
#include "file.h"
int main()
{
Citydata c1;
c1.add_data("id1","Georgia", 34);
c1.add_data("id2", "California", 32);
c1.add_data("id3","Texas", 23);
//lets print out the element of info_map
for(auto &it:c1.info_map)
std::cout << it.first <<" "<<it.second.name<<" "<<it.second.taxrate<<std::endl;
return 0;
}
file.h
#pragma once
#include <unordered_map>
#include <string>
struct City_details {
std::string name;
int taxrate;
};
class Citydata {
public:
bool add_data(std::string id, std::string name, int taxrate);
//other member functions...
//private:
std::unordered_map<std::string, City_details> info_map;//the first tempate argument should be int and not id
};
file.cpp
#include "file.h"
bool Citydata::add_data(std::string id, std::string name, int taxes) {
if ( info_map.find(id) == info_map.end()) {
City_details dataload;
dataload.name = name;
dataload.taxrate = taxes;
info_map[id] = dataload;
return true;
}
else return false;
}
#include <iostream>
#include <vector>
using namespace std;
class Test
{
private:
int ID;
string name;
public:
Test(int ID, string name);
};
Test::Test(int ID, string name)
{
this->ID = ID;
this->name = name;
}
int main()
{
vector<Test *> test_vect;
Test *ptr = new Test(100, "John");
test_vect.push_back(ptr);
cout << ptr->ID << endl;
return 0;
}
This is a simple code I'm trying.
I want to access to the data that I stored in vector.
I thought it would be accessible by using -> just like vector of struct but I can't. So I want to know how to load the data in class.
In addition, I thought sending data to heap section using new would make it accessible at any time I want regardless of whether it is private or public, but it seems like it is not possible.
Can you explain how it works?
(I don't even fully understand how class work, so detailed explanation would be very appreciated. Thx!)
A private variable cannot be accessed by code outside the class definition. (There are exceptions with friend)
ptr->ID does not work because main is outside the class definition.
This can be fixed by using a getter method.
#include <iostream>
#include <vector>
using namespace std;
class Test
{
private:
int _ID;
string _name;
public:
int ID() {return _ID;}
string name() {return _name;}
Test(int param_ID, string param_name);
};
Test::Test(int param_ID, string param_name)
{
_ID = param_ID;
_name = param_name;
}
int main()
{
vector<Test *> test_vect;
Test *ptr = new Test(100, "John");
test_vect.push_back(ptr);
cout << ptr->ID() << endl;
return 0;
}
The above example shows the getter methods ID() and name() which return the data members _ID and _name respectively.
ID() is allowed to access _ID because ID() is part of the class definition. name() is allowed to access _name because name() is part of the class definition.
Note: I would still consider this code to be flawed because it creates a new object on the heap, but does not delete it. You should also look up the keywords new and delete to see how they operate together.
I would like to use a string before .getName()
Enemy Troll(Troll, 250, 30);
string enemyName;
enemyName = Troll;
enemyName.getName(); //this is causing the error... "No member named 'setHP' in 'std::__1::basic_string<char>'"
I want to be able to get the same results as using Troll.getName(); but instead use a string.
There is no built in way in C++ to use a string to look up a variable of the same name in C++.
What you have to do is create the appropriate data structure for yourself. One way to do that would be to use a map.
#include <map>
std::map<std::string, Enemy> my_map;
...
Enemy trump("Trump", 250, 30);
my_map["Trump"] = trump;
...
std::string name = ...;
Enemy some_enemy = my_map[name];
You can store all the instances by name as a static data member in a map for example. But there should be better ways to do it depending on the complete design of your program. But since you didn't provide all the context I'm just going to show a generic example. With some issues like non thread safety, missing null checks, not dealing with duplicates, etc.
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Enemy{
public:
std::string m_name;
int m_hp;
int m_dmg;
static std::map<std::string,Enemy*> s_instances;
Enemy(const std::string& name, int hp, int dmg)
: m_name(name),
m_hp(hp),
m_dmg(dmg)
{
s_instances[name] = this;
}
~Enemy()
{
s_instances.erase(m_name);
}
const std::string& getName() const
{
return m_name;
}
static Enemy* getInstanceByName(const std::string& name)
{
Enemy* result = nullptr;
auto iter = s_instances.find(name);
if(iter!=s_instances.end()) result = iter->second;
return result;
}
};
std::map<std::string,Enemy*> Enemy::s_instances;
void doStuff()
{
Enemy* instance = Enemy::getInstanceByName("Ork");
std::cout << instance->getName();
}
int main() {
Enemy ork("Ork",300,20);
doStuff();
return 0;
}
May someone please help me correct the following. I'm trying to wrap my head around how to create a pointer to an existing object within a new object. I can't get the syntax correct and keep getting errors.
Here's my code:
class Country
{
string name;
public:
Country (string name)
{ name = name;
cout << "Country has been created" << endl;}
~Country()
{cout << "Country destroyed \n";}
};
class Person
{
//string name;
//Date dateOfBirth;
Country *pCountry;
public:
Person(Country *c):
pCountry(c)
{}
};
int main()
{
Country US("United States");
Person(&US)
return 0;
}
Did you forget in your main.cpp?
#include <string>
#include <iostream>
using namespace std;
you also need a semicolon in your main:
int main()
{
Country US("United States");
Person person(&US); // < -- variable name and semicolon missing
return 0;
}
You should also change:
Country (string name)
{
this->name = name; // <-- assign name to member variable
...
or better, use member initializer lists:
Country (string name) : name(name) // <-- assign name to member variable
{
...
And in general you should try to be consistent with the way you format your code.
I got two classes, one named Person that I checked is working (I can create objects of that class so the problem should not be here).
I then have another class called Family with composition from Person:
Family.h
#include "Person.h"
class Family
{
public:
Family();
void printFamily();
private:
Person dad_();
Person mum_();
Person son_();
Person daughter_();
};
Family.cpp
#include "Family.h"
Family::Family()
{
}
void printFamily()
{
dad_.printAll();
mum_.printAll();
son_.printAll();
daughter_.printAll();
//printAll() is a function in the Person class that worked when
//I tested it earlier with only the person class
}
But when i try to compile this I get an error:
left of '.printAll' must have class/struct/union
'son_' : undeclared identifier
This error goes for all the .printAll() calls in family.cpp.
I can't see why this wouldn't work, so I hope you can.
Edit1:
Ok i changed
void printFamily()
to
void Family::printFamily()
That removes one error, but i still get
left of '.printAll' must have class/struct/union
Edit2
Ah my bad with the Person calls i changed them to
Person dad_;
and the same with the rest.
Seems like their might be an error with my Person class so i will post that also
Person.h
#include <string>
using namespace std;
class Person
{
public:
Person( const string & = "000000-0000", const string & = "N", const string & = "",const string & = "N");
~Person();
void setFirstName(const string &);
void setMiddleName(const string &);
void setLastName(const string &);
void getData(string &,string &,string &,string &);
static int getNumberOfPersons();
void printPartially() const;
void printAll() const;
bool checkForSameName(const Person &);
private:
string firstName_;
string middleName_;
string lastName_;
string socialSecNumber_;
static int numberOfPersons_;
};
Person.cpp
#include "Person.h"
#include <iostream>
int Person::numberOfPersons_ = 0;
Person::Person( const string &sNumber, const string &firstName, const string &middleName,const string &lastName )
:firstName_(firstName),middleName_(middleName),lastName_(lastName),socialSecNumber_(sNumber)
{
numberOfPersons_ ++;
}
Person::~Person()
{
numberOfPersons_--;
}
void Person::setFirstName(const string &firstName)
{ firstName_ = firstName; }
void Person::setMiddleName(const string &middleName)
{ middleName_ = middleName; }
void Person::setLastName(const string &lastName)
{lastName_ = lastName;}
void Person::getData(string &fName,string &mName,string &lName,string &sNumber)
{
fName = firstName_;
mName = middleName_;
lName = lastName_;
sNumber = socialSecNumber_;
}
int Person::getNumberOfPersons()
{
return numberOfPersons_;
}
void Person::printPartially() const
{
cout <<"Navn: "<<firstName_<<" "<<middleName_<<" "<<lastName_<<endl;
cout <<"Født: ";
for (int i = 0;i<6;i++)
{
cout <<socialSecNumber_.at(i);
}
}
void Person::printAll() const
{
cout <<"Navn: "<<firstName_<<" "<<middleName_<<" "<<lastName_<<endl;
cout <<"Personnr: "<<socialSecNumber_<<endl;
}
bool Person::checkForSameName(const Person &p)
{
if (p.firstName_ == firstName_ && p.middleName_ ==middleName_ && p.lastName_ == lastName_)
return true;
else
return false;
}
Now i am getting some new errors:
error C2011: 'Person' : 'class' type redefinition
see declaration of 'Person'
'Family::dad_' uses undefined class 'Person'
The "dad" error applies to the whole family
You have a few syntax issues.
First, you're declaring each of what are supposed to be member variables as functions which return Person. They should look like (note, no parens):
Person dad_;
Person mum_;
Person son_;
Person daughter_;
You're also missing the scoping on your definition of printFamily:
void Family::printFamily() {
...
}
Without the preceding Family::, C++ thinks you're defining a free function, and doesn't know to look inside the Family class for the declarations of dad_, mum_, etc.
Additionally, at least with the code you've shown, there's no way to initialize the people in your class. The Family constructor should take arguments to define the people, or you should have setters which allow defining them later. Right now, you'll get 4 identical people, set up however the default person constructor builds them.
I would normally prefer the constructor method, but I have other design reservations about your code to begin with (e.g. Does a family always contain mum, dad, brother, sister?) and that's not really what this question is about.
The line:
Person dad_();
says that dad_ is a function that returns a Person, not an object. Did you mean that? Similarly for others.
Try
Person dad_;
Family.h
#include "Person.h"
class Family
{
public:
Family();
void printFamily();
private:
Person dad_;
Person mum_;
Person son_;
Person daughter_;
};
Family.cpp
#include "Family.h"
Family::Family()
{
}
void Family::printFamily()
{
dad_.printAll();
mum_.printAll();
son_.printAll();
daughter_.printAll();
//printAll() is a function in the Person class that worked when
//I tested it earlier with only the person class
}
The out of line definition of a member function needs to include the class name:
void Family::printFamily()
{
//...
Surprisingly, you already got this right for the constructor but then immediately forgot...
Second, your private class members are functions, not data members (which is odd), but if that's deliberate, you need to call them:
dad_().printAll();
// ^^^