tags:

views:

99

answers:

5

Say, I want to create a File class

class File{
    public:
          File(const char *file){
               openFile(file);
          }
          ~File();
          isEmpty();
};

openFile checks if the file exists or if the contents of the file are valid or not.

File *file = new File("filepath");
if(file)
file->isEmpty();

if my filepath is correct, then all fine, file instance is proper and we can invoke file->isEmpty(); What is the file does not exist, in that case still the check if(file) evaluates to true and will lead to creation of file instance which is actually invalid. How can i guarantee that if file path is invalid, the file instance should be null.

+6  A: 

Your constructor should throw an exception in the case that the file couldn't be opened.

File::File( const char* pPath )
{
  if ( !openFile( pPath ) )
    throw FileNotFound( pPath );
}
Evän Vrooksövich
In connection with this, it might also make sense to avoid allocating things dynamically unnecessarily. The File object is one of those, that don't require **new**.
UncleBens
A: 

The only way to do checking like that in the constructor is to throw an exception. But that's not considered good design - you should create it, check if it's valid, and delete it if it isn't.

Paul Tomblin
how? are you suggesting a factory kind of stuff
Devil Jin
why not? "C++ coding standards" states that throwing an exception from the constructor is the right thing to do in these cases. Of course you have to ensure that all resources are correctly deallocated, but this is no different from other uses of exceptions as far as I can tell.
UncleZeiv
@Uncle, because a wrong file name is not "exceptional". I'd rather not incur the overhead of exception handling for something that is easily handled by calling a "isValid" method.
Paul Tomblin
There's probably unnecessary overhead in allocating a File object dynamically. And in any case, how many Files you are going to try to open?
UncleBens
I think "not considered good design" is overstating the case slightly, but I don't think it's worth downvoting someone for suggesting doing exactly what `std::fstream` does...
Steve Jessop
+1  A: 

I suggest using a static factory method that does the check for you and if the object is invalid deletes it and throws an exception. You would create the file like this:

File* file = File::open("whatever");
Martinho Fernandes
A: 

If I understand what you correctly, you want to return null from the constructor when "filepath" is invalid? This isn't (directly) possible in C++, although there are a few possibilities. You could throw an exception from your constructor, but this can get hairy in C++. You could have some functions that can check the validity of a File object, so if(file) would become if(isValid(file)). You could also wrap some of this logic in some sort of factory that would return null if the file to be created is invalid.

Paul Wicks
why do you say that it can get hairy to throw an exception in the constructor? the language enforces that the lifetime of the object never begins if an exception is thrown from the constructor.
UncleZeiv
A: 

I would use the STL, templates and throw an empty class. Now, you can't return anything from a constructor... so either do something like this:

#include <string>
using std::basic_string;

class EmptyFile{};

template<typename T>
    class File
    {
    public:
     File(const basic_string<T> &FILE)
     {
      if (isEmpty(FILE)) throw EmptyFile();
      openFile(FILE);
     }
     bool isEmpty(const basic_string<T> &FILE) const
      { return FILE.empty(); }
    };

or you could do this:

#include <string>
using std::basic_string;

template<typename T>
class File
{
public:
    bool Open(const basic_string<T> &FILE) const
    {
     bool empty = isEmpty(FILE);
     if(!empty)
        /* open the file */;
     return isEmpty;
    }
    bool isEmpty(const basic_string<T> &FILE) const
    { return FILE.empty(); }
};
Partial