Segment fault upon calling method of class - c++

I was able to safely call builder, but builder2 exits with a segment fault.
The compiler does not output any warnings.
I would like to know the cause of the segment fault.
This code is a builder pattern to compose html. ul and li are collected with emplace_back and finally str() is called to build the parts and return them as string.
#include <memory>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct HtmlBuilder;
struct HtmlElement {
string name;
string text;
vector<HtmlElement> elements;
const size_t indent_size = 2;
HtmlElement() {}
HtmlElement(const string& name, const string& text)
: name(name), text(text) {}
string str(int indent = 0) const {
ostringstream oss;
string i(indent_size * indent, ' ');
oss << i << "<" << name << ">" << endl;
if (text.size() > 0)
oss << string(indent_size * (indent + 1), ' ') << text << endl;
for (const auto& e : elements) oss << e.str(indent + 1);
oss << i << "</" << name << ">" << endl;
return oss.str();
}
static unique_ptr<HtmlBuilder> build(string root_name) {
return make_unique<HtmlBuilder>(root_name);
}
};
struct HtmlBuilder {
HtmlBuilder(string root_name) { root.name = root_name; }
// void to start with
HtmlBuilder& add_child(string child_name, string child_text) {
HtmlElement e{child_name, child_text};
root.elements.emplace_back(e);
return *this;
}
// pointer based
HtmlBuilder* add_child_2(string child_name, string child_text) {
HtmlElement e{child_name, child_text};
root.elements.emplace_back(e);
return this;
}
string str() { return root.str(); }
operator HtmlElement() const { return root; }
HtmlElement root;
};
int main() {
// easier
HtmlBuilder builder{"ul"};
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto* builder2 = HtmlElement::build("ul")
->add_child_2("li", "hello")
->add_child_2("li", "world");
cout << (*builder2).str() << endl;
return 0;
}
The output results are as follows
hello
world
Segmentation fault (core dumped)

You dynamically create a std::unique_ptr object holding the builder with HtmlElement::build("ul"). This std::unique_ptr object gets destroyed at the end of the full expression which means the object builder2 points to is destroyed and dereferencing it is undefined behaviour resulting in the crash you observed.
I recommend not returning a dynamically allocated builder object at all. Instead move the definition of the HtmlElement::build below the definition of HtmlBuilder. You may also want to consider allowing for move semantics to avoid creating unnecessary copies of the objects:
struct HtmlElement {
...
static HtmlBuilder build(string root_name);
};
struct HtmlBuilder {
HtmlBuilder(string root_name) { root.name = std::move(root_name); }
// void to start with
HtmlBuilder& add_child(string child_name, string child_text) &
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return *this;
}
HtmlBuilder&& add_child(string child_name, string child_text) &&
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return std::move(*this);
}
string str() { return root.str(); }
operator HtmlElement() &&
{
return std::move(root);
}
HtmlElement root;
};
inline HtmlBuilder HtmlElement::build(string root_name) {
return { root_name };
}
int main() {
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto builder2 = HtmlElement::build("ul")
.add_child("li", "hello")
.add_child("li", "world");
cout << builder2.str() << endl;
HtmlElement product = std::move(builder2); // use move constructor for creating product here (works without std::move if HtmlElement::build is in the same full expression as the conversion to HtmlElement)
return 0;
}

As far as I understand, you return a unique_ptr with the build method. However, in your main function when you do this:
auto* builder2 = HtmlElement::build ...
What you actually do is retrieve the raw pointer from the unique_ptr that is returned and then you let this temporary unique_ptr be destroyed. Which in turn frees the instance that was handled by the unique_ptr, which is the very same instance that you retrieved the raw pointer for.
So in the next line:
cout << (*builder2).str() << endl;
You are actually trying to dereference a pointer to invalid memory, since the instance that resided there was just deleted by the temoporary unique pointer that was destroyed in the previous line.
If you remove the raw pointer from the "auto" part like this:
auto builder2 = HtmlElement::build("ul") ...
Then you will have a smart pointer to your instance.
Then you can call the str method on the smart pointer as if it was a pointer to your instance:
cout << builder2->str() << endl;

