I am trying to make a basic text editor and I'm having trouble loading the data in the struct using push_back.
Here is the struct
struct LineInfo
{
int lineNumber;
string text;
};
Here is the main program
vector<LineInfo> globalDocument;
int main()
{
cout << "COSC 120 line editing system now running." << endl << endl;
bool done = false;
while ( !done )
{
cout << "> ";
string inputBuffer;
getline( cin, inputBuffer );
int lineNumber;
splitLineNumberAndText( inputBuffer, lineNumber );
ptr.push_back( &lineNumber );
if ( lineNumber > 0 )
handleTextLine( inputBuffer, lineNumber );
else
done = handleSystemCommand( inputBuffer );
}
cout << endl << "COSC 120 line editing system has shut down. Bye." << endl;
return 0;
}
This receives a line number and text from input then splits it up into an int and a string and sends it to handleTextLine
Here is the function
void handleTextLine( const string& s, int lineNumber2 )
{
globalDocument.lineNumber.push_back(lineNumber2);
globalDocument.text.push_back(s);
}
vector<LineInfo> globalDocument is a vector of LineInfo structs, so you must push_back LineInfo objects into it:
void handleTextLine( const string& s, int lineNumber2) {
LineInfo li;
li.lineNumber = lineNumber2;
li.text = s;
globalDocument.push_back(li);
}
Or, using struct initialization syntax:
void handleTextLine( const string& s, int lineNumber2) {
LineInfo li = {lineNumber2, s};
globalDocument.push_back(li);
}
You can get even more compact insertion code if you declare a constructor for LineInfo:
struct LineInfo {
// Default constructor
LineInfo() : lineNumber(0) {}
// Constructor taking 2 arguments
LineInfo(int n, string s) : lineNumber(n), text(s) {}
int lineNumber;
string text;
};
void handleTextLine( const string& s, int lineNumber2) {
globalDocument.push_back(LineInfo(lineNumber2, s));
}
Related
I am looking for an easy way to save and load this C++ object to and from a binary file.
#include <iostream>
#include <vector>
class User
{
private:
std::string _name;
int _age;
std::vector<std::string> _hobbies;
public:
std::string Name() { return _name; }
int Age() { return _age; }
std::vector<std::string> Hobbies() { return _hobbies; }
void Hobbies(std::string hobbieName)
{
_hobbies.push_back(hobbieName);
}
User(std::string name, int age)
{
_name = name;
_age = age;
}
};
int main()
{
User u1 = User("John", 48);
u1.Hobbies("Art");
u1.Hobbies("Lego");
u1.Hobbies("Blogging");
User u2 = User("Jane", 37);
u2.Hobbies("Walking");
u2.Hobbies("Chess");
u2.Hobbies("Card Games");
std::cout << u1.Name() << "'s Hobbies:\n";
for (std::string hobbie : u1.Hobbies())
{
std::cout << hobbie << "\n";
}
std::cout << u2.Name() << "'s Hobbies:\n";
for (std::string hobbie : u2.Hobbies())
{
std::cout << hobbie << "\n";
}
//Save 'u1' to file.
//Load 'u1' from file into a new 'User' object
}
I have looked at a number of StackOverflow answers for similar questions, but I am struggling to find a solution specifically regarding using the vector in my class. I would appreciate any suggestions that you might have.
Here is a very abstract idea of what you want, Write string size that helps to read string back for name, write age, again wite hobbies size that helps to read it again and finally write each string with it size. To read reverse the process,
#include <iostream>
#include <fstream>
#include <vector>
class User
{
private:
std::string _name;
int _age;
std::vector<std::string> _hobbies;
public:
std::string Name()
{
return _name;
}
int Age()
{
return _age;
}
std::vector<std::string> Hobbies()
{
return _hobbies;
}
void Hobbies(std::string hobbieName)
{
_hobbies.push_back(hobbieName);
}
User(std::string name, int age)
{
_name = name;
_age = age;
}
/**
* #brief saves to user binary file
*
* #param fout
*/
void save(std::ofstream &fout)
{
// write name
size_t nsiz = this->_name.size();
// write string size to read
fout.write((const char *)(&nsiz), sizeof(nsiz));
fout.write(&_name[0], nsiz);
// write age
fout.write((const char *)&_age, sizeof(int));
// write hobbies size
size_t hsiz = _hobbies.size();
fout.write((const char *)&hsiz, sizeof(size_t));
// write hobbies
for (auto &str : _hobbies)
{
size_t ssiz = str.size();
fout.write((const char *)(&ssiz), sizeof(ssiz));
fout.write(&str[0], str.size());
}
}
/**
* #brief loads from binary file
*
* #param fin
*/
void load(std::ifstream &fin)
{
// read name
size_t nsiz = 0;
fin.read((char*)&nsiz, sizeof(size_t));
this->_name.resize(nsiz);
fin.read(&_name[0], nsiz);
// read age
fin.read((char*)&_age, sizeof(int));
// read hobbies size
size_t hsiz = 0;
fin.read((char*)(&hsiz), sizeof(hsiz));
_hobbies.resize(hsiz);
for(size_t i = 0; i < hsiz; ++i)
{
size_t ssiz = 0;
fin.read((char*)&ssiz, sizeof(size_t));
_hobbies[i].resize(ssiz);
fin.read(&_hobbies[i][0], ssiz);
}
}
};
int main()
{
User u1 = User("John", 48);
u1.Hobbies("Art");
u1.Hobbies("Lego");
u1.Hobbies("Blogging");
std::ofstream fout("data.bin", std::ofstream::binary);
u1.save(fout);
fout.close();
User u2("", 0);
std::ifstream fin("data.bin", std::ifstream::binary);
u2.load(fin);
fin.close();
std::cout << u2.Name() << " with age " << u2.Age() << "'s Hobbies:\n";
for (std::string hobbie : u2.Hobbies())
{
std::cout << hobbie << "\n";
}
}
I hope that it helps, and make sure to do things what if string size is zero :)
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;
}
};
The way the code works is that there is a base pure virtual class called "Module" with subclasses of "Living", "Manufacturing" and "PowerGen" which uses some of the virtual functions. In the main driver file, I am successfully able to read a list of modules from a file and store them in a vector of Module pointers. The problem comes when I want to use the virtual display function to display that vectors element's module details. I am faced with a segmentation fault. Any help would be much appreciated!
Class Module
{
public:
Module() = default;
explicit Module(const string& purpose, const string& id)
{
this->purpose = purpose;
this->id = id;
}
explicit Module(const string& purpose) { this->purpose = purpose; }
virtual ~Module() { }
virtual void setCrew( ) = 0;
virtual void display( ) const = 0;
void addCrew(const string& name)
{
crew_list.push_back(Crew(name));
++number_of_crew;
}
void setPowerReq(double power) { this->power = power; }
void setMaxCrew(int max_crew) { this->max_crew = max_crew; }
string getPurpose( ) { return purpose; }
string getId( ) { return id; }
int getNumberOfCrew( ) { return number_of_crew; }
int getMaxCrew( ) { return max_crew; }
double getPower( ) { return power; }
friend ostream& operator << ( ostream& os, const Module& m );
friend ofstream& operator << ( ofstream& os, const Module& m );
friend ifstream& operator >> ( ifstream& is, Module& m );
private:
string purpose = "unknown";
string id = "unknown";
int number_of_crew = 0;
int max_crew = 0;
double power = 0;
vector<Crew> crew_list;
};
class Living : public Module
{
public :
Living( ) = default;
explicit Living(const string& purpose);
Living(int meals, const string& id);
void setCrew( )
{
setMaxCrew( floor(meals / 3) );
setPowerReq( meals * 1.4 );
}
void display( ) const
{
cout << *this;
}
friend ofstream& operator << ( ofstream& os, const Living& l );
friend ifstream& operator >> ( ifstream& is, Living& l );
private :
int meals = 0;
static string type;
};
class Manufacturing : public Module
{
public:
Manufacturing( ) = default;
Manufacturing( const string& product, int quantity, const string& id );
explicit Manufacturing( const string& purpose );
void setCrew( )
{
setPowerReq( quantity * 6 );
setMaxCrew( maxCrew );
}
void display( ) const
{
cout << *this;
}
friend ofstream& operator << ( ofstream& os, const Manufacturing& m );
friend ifstream& operator >> ( ifstream& is, Manufacturing& m );
private:
static string type;
string product = "unknown";
static int maxCrew;
int quantity = 0;
};
class PowerGen : public Module
{
public:
PowerGen() = default;
PowerGen( int number_of_generators, const string& id );
explicit PowerGen( const string& purpose );
void setCrew( )
{
setMaxCrew(0);
setPowerReq( -(number_of_generators * 7) );
}
void display( ) const
{
cout << *this;
}
friend ofstream& operator << ( ofstream& os, const PowerGen& p );
friend ifstream& operator >> ( ifstream& is, PowerGen& p );
private:
static string type;
const int max_generators = 8;
int number_of_generators = 0;
};
Portion relevant from the main driver file which when called causes the segmenation fault:
if( cityVector.size() > 0 )
{
cout << "\nHere is the complete City" << endl;
for ( auto iter : cityVector )
{
iter->display( );
cout << endl << endl;
}
} else {
cout << "\nThere are no Modules in the City" << endl;
}
This is how I loaded the data from a file, where VMOD = vector:
void load( VMOD & cityVector )
{
string filename;
cout << "Please enter a filename >> ";
getline( cin, filename );
ifstream fin;
fin.open(filename);
if (!fin.fail())
{
while ( !fin.eof() )
{
string purpose;
getline( fin, purpose );
if ( purpose == "Living" )
{
Living object(purpose);
loadObject( cityVector, object, fin );
} else if ( purpose == "Manufacturing" )
{
Manufacturing object(purpose);
loadObject( cityVector, object, fin );
} else if ( purpose == "Power Generation" )
{
PowerGen object(purpose);
loadObject( cityVector, object, fin );
} else {
}
}
cout << "The city has " << cityVector.size() << " modules!" << endl;
} else {
cout << "File does not exist... " << endl;
}
fin.close();
}
template<typename T>
void loadObject( VMOD & cityVector, T object, ifstream& fin )
{
fin >> object;
Module* mptr;
mptr = &object;
cityVector.push_back(mptr);
}
With the file looking like this:
Living
L1
5
21
2
First Person
Fourth Person
15
Manufacturing
M1
10
12
4
Second Person
Third Person
Fifth Person
Sixth Person
C++ ness
2
Power Generation
P1
0
-14
0
2
You have a dangling pointer problem.
template<typename T>
void loadObject( VMOD & cityVector, T object, ifstream& fin )
{
// object is a function local object
// since it is passed by value.
fin >> object;
Module* mptr;
// This is a pointer to a local object.
// It is invalid when the function returns.
mptr = &object;
// Storing a pointer that will be a dangling pointer as soon
// the function returns.
cityVector.push_back(mptr);
}
You need to use dynamically allocated memory for the restored objects and save them in cityVector.
You'll need to make sure that memory is deallocated once you are done using it.
template<typename T>
void loadObject(VMOD& cityVector, ifstream& fin )
{
T* objectPtr = new T;
fin >> *objectPtr;
cityVector.push_back(objectPtr);
}
and call it using the following syntax:
loadObject<LivingObject>(cityVector, fin);
For a project I'm doing we have to populate a queue with "House" objects. The "House" objects get their information from a file called "data.dat". Each line of the file is another thing that goes into the house object. So first I take a char* for the address, then an int, another int, a third int, and then another char*. We aren't aloud to use strings to get the char* variables which I believe is where I'm running into my problem. Every time I compile it tells me I have a segmentation fault. Here is the area of my queue.cpp that I'm pretty sure the error is in
#include"queue.h"
#include<iostream>
#include<fstream>
#include<istream>
Queue::Queue(const char *filename){
head = NULL;
tail = NULL;
std::ifstream infile(filename);
char * address = NULL;
int footage = 0;
int bedrooms = 0;
int bathrooms = 0;
char * features = NULL;
while(!infile.eof()){
while(infile.get() != '\n'){
//std::cout << infile.get();
infile.get(address[i]);
}
infile >> footage >> bedrooms >> bathrooms;
while(infile.get() != '\n'){
infile.get(features[i]);
}
enqueue(House(address, footage, bedrooms, bathrooms, features));
}
infile.close();
}
Here is the house object header file:
House();
House(char * ad, int fo, int be, int ba, char * fe);
char * getAddress();
int getFootage();
int getBedrooms();
int getBathrooms();
char * getFeatures();
void setAddress(char * ad);
void setFootage(int fo);
void setBedrooms(int be);
void setBathrooms(int ba);
void setFeatures(char * fe);
friend std::ostream& operator<<(std::ostream& out, House& house);
private:
char * address;
int footage;
int bedrooms;
int bathrooms;
char * features;
};
You need to initialize the features and address first, either by using new or by creating it as array of chars of some length.
The way you do it you're trying to write to the memory that was't assigned yet - hence buffer overflow.
For fun, here's a cleaned up version
mainly it replaces char* with std::string (since we're doing C++)
it makes the whole thing self-contained.
It uses correct input validation (don't use while (!infile.eof()), check extraction operators)
I didn't implement your Queue :)
Live On Coliru
#include <iostream>
#include <fstream>
#include <sstream>
struct House {
House()
: address(), footage(0), bedrooms(0), bathrooms(0), features()
{ }
House(std::string ad, int fo, int be, int ba, std::string fe)
: address(ad), footage(fo), bedrooms(be), bathrooms(ba), features(fe)
{ }
std::string getAddress() const { return address; }
int getFootage() const { return footage; }
int getBedrooms() const { return bedrooms; }
int getBathrooms() const { return bathrooms; }
std::string getFeatures() const { return features; }
void setAddress(std::string ad) { address = ad; }
void setFootage(int fo) { footage = fo; }
void setBedrooms(int be) { bedrooms = be; }
void setBathrooms(int ba) { bathrooms = ba; }
void setFeatures(std::string fe) { features = fe; }
friend std::ostream &operator<<(std::ostream &out, House const &house) {
return out << "Address: " << house.getAddress() << '\n'
<< "Footage: " << house.getFootage() << '\n'
<< "Bed rooms: " << house.getBedrooms() << '\n'
<< "Bath rooms: " << house.getBathrooms() << '\n'
<< "Features: " << house.getFeatures() << '\n';
}
private:
std::string address;
int footage;
int bedrooms;
int bathrooms;
std::string features;
};
struct Queue {
Queue(std::string filename);
struct Node {
House value;
Node* next;
};
Node *head, *tail;
void enqueue(House const& h) {
// TODO
std::cout << h << "\n";
}
};
Queue::Queue(std::string filename) : head(nullptr), tail(nullptr) {
std::ifstream infile(filename);
std::string address;
int footage = 0;
int bedrooms = 0;
int bathrooms = 0;
std::string features;
std::string line; // lines
while (getline(infile, address) && getline(infile, line)) {
std::istringstream iss(line);
if (iss >> footage >> bedrooms >> bathrooms && getline(iss, features)) {
enqueue(House(address, footage, bedrooms, bathrooms, features));
}
}
}
int main()
{
Queue q("data.dat");
}
For input:
Blv. Dreams Abroken 24, 78377d XG, ClassyCode
2 4 1 pool sauna porch indoor-parking
Oyi. Qernzf Noebxra 24, 78377q KT, PynfflPbqr
3 8 2 cbby fnhan cbepu vaqbbe-cnexvat
It prints the output:
Address: Blv. Dreams Abroken 24, 78377d XG, ClassyCode
Footage: 2
Bed rooms: 4
Bath rooms: 1
Features: pool sauna porch indoor-parking
Address: Oyi. Qernzf Noebxra 24, 78377q KT, PynfflPbqr
Footage: 3
Bed rooms: 8
Bath rooms: 2
Features: cbby fnhan cbepu vaqbbe-cnexvat
I made a program for binary heap given below-
#include<iostream>
using namespace std;
/**
* Construct the binary heap.
* capacity is the capacity of the binary heap.
*/
class BinaryHeap
{
private:
int currentSize; // Number of elements in heap
int array[]; // The heap array
void buildHeap( );
void percolateDown( int hole );
public:
bool isEmpty( ) const;
bool isFull( ) const;
int findmini( ) const;
void insert( int x );
void deleteMin( );
void deleteMin( int minItem );
void makeEmpty( );
public :
BinaryHeap( )
{
currentSize = 0;
}
BinaryHeap( int capacity )
{
array[capacity + 1];
currentSize = 0;
}
};
int main()
{
int resp, ch, choice;
int n, i;
cout << "enter the size of heap" << endl;
cin >> n;
BinaryHeap b(int n);
cout << "enter the item " << endl;
cin >> ch;
b.insert( int ch);
return 0;
}
while compiling it gives errors
request for member 'insert' in 'b', which is of non-class type 'BinaryHeap(int)'
and
expected primary-expression before 'int'
why is this happening and how could it be resolved?
Remove int from BinaryHeap b(int n); and b.insert( int ch); and you are good to go.
When you call a function you shouldn't specify the data type of the variables you call it with.
Try changing this
b.insert( int ch);
to this:
b.insert(ch);