I just came from Java and Python world to C++ world, and faced a problem while trying to get the value from a public const function of a class.
I have a class as follows:
class CMDPoint
{
public:
CMDPoint();
CMDPoint(int nDimensions);
virtual ~CMDPoint();
private:
int m_nDimensions; // the number of dimensions of a point
float* m_coordinate; // the coordinate of a point
public:
const int GetNDimensions() const { return m_nDimensions; }
const float GetCoordinate(int nth) const { return m_coordinate[nth]; }
void SetCoordinate(int nth, float value) { m_coordinate[nth] = value; }
};
Ultimately, I wish to write all the clusterPoints in clusterPointArray into the file. However, now I am just testing it with the first clusterPoint (thus, GetCoordinate(0)).
ofstream outFile;
outFile.open("C:\\data\\test.txt", std::ofstream::out | std::ofstream::app);
for (std::vector<CMDPoint> ::iterator it = clusterEntry->clusterPointArray.begin(); it != clusterEntry->clusterPointArray.end(); ++it)
{
outFile << ("%f", (*it).GetCoordinate(0)); // fails
outFile << " ";
}
outFile << "\n";
outFile.close();
The problem is I only see the " " in the file. No coordinate has been written in. Did I do anything wrong while fetching the value from const float GetCoordinate(int nth)?
try to change this
outFile << ("%f", (*it).GetCoordinate(0)); // fails
to this:
outFile << (*it).GetCoordinate(0); // OK
Because the ("%f", (*it).GetCoordinate(0)) represents nothing , only a enumerations of expressions separated by , . It will not be evaluated into a pair of objects as in java is i think.
Edit:("%f", (*it).GetCoordinate(0)) actually evaluates to the last element that is (*it).GetCoordinate(0) ( PlasmaHH comment ) so it should still print something. However if nothing is printed then the collection clusterEntry->clusterPointArray could be empty and the code inside the for loop might not be executed ever.
Hope this helps,
Razvan.
outFile << it->GetCoordinate(0);
Related
// In my Class A, I have many nodes and every node data is stored in a struct like this:
Class A
{
private:
struct BriteNodeInfo
{
int nodeId;
double xCoordinate;
double yCoordinate;
int inDegree;
int outDegree;
int asId;
std::string type;
};
};
// Each node instance is stored in a vector like this:
typedef std::vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
//And then, here is the function that I want to implent down below
void SaveNodeData (std::string fname);
};
Problem: How do I implent that SaveNodeData() function to save my nodes data in .txt file like this?:
nodeId0 yCoordinate0 xCoordinate0
nodeId1 yCoordinate1 xCoordinate1
nodeId2 yCoordinate2 xCoordinate2
nodeId3 yCoordinate3 xCoordinate3
etc...
I have tried but my iteration syntax is not good enough. Here is my function, please help:
Here is my failed function:
void SaveNodeData (std::string fname)
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
for (BriteNodeInfoList::Iterator i = m_briteNodeInfoList.Begin(); i != m_briteNodeInfoList.End(); ++i)
{
os << BriteNodeInfo[i].nodeId "\t" << "\t" << BriteNodeInfo[i].yCoordinate; << "\t"BriteNodeInfo[i].xCoordinate<< "\n";
}
os << "\n";
}
Before starting, this code as written will clearly have some compile errors. But assuming you can manage to fix those issues, there's one huge flaw.
Your function SaveNodeData creates an empty BriteNodeInfoList, and then tries to read from it. The for loop you have written will always simply exit.
What you need to do is create and populate a BriteNodeInfoList somewhere that this function reads. You could pass it in as an argument to the function, have it as a private variable for class A (assuming SaveNodeData is made into a member of class A). Or you could make it a static member variable of class A (not really recommended: static member variables of objects have some serious problems).
void SaveNodeData (std::string fname) // file path
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList = m_briteNodeInfoList;
for (std::vector<BriteNodeInfoList>::Iterator it = BriteNodeInfoList.Begin(); it != BriteNodeInfoList.End(); ++it)
{
os << (*it).nodeId << "\t" << (*it).yCoordinate << "\t" << (*it).xCoordinate << "\n";
}
}
Class C {
struct Something {
string s;
// Junk.
}
// map from some string to something.
map<string, Something> map;
// Some more code:
const Something *Lookup(string k) const {
const something *l = SomeLookUpFunction();
cout << l;
cout << &l->s;
cout << l->s;
return l;
}
}
// Some Test file
const C::Something *cs = C::Lookup("some_key");
cout << cs;
cout << &cs->s;
cout << cs->s;
The weird thing is this outputs:
* For lookup fucntion:
0x9999999
0x1277777
some_string
* For test code
0x9999999
0x1277777
000000000000000000000000000000000000000000 ....
In test file it gives a very long string of zeros, but the addresses are the same. Any idea what could be going wrong ?
Since you have not shared code for function SomeLookUpFunction, I have to guess that you are returning pointer to local object of type Something. This is a bad idea, see similar QA.
To start fixing your code, you should start by returning simple object, instead of pointer, as shown below:
// Some more code:
const Something lookup(string k) const {
const something l = SomeLookUpFunction(); // return simple object
cout << &l;
cout << &l.s;
cout << l.s;
return l; // same object
}
Of course you should improve the code by providing copy constructors for type something and even improving your map.
I'm currently creating a FAT filesystem in C++. I have three classes:
Sdisk (which formats the disk via string to file output).
Filesys (which writes the FAT and the root).
Shell (offers ability to perform various file system operations.)
During each class coding I wrote simple main initializations of Sdisk and Filesys constructors. Now that I'm writing the Shell when I test Shell's default constructor I'm having issues with memory. Specifically: EXC_BAD_ACCESS(code=1 address=0xfffffffffffffff....).
I decided to use my debugger to find out what went wrong and determined that the values I was passing in the Shell constructor to the Sdisk default constructor wasn't correct. I started to dig and found out that calling another classes constructor inside of the current classes constructor was called: "Delegating" or "Constructor Chaining". I had no idea. I found out that you have to "use it from the initialization list, not from the constructor body".
I've created an extra constructor with no parameters for Shell. What I need to do is call Sdisk from inside of the Shell constructor with Sdisk's default three parameters. But when I try that I continue to get the memory errors. I've even attempted to give the default constructor of Shell no parameters but as long as I call it in main it causes the error.
Any help regarding this matter is extremely appreciated! Thank you!
Here is my reference for Delegating Constructors
class Shell: public Filesys
{
public:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {Filesys(filename,blocksize,numberofblocks);}; // creates the file system.
int dir(); // call ls which lists all files
int add(string file); // add a new file using input from the keyboard.
int del(string file); // deletes the file
int type(string file); //lists the contents of file
int copy(string file1, string file2);//copies file1 to file2
friend class Sdisk;
friend class Filesys;
};
class Sdisk
{
public :
Sdisk() { }
Sdisk(string diskname); // Default Constructor
Sdisk(string diskname, int numberofblocks, int blocksize);
int getblock(int blocknumber, string& buffer);
int putblock(int blocknumber, string buffer);
int getblocksize() {return blocksize; } // Returns the blocksize.
int getnumberofblocks() { return numberofblocks; } // Returns the number of blocks.
string getfilename() { return diskname; } // Returns the disk name.
friend class Shell;
friend class Filesys;
private :
int numberofblocks; // number of blocks on disk
string diskname; // file name of pseudo-disk
string diskname1;
int blocksize; // block size in bytes/the number of blocks.
};
class Filesys
{
public:
Filesys(Sdisk&);
int fsclose();
int newfile(string file);
int rmfile(string file);
int getfirstblock(string file);
int addblock(string file, string block);
int delblock(string file, int blocknumber);
int readblock(string file, int blocknumber, string& buffer);
int writeblock(string file, int blocknumber, string buffer);
int nextblock(string file, int blocknumber);
bool checkblock(string file, int blocknumber);
vector<string> block(string buffer, int b);
Sdisk disk;
friend class Shell;
private :
int fssync(); //writes the Root and FAT to the disk.
string buffer;
int rootsize; // maximum number of entries in ROOT
int fatsize; // number of blocks occupied by FAT
vector<string> filename; // filenames in ROOT
vector<int> firstblock; // firstblocks in ROOT parallel
vector<int> fat; // FAT # of blocks
};
int main()
{
Shell("disk",256,128);
string s;
string command="go";
string op1,op2;
while (command != "quit")
{
command.clear();
op1.clear();
op2.clear();
cout << "$";
getline(cin,s);
unsigned long firstblank = s.find(' ');
if (firstblank < s.length()) s[firstblank]='#';
unsigned long secondblank = s.find(' ');
command=s.substr(0,firstblank);
if (firstblank < s.length())
op1=s.substr(firstblank+1,secondblank-firstblank-1);
if (secondblank < s.length())
op2=s.substr(secondblank+1);
if (command=="dir")
{
cout << "dir" << endl;
// use the ls function
}
if (command=="add")
{
// The variable op1 is the new file
cout << "add" << endl;
}
if (command=="del")
{
cout << "del" << endl;
// The variable op1 is the file
}
if (command=="type")
{
cout << "type" << endl;
// The variable op1 is the file
}
if (command=="copy")
{
cout << "copy" << endl;
// The variable op1 is the source file and the variable op2 is the destination file.
}
if (command=="exit")
{
cout << "Exiting now..." << endl;
return 0;
}
}
return 0;
}
Filesys::Filesys(Sdisk& sdisk):Sdisk(disk)
{
this-> disk = sdisk;
rootsize = disk.getblocksize()/12;
fatsize = (disk.getnumberofblocks()*5) / (disk.getblocksize())+1;
cout << "rootsize: " << rootsize << endl << "fatsize: " << fatsize << endl << "number of blocks: " << disk.getnumberofblocks() << endl << "getblocksize(): " << disk.getblocksize() << endl;
for(int i=0; i<rootsize; i++)
{
filename.push_back("XXXXXX");
firstblock.push_back(0);
}
int k= disk.getnumberofblocks();
fat.push_back(fatsize + 2);
for (int i = 0; i <= fatsize; i++)
{
fat.push_back(0);
}
for(int i = fatsize + 2; i < k; i++)
{
fat.push_back(i+1);
}
fat[fat.size()-1] = 0;
fssync();
}
Sdisk::Sdisk(string disk)
{
diskname = disk + ".dat";
diskname1 = disk + ".spc";
ifstream ifile(diskname1.c_str());
if(ifile.is_open())
{
ifile >> numberofblocks >> blocksize;
ifile.close();
}
else
{
cout << "Was unable to open the file" << endl;
}
}
// Sdisk default constructor
Sdisk::Sdisk(string disk, int numberofblocks, int blocksize)
{
this->diskname = disk + ".dat";
this->diskname1 = disk + ".spc";
this->numberofblocks = numberofblocks;
this->blocksize = blocksize;
fstream spcfile;
fstream datfile;
spcfile.open((this->diskname1).c_str(),ios::in | ios::out);
datfile.open((this->diskname).c_str(),ios::in | ios::out);
if (spcfile.good() && datfile.good())
{
cout << "The disk named: " << diskname.c_str() << " exists and is now ready to be written to." << endl;
}
else // .spc/.dat file creation.
{
cout << "The disk: " << diskname.c_str() << "could not be found. " << endl;
cout << "Both the SPC and DAT file were not found. Creating both now. Please wait...." << endl;
spcfile.open((this->diskname1).c_str(),ios::out);
datfile.open((this->diskname).c_str(),ios::out);
spcfile << numberofblocks << " " << blocksize;
cout << "The SPC file " << diskname.c_str() << " was created" << endl;
cout << "The DAT file " << diskname.c_str() << " was created" << endl;
for (int i=0; i<numberofblocks*blocksize; i++)
{
datfile.put('#'); // Fills the file with '#' character.
}
}
spcfile.close();
datfile.close();
return;
}
The problem is here:
Shell(string filename, int blocksize, int numberofblocks) : Filesys(disk)
{
Shell(filename,blocksize,numberofblocks);
};
The Shell constructor creates in the body a temporary shell object, which itself calls the Shell constructor again and so on. So you will end up with an infinite recursion and a full stack.
Other remarks:
In your shell constructor you also initalize the base class subobject with the mem-initializer Filesys(disk). I couldn't find a valid disk in your code snippet. But as you experience runtime problems and not compilation errors, I suppose it was simply lost in the copy&paste.
Are you sure that Shell should inherit from Filesys ? I.e. can you say that your Shell is a FAT filesystem ? And how would your class design evolve if later you'd decide to enrich the filesystems that your Shell suports with say NTFS or EXT3 filesystems ?
Finally, is the number of blocks and the blocksize not parameters of the filesystem rather than the shell you construct on its top ?
For these reasons I'd rather go for something like:
class Shell
{
string fname;
Filesys &fs;
public:
Shell(string filename, Filesys &filesystem)
: fname(filename), fs(disk)
{ ... }; // creates the file system.
...
};
In this case you'd create the Shell:
Sdisk mydisk("drive1", 32768, 4096); // disk data for the disk constructor
Filesys mysystem(mydisk); // fs parameters for the fs
Shell myshell("A:", mysystem); // higher level abstraction
I hope I got it right. You got quite many problems in your code. As I said in the comment this is very bad:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {
Shell(filename,blocksize,numberofblocks);
};
It is an obvious infinite recursion. You said you needed to call Sdisk from inside the Shell so I guess you meant:
Shell(string filename, int blocksize, int numberofblocks):Filesys(disk) {
Sdisk(filename,blocksize,numberofblocks);
};
However, it won't help you in any way. Such syntax will create a temporary Sdisk which will die immediately. You said you want to use default parameter but you don't provide any anywhere.
Generally your design is quite bad. The relations between classes are not clear and you ended up with strange solutions which do not make much sense. I.e. why do you want to call this constructor? What is the relation between Sdisk and Shell? Shell doesn't have any member variables so it cannot hold any state (I'm almost certain that it's not intended)... etc.
I have a newbie question about how to assign class member (setter). I am used to scripting and mostly there it's done via (in python)
def set_mymember(mymember):
self.mymeber = mymember
My coworker told me "self" and "this" are not needed in C++ , "this" exists and it's not wrong in this context but that would be hard to understand for me so he said I should not care. So I first tried according to his advice:
My class definition: - (it should create a sql query string)
class Query
{
public:
Query() { }
~Query() { }
void setoptions( std::string qtext_where="", bool qtext_erl=true, std::vector<std::string> kids=std::vector<std::string>() );
Query build_query( );
void set_db_table( std::string db_table );
void set_db_key( std::string db_key );
void set_m_qtext( std::string m_qtext );
void set_foo( std::string foo );
std::string sql();
std::string get_sql_update();
private:
std::string m_db_table; // Tabellenname
std::string m_db_key; // Tabellen-key
std::string m_qtext_where; // add.optionale where clause
std::string m_qtext; // fertiger SELECT
std::string m_sql_update; // fertiger UPDATE
bool m_erl; // nur erledigte waehlen?
std::vector<std::string> m_kids; // Liste von keys zu selecten
};
ANd here's one of the setter methods: I call them with filled string and vector, double check it in this code
void Query::setoptions( string qtext_where, bool erl, vector<string> kids ) {
m_qtext_where = qtext_where;
m_erl = erl;
m_kids = kids;
}
But when my app later calls query.build_query()
the variables are empty
Query Query::build_query( ) {
cout << "kids size" << m_kids.size() << endl;
cout << "m_qtext_where " << m_qtext_where << endl;
// Query zur auswahl der zu uebertragenden Datensaetze
string sql_update = "UPDATE " + m_db_table;
string qtext = "SELECT * FROM " + m_db_table;
string qtext_order = " ORDER BY " + m_db_key;
(...)
EDIT: So here's part of the app code which calls 1.setoptions, and 2.build_query
// read file line by line into vector of strings
vector<string> text_file;
ifstream ifs( input );
string temp;
while( getline( ifs, temp ) ) {
if (temp.substr(0,1) == "#" ) {
cout << "COMMENT: " << temp << endl;
continue;
}
cout << temp << endl;
text_file.push_back( temp );
}
// check: yes, vector has a size = number of lines
cout << "text_file size " << text_file.size() << endl;
// create Query object
Query query = Query();
// set the members, bool erl = true
query.setoptions( "", erl, text_file );
// call 2nd method
q2 = query.build_query();
Can't really tell whats going on without the full code, but I suspect that you're returning a query object from query.build_query that isn't a full copy of the query object, if that makes sense? Can you include the full text of build_query?
Also, I'd make the build_query method void, and not try to assign a fresh Query object back to a second Query object (q2) at all (unless you really need to, again, can't really tell without the full code), something like this:
void Query::build_query( ) {
std::cout << "kids size" << m_kids.size() << std::endl;
std::cout << "m_qtext_where " << m_qtext_where << std::endl;
}
main
{
...
Query query = Query();
// set the members, bool erl = true
query.setoptions( "", true, text_file );
// call 2nd method
query.build_query();
}
Also, just being pedantic here, but given that you're providing default args for all the options, I'd be inclined to initialise them in the constructor like this:
Query::Query()
: m_qtext_where("")
, qtext_erl(true)
, kids (std::vector<std::string>()
{}
And then instead of a setOptions method, have setters for each individual variable:
void setWhere(std::string qtext_where) {m_qtext_where = qtext_where ;}
void setErl(bool query_erl) { m_erl = query_erl; }
void setKids(std::vector<std::string> kids) { m_kids = kids; }
which you call only when you need to..
I have the following functor:
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
It is supposed to be a strict weak ordering, and it's this long (could be one line only) for debug purposes.
I'm using this functor as a comparator functor for a stl::set. Problem being, it only inserts the first element. By adding console output to the comparator function, I learned that it's actually comparing the file name to itself every time.
Other relevant lines are:
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
and
// (FileSet files_;) <- SimulatedDisk private class member
void SimulatedDisk::addFile(SimulatedDiskFile * file) {
files_.insert(file);
positions_calculated_ = false;
}
EDIT: the code that calls .addFile() is:
current_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
current_request++;
}
Where all_requests is a list, and class Request is such that:
class Request {
private:
string file_name_;
int response_code_;
int response_size_;
public:
void setFileName(string file_name);
string getFileName();
void setResponseCode(int response_code);
int getResponseCode();
void setResponseSize(int response_size);
int getResponseSize();
};
I wish I could offer my hypotesis as to what's going on, but I actually have no idea. Thanks in advance for any pointers.
There's nothing wrong with the code you've posted, functionally speaking. Here's a complete test program - I've only filled in the blanks, not changing your code at all.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class SimulatedDiskFile
{
public:
string getFileName() { return name; }
SimulatedDiskFile(const string &n)
: name(n) { }
string name;
};
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
int main()
{
FileSet files;
files.insert(new SimulatedDiskFile("a"));
files.insert(new SimulatedDiskFile("z"));
files.insert(new SimulatedDiskFile("m"));
FileSet::iterator f;
for (f = files.begin(); f != files.end(); f++)
cout << (*f)->name << std::endl;
return 0;
}
I get this output:
z and a: false
a and z: true
z and a: false
m and a: false
m and z: true
z and m: false
a and m: true
m and a: false
a
m
z
Note that the set ends up with all three things stored in it, and your comparison logging shows sensible behaviour.
Edit:
Your bug is in these line:
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
You're taking the address of a local object. Each time around the loop that object is destroyed and the next object is allocated into exactly the same space. So only the final object still exists at the end of the loop and you've added multiple pointers to that same object. Outside the loop, all bets are off because now none of the objects exist.
Either allocate each SimulatedDiskFile with new (like in my test, but then you'll have to figure out when to delete them), or else don't use pointers at all (far easier if it fits the constraints of your problem).
And here is the problem:
SimulatedDiskFile temp_file(current_request->getFileName(),
current_request->getResponseSize());
disk.addFile(&temp_file);
You are adding a pointer to a variable which is immediately destroyed. You need to dynamically create your SDF objects.
urrent_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(...blah..blah..); ====> pointer to local variable is inserted
disk.addFile(&temp_file);
current_request++;
}
temp_file would go out of scope the moment next iteration in while loop. You need to change the insert code. Create SimulatedDiskFile objects on heap and push otherwise if the objects are smaller then store by value in set.
Agree with #Earwicker. All looks good. Have you had a look inside all_requests? Maybe all the filenames are the same in there and everything else is working fine? (just thinking out loud here)