Related

The delete keyword is not working even though when I build the code it say there are not errors

#include <iostream>
#include <cstring>
#include <string>
class Cd {
private:
const char* performers;
const char* label;
int selections;
double playtime;
public:
Cd(){
int eight = 8;
performers = new char[eight];
label = new char[eight];
label = "Unknown";
performers = "Unknown";
selections = 0;
playtime = 0.0;
}
Cd(const char* performers, const char* label, int selections, double playtime) {
this->performers = new char[strlen(performers)+1];
this->performers = performers;
this->label = new char[strlen(label) + 1];
this->label = label;
this->selections = selections;
this->playtime = playtime;
}
void setLabel(const char* label) {
this->label = label;
}
void setPerformers(const char* performers) {
this->performers = performers;
}
void setSelections(int selections) {
this->selections = selections;
}
void setPlaytime(double playtime) {
this->playtime = playtime;
}
const char* getLabel() {
return label;
}
const char* getPerformers() {
return performers;
}
int getSelections() {
return selections;
}
double getPlaytime() {
return playtime;
}
virtual void Report() {
std::cout << "Performers: " << performers<<std::endl
<<"Label: " <<label<< std::endl
<<"Number of selections: " << selections << std::endl
<<"Play time: " << playtime << std::endl;
}
~Cd() {
delete[] performers;
delete[] label;
}
};
class Classic : public Cd {
private:
const char* primaryWork;
public:
Classic() {
primaryWork = new char[8];
primaryWork = "Unknown";
}
Classic(const char* primaryWork, const char* performers, const char* label, int selections, double playtime) {
this->primaryWork = new char[strlen(primaryWork) + 1];
this->primaryWork = primaryWork;
setLabel(label);
setPerformers(performers);
setSelections(selections);
setPlaytime(playtime);
}
virtual void Report() {
std::cout << "Primary work: " << primaryWork << std::endl<<
"Performers: " << getPerformers() << std::endl <<
"Label: " <<getLabel() << std::endl<<
"Number of selections: " << getSelections() << std::endl
<< "Play time: " << getPlaytime() << std::endl;
}
~Classic() {
delete[] primaryWork;
};
};
int main()
{
Cd c1("Beatles", "Capitol", 14, 35.5);
Classic c2 = Classic("Piano Sonata in B flat, Fantasia in C", "Alfred Brendel", "Philips" , 2, 57.17);
Cd* parent = &c1;
std::cout << "\nUsing object directly:\n";
std::cout << "***************************" << std::endl;
c1.Report();
c2.Report();
std::cout << "\nUsing type cd * pointer to objects:\n";
std::cout << "***************************" << std::endl;
// Call Report() using Cd type pointer created above
parent->Report();
Classic* classic = &c2;
classic->Report();
// Call Report() using Cd type pointer containing Classic object address
return 0;
}
I don't understand what is wrong with the delete keyword. My code is supposed to delete the memory allocation for the array at the end to save memory, but it is not working. The delete_scalar file pops up and the code does not finish executing. The rest of the code is working fine. The big problem is when I build the code the compiler tells me that there are no errors found.
Write
std::string performers;
instead of
const char* performers;
and so on for the other string-like members. Then you can drop the new[] and delete[] entirely. The bug you have is a reassignment of the pointer to some read-only const char[] type "Unknown" which compiles due to the rules of pointer decay.
Using bare pointers for class members also means you need to write constructors, a destructor, and an assignment operator correctly. If you use std::string then you can rely on the compiler-generated ones.

Parameter forwarding without temporary instances

