There is example that shows that using RAII this way:
class File_ptr{
//...
File* p;
int* i;
public:
File_ptr(const char* n, const char* s){
i=new int[100];
p=fopen(n,a); // imagine fopen might throws
}
~File_ptr(){fclose(p);}
}
void use_file(const char* fn){
File_ptr(fn,"r");
}
is safe. but my question is: what if there is exception thrown in p=fopen(n,a);
then memory allocated to i
is not returned. Is this right to assume that RAII
tells you then each time you want X
to be safe then all resources acquired by X
must be allocated on stack? And if X.a
is being created then resources of a
must also be placed on stack? and again, and again, I mean finally if there is some resource placed on heap how it could be handled with RAII? If it is not mine class i.e.
Treating this as an intellectual exercise where you don't want to use std::vector
, you need to divide your classes up so they have a single responsibility. Here's my "integer array" class. Its responsibility is to manage the memory for an integer array.
class IntArray {
public:
IntArray() : ptr_(new int[100]) {}
~IntArray() { delete[] ptr_; }
IntArray(const IntArray&) = delete; // making copyable == exercise for reader
IntArray& operator=(const IntArray&) = delete;
// TODO: accessor?
private:
int* ptr_;
};
Here is my file handling class. Its responsibility is to manage a FILE*
.
class FileHandle {
public:
FileHandle(const char* name, const char* mode)
: fp_(fopen(name, mode))
{
if (fp_ == 0)
throw std::runtime_error("Failed to open file");
}
~FileHandle() {
fclose(fp_); // squelch errors
}
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// TODO: accessor?
private:
FILE* fp_;
};
Note, that I convert my construction error to an exception; fp_
being a valid file pointer is an invariant that I wish to maintain so I abort construction if I cannot set this invariant up.
Now, makeing File_ptr
exception safe is easy and the class needs no complex resource management.
class File_ptr {
private:
FileHandle p;
IntArray i;
public:
File_ptr(const char* n, const char* s)
: p(n, s)
, i()
{}
};
Note the lack of any user-declared destructor, copy assignment operator or copy constructor. I can swap the order of the members and in either case it doesn't matter which constructor throws.