I'm currently creating a FAT filesystem in C++. I have three classes:
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