I have a templated class which wraps a std::vector called mObjects.
Class has an insert function which forwards parameters of the actually stored type in the MyArray instance (its called variadic template arguments I think). In this example, I store MyMesh type, but it can be any kind of type.
As you can see in the main() function, mObjects vector doesn't grow, its elements get overwritten over and over again.
Think about an object-pool kind of data structure.
Everything works as expected.
template <class T>
class MyArray
{
public:
MyArray(const int capacity) :
mObjects(capacity)
{
mNextIndex = 0;
}
template <typename... Args>
void insert(Args&&... args)
{
mObjects[mNextIndex] = T{ std::forward<Args>(args)... }; //PROBLEMATIC ASSIGNMENT
//... not relevant code
}
private:
int mNextIndex;
std::vector<T> mObjects;
};
int main()
{
MyArray<Mesh> sa(2);
sa.insert("foo",1111); //goes to mObjects[0]
sa.insert("bar",2222); //goes to mObjects[1], and tada, the vector is full
sa.remove(1111); //not implemented above, but not relevant. Remove func basically adjusts mNextIndex, so mObjects[0] will be overwritten upon next insert.
sa.insert("xxx",3333); //mObjects[0] gets overwritten from "foo" to "xxx" and 1111 to 3333
}
My problem is with one row above commented as //PROBLEMATIC ASSIGNMENT.
mObjects[mNextIndex] = T{ std::forward<Args>(args)... };
When that command executes 3 things happen:
MyMesh(const string s, int x) constructor is called, meaning an entire MyMesh gets allocated on stack here. Why? I just want to pass the forwarded arguments to an existing mObjects[mNextIndex] element.
operator=(MyMesh&& other) is called, and does assignment variable by variable between the temporary variable and mObjects[mNextIndex].
~cVMesh() is called meaning the temporary variable deallocates and dies.
I would like to get rid of #1 and #3. So don't want the "expensive" temporary object creation. I just wish to forward/assign the incoming MyMesh parameters to mObjects[mNextIndex]. Similarly like what std::vector.emplace_back() does, but to any location pointed by mNextIndex.
How can I forward only the parameters to an existing variable in C++, without instantiate temporary variables?
For completness, here is the MyMesh class which gets stored in the MyArray class. Nothing special just printing out some message, when constructor/destructor/assignement operator is called:
class Mesh
{
public:
Mesh()
{
cout << "Mesh()" << std::endl;
mStr = "";
mId = 99999999;
}
Mesh(const string s, int x)
{
cout << "Mesh(const string s, int x)" << std::endl;
mStr = s;
mId = x;
}
~Mesh()
{
cout << "~Mesh()" << std::endl;
}
Mesh& operator=(const Mesh& other)
{
cout << "operator=(const Mesh& other)" << std::endl;
cout << mStr << " becomes " << other.mStr << endl;
cout << mId << " becomes " << other.mId << endl;
mStr = other.mStr;
mId = other.mId;
return *this;
}
Mesh& operator=(Mesh&& other) noexcept
{
cout << "operator=(Mesh&& other)" << std::endl;
cout << mStr << " becomes " << other.mStr << endl;
cout << mId << " becomes " << other.mId << endl;
mStr = other.mStr;
mId = other.mId;
return *this;
}
Mesh(const Mesh& other)
{
cout << "Mesh(const Mesh& other)" << std::endl;
mStr = other.mStr;
mId= other.mId;
}
Mesh(Mesh&& other) noexcept
{
cout << "Mesh(Mesh&& other)" << std::endl;
mStr = other.mStr;
mId = other.mId;
other.mStr = "";
other.mId = 99999999;
}
string mStr;
int mId;
};
I think what you want is to reconstruct an arbitary element in the vector with new values
#include<vector>
template<class T, class... Args>
void create_at_nth_place(std::vector<T>& v, int n, Args&&...args){
auto& elem = v[n];
elem.~T();
new(&elem) T(std::forward<Args>(args)...);
}
struct S {
S();
S(int, bool);
template<class... Args>
S(Args&&...);
S(S&&) noexcept;
~S();
};
void f() {
std::vector<S> v(3);
create_at_nth_place(v, 2, 4323, false);
char a = 'a';
create_at_nth_place(v, 2, 'a', 123, 1232, 32.f, a);
}
Link: https://godbolt.org/g/3K9akZ
mObjects[mNextIndex] = T{ std::forward<Args>(args)... }; line creates a temporary object, performs a move(copy) assignment to object already stored in vector at specified position and finally destroys a temporary.
The whole MyArray class is rather useless since vector already has similar functionality.
vector<Mesh> sa;
sa.reserve(2);
sa.emplace_back("foo",1111); // Mesh constructor called once
sa.emplace_back("bar",2222); // Mesh constructor called once again
You might add:
void assign(const string& s, int x)
{
cout << "assign(const string s, int x)" << std::endl;
mStr = s;
mId = x;
}
And use it:
mObjects[mNextIndex].assign(std::forward<Args>(args)...);
If your mesh class is very heavy, you should consider having an array of pointers, this would eliminate spurious copies altogether. Wouldn't MyArray<std::shared_ptr<MyMesh>> work as is?

Setting a variable in a child class [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am currently working on a very basic assembler. The assembler needs to take in assembly instructions and output 16-bit binary instructions for use with a computer we are making.
My design strategy has been to create a Command class, that has 3 child classes. There is one for each type of command: A-commands, C-commands, and L-commands. To identify the type of command I am working with, I have included a string command_type that is either "A", "C", or "L" respectively.
EDIT:
I am still having a lot of trouble figuring out how to properly derive these classes. Basically, A and L commands should have a "symbol" string, which represents an integer value that needs to be converted, while C commands have "dest","comp", and "jump" values that also must be accessed, however they do not have "symbol" values.
Command.h
#include <fstream>
#include <string>
class Command {
std::string command_type = "";
protected:
void set_commandType(std::string x){command_type = x;}
public:
Command();
virtual ~Command();
std::string commandType() const {return command_type;}
};
class A_COMMAND : public Command
{
std::string symbol;
public:
A_COMMAND(std::string s);
std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command #Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.
};
class C_COMMAND : public Command
{
std::string comp;
std::string dest;
std::string jump;
public:
C_COMMAND(std::string s, std::string d, std::string j);
std::string get_comp(){return comp;}; //Returns the comp mnemonic in the current C-command (28 possibilities). Should be called only when commandType() is C_COMMAND.
std::string get_dest(){return dest;}; //Returns the dest mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
std::string get_jump(){return jump;}; //Returns the jump mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
};
class L_COMMAND : public Command
{
std::string symbol;
public:
L_COMMAND(std::string s);
std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command #Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.
};
Command.cpp
#include "Command.h"
//---------------------------------------------
//A-Command functions
Command::Command(){}
A_COMMAND::A_COMMAND(std::string s) : symbol(s)
{
set_commandType("A");
}
//---------------------------------------------
//C-Command functions
C_COMMAND::C_COMMAND(std::string c, std::string d, std::string j) : comp(c), dest(d), jump(j)
{
set_commandType("C");
}
//---------------------------------------------
//L-Command functions
L_COMMAND::L_COMMAND(std::string s) : symbol(s)
{
set_commandType("L");
}
I have a Parser.cpp and Parser.h that process the input and are responsible for creating a deque of commands:
Parser.h
#include "Command.h"
#include <vector>
#include <deque>
class Parser {
private:
std::deque<Command> commands;
public:
Parser(std::vector<std::string>);
bool hasMoreCommands() //are there more commands in the input?
{
if(commands.size() != 0)
return true;
else
return false;
}
void advance(){commands.pop_front();} //move to next command, should only work if hasMoreCommands returns false}
Command currentCommand(){return commands.front();}
std::vector<std::string> translateCommands(); //convert commands into binary strings
};
Parser.cpp
#include "Parser.h"
#include "Command.h"
#include <vector>
#include <iostream>
#include <string>
#include <unordered_map>
bool inList(std::string& str, std::vector<std::string> list) //check if a given string contains one of the elements in the comp, dest, jump vectors. if so extract string for use in constructor
{
for(auto i = list.begin(); i!=list.end(); ++i)
{
std::size_t found = str.find(*i);
if(found!=std::string::npos)
{
return true;
}
}
return false;
}
Parser::Parser(std::vector<std::string> input) {
std::vector<std::string> dest_list = {"","M","D","MD","A","AM","AD","AMD"}; //all possible dests
std::vector<std::string> comp_list = {"0","1","D","A","!D","!A","-D","-A","D+1","A+1","D-1","A-1","D+A","D-A","A-D","D&A","D|A","M","!M","-M","M+1","M-1","D+M","D-M","M-D","D&M","D|M"}; //all possible comps
std::vector<std::string> jump_list = {"","JGT","JEQ","JGE","JLT","JNE","JLE","JMP"}; //all possible jumps
std::string dest, comp, jump;
std::deque<Command> commands;
for(std::vector<std::string>::const_iterator i = input.begin(); i != input.end(); ++i)
{
std::string line = *i;
if(*line.begin()=='#') //A-command
{
A_COMMAND command(line.substr(1));
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "symbol: " << command.get_symbol() << "\n";
commands.push_back(command);
}
else if(*line.begin()=='(' && *line.rbegin() == ')' && line.size() > 2) //L-command
{
L_COMMAND command(line.substr(1, line.size() - 2));
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "symbol: " << command.get_symbol() << "\n";
commands.push_back(command); }
else
{
std::string rhs = line;
std::string dest_string = "";
std::string comp_string = "";
std::string jump_string = "";
size_t equals_pos = line.find('='); //position of = in string, if present
size_t semi_pos = line.find(';'); //position of ; in string, if present
if(equals_pos != line.npos) //if there is an = then we have a dest
{
dest_string = line.substr(0,equals_pos);
rhs = line.substr(equals_pos+1);
}
if(semi_pos != line.npos) //jump
{
comp_string = rhs.substr(0,semi_pos);
jump_string = rhs.substr(semi_pos+1);
}
else //no jump
{
comp_string = rhs;
}
//now confirm if inputs are valid
if(inList(dest_string, dest_list))
dest = dest_string;
else
std::cout << "invalid dest \n";
if(inList(comp_string, comp_list))
comp = comp_string;
else
std::cout << "invalid comp \n";
if(inList(jump_string, jump_list))
jump = jump_string;
else
std::cout << "invalid jump \n";
C_COMMAND command(comp, dest, jump);
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "dest: " << command.get_dest() << "\n";
std::cout << "comp: " << command.get_comp() << "\n";
std::cout << "jump: " << command.get_jump() << "\n";
commands.push_back(command);
}
}
}
My main.cpp loads the input, and passes it through the parser. The problem I have is that I cannot do anything with the input.
I have tried to write a function like so:
string translateLine(Command command, Code code) //Code is a table for translating the command
{
string output;
if(command.commandType() == "A")
{
string symbol = parser.currentCommand().get_symbol();
cout << symbol << endl;
//perform binary conversion
}
/*else if(command.commandType() == "C")
{
string dest = command.get_dest();
}*/
//shouldn't be any L commands in symbol-less version
else
{
std::cout << "unexpected command value \n";
}
return output;
}
But as soon as I call get_symbol(), the compiler doesn't recognize the function. I know that this is because the base Command doesn't have a get_symbol() function, but I can't figure out how to correctly add the functions to the base class and derive them to the lower 3. I can't just make the pure virtual because not all of the functions are used in each class. How can I correctly accomplish this?
First, if translateLine() should be able to accept A_COMMAND, C_COMMAND, or L_COMMAND objects, then it needs to take a Command* parameter, not a Command parameter. A pointer to a base class can hold a pointer to a class derived from that base, but an object of the base class cannot hold a derived object.
Second, you cannot call a function that belongs to A_COMMAND even with a Command pointer that is really pointing to a A_COMMAND object without doing a dynamic_cast. A dynamic_cast can convert a pointer from Command* to A_COMMAND at run-time and will return NULL if the object pointed to is not really an A_COMMAND object.
The basic problem is that you're reinventing Run-Time Type information. You don't need to add "command_type=A" to class A_COMMAND. C++ already knows what types your objects have, if there's at least one virtual method. And it looks like your class needs a virtual destructor.

Pointer Function return value of Struct in Class [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 7 years ago.
I have been attempting to create a function getLocation() that utilizes a pointer to return the value of the struct Location declared in the Character class. I was curious as to the problem with my syntax (or my structure). Knowing that the asterisk * should refer to the value, why is it that my function using an ampersand string& Character::getInventory is able to return the value of that particular index (its return does not need to be converted)?
Trying Location& Character::getLocation() {return position; }
when run results in error C2679: binary '<<': no operator found
Nor
Location*
Which cannot be run as there is no conversion.
I read that the following is likely the most proper because it specifies the scope in which the structure resides, but still results in needing and returning a temporary.
Character::Location* const & Character::getLocation() {return &position; }
Any advice or input would be greatly appreciated, thanks in advance.
Below is my main.cpp, which of course will show the hexadecimal address for Location.
#include <iostream>
#include <string>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::string;
class Character {
private:
string name;
string inventory[4];
public:
struct Location {
int x; int y;
};
Location position;
public:
void Character::setName(string x) { name = x; }
string Character::getName() { return name; }
void Character::setLocation(int x, int y) {
position.x = x; position.y = y;
}
Location* Character::getLocation() {return &position; }
void Character::setInventory(string(&x)[4]) { for (int i = 0; i < 4; ++i) { inventory[i] = x[i]; } }
string& Character::getInventory(int itemNumber) { return inventory[itemNumber]; }
};
void showUser(Character Character);
int main() {
try {
string items[4] = { "Sword", "Shield", "Potion", "Cloak" };
Character CharacterI;
CharacterI.setName("Some Character");
CharacterI.setInventory(items);
CharacterI.setLocation(1, 30);
cout << "\n" << "Retrieving Character Info..." << "\n" << endl;
showUser(CharacterI);
}
catch (std::exception & e) {
cerr << "\nError : " << e.what() << '\n';
}
system("pause");
return 0;
}
void showUser(Character character) {
cout << "Name : " << character.getName() << endl;
cout << "Location : " << character.getLocation() << endl;
for (int i = 0; i < 4; ++i) {
cout << "Inventory " << i + 1 << " : " << character.getInventory(i) << endl;
}
}
Ok, I think I understand the question better now. The reason why getInventory can successfully return a reference while getLocation does not is because getLocation returns a reference to a temporary variable, which is not good. See the link in #NathanOliver's comment for details. Additionally, to paraphrase a previous comment by #Peter Schneider, an * in an expression dereferences a pointer to return a value, while in a declaration it signifies that a variable will be of pointer type. The two usages are more or less opposites of each other. Example:
int* p = new int; //Declares a pointer to int
int x = *p; //Dereferences a pointer and returns an int
What you need to do is create a member variable to hold the Character's location, then set/get from that variable instead of creating temporaries. You did this already for name and inventory, just keep using that same pattern.
Additionally, whenever you use the Location struct outside of the Character class scope, you need to fully-qualify it with Character::Location.
Example:
#include <iostream>
using namespace std;
class Character {
public:
struct Location {
int x;
int y;
};
Location loc;
void SetLocation(int x, int y) {loc.x = x; loc.y = y;}
Location& GetLocation() {return loc;}
};
int main ()
{
Character c;
c.SetLocation(1,42);
Character::Location l = c.GetLocation();
cout << l.x << endl << l.y << endl;
return 0;
}
Output:
1
42

C++ string member variable not present in vector

I am creating a vector that contains pointers to a base class. In this vector I'm dynamically storing pointers to derived classes which contain some member variables, one of them being a string variable name.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
bool hasDirection = false;
bool hasDiameter = false;
int direction;
float diameter;
int starDimension = 0;
int animalDimension = 0;
int fishDimension = 0;
class MovingObject
{
protected:
std::string name;
int direction;
float diameter;
int dimension;
float movingSpeed;
public:
std::string getName(){ return name;};
int getDirection(){ return direction;};
float getDiameter(){ return diameter;};
float getMovingSpeed(){ return movingSpeed;};
int getDimension(){ return dimension;};
void setName(std::string v){ name = v;};
void setDirection(int d){ direction = d;};
void setDiameter(float f){ diameter = f;};
void setMovingSpeed(float s){ movingSpeed = s;};
void setDimension (int d){ dimension = d;};
virtual void PrintContents()=0;
};
static std::vector<MovingObject*> data;
class starObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ")";
}
};
class animalObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ")";
}
};
class fishObject : public MovingObject
{
public:
void PrintContents()
{
std::cout << "(" << getName() << "," << getDiameter() << "," << getDirection() << ", [" << getDimension() << "], " << getMovingSpeed() << ")";
}
};
I later set all these member variables inside a main function. The problem is when I try to output the contents of the member variables, all of them show up except for the string name.
Now, I've checked to make sure that the string gets set before calling the PrintContent() method, and it shows that the value is in the vector. However, when I debug through the code, the value is no longer there, instead containing an empty string.
Could someone with better c++ knowledge explain to me why this is happening? This is the main class:
int main()
{
std::string type;
Reader reader;
while (!std::cin.eof())
{
try
{
std::string type;
std::cin >> type;
if (type =="int")
{
reader.ReadDirection();
}
else if (type =="float")
{
reader.ReadDiameter();
}
else if (type == "string")
{
std::string name;
std::cin >> name;
if (hasDirection && hasDiameter)
{
int dimension;
if (diameter > 0 && diameter < 10)
{
//fish
fishObject fish;
fish.setName(name);
fish.setDiameter(diameter);
fish.setDirection(direction);
dimension = fishDimension;
fishDimension += 50;
fish.setDimension(dimension);
fish.setMovingSpeed(0.1);
data.push_back(&fish);
}
else if (diameter >= 10 < 500)
{
//animal
animalObject animal;
animal.setName(name);
animal.setDiameter(diameter);
animal.setDirection(direction);
dimension = animalDimension;
animalDimension += 800;
animal.setDimension(dimension);
animal.setMovingSpeed(5.0);
data.push_back(&animal);
}
else if (diameter >=500)
{
//star
starObject star;
star.setName(name);
star.setDiameter(diameter);
star.setDirection(direction);
dimension = starDimension;
starDimension += 5000;
star.setDimension(dimension);
star.setMovingSpeed(30.0);
data.push_back(&star);
}
}
else
{
throw (IncompleteData(name));
}
}
}
catch (IncompleteData e)
{
std::cerr << "No diameter or direction given for object " << e.objectName << "\n";
}
}
The objects you push to the data vector are local because they are declared inside if/else blocks (see the declarations of fish and animal).
When you push the address of such an object to the vector, it will continue to point to the local object, which ceases to exist at the end of the local scope. You need to create objects that live beyond the local scope. One way of doing this is to create copies of the local objects on the heap and push those to the vector:
data.push_back(new fishObject(fish));
Of course this means that you get a memory leak unless you make sure you explicitly delete the elements of the vector some time before the end of the program. The usual recommendation to avoid having to think of this is to use a vector of std::unique_ptr<MovingObject> instead of a vector of naked pointers.