Operator overloading in C++

I did my first operator overloading in C++ today. My code ended up being quite different to the example code that I found, but I think I’m happy with it:

class FileData {
public:
  FileData( const string& path );
  static bool LessThan( const FileData& a, const FileData& b );
  bool operator< ( const FileData& );
  unsigned long size() const { return m_size; }
private:
  unsigned long m_size;
  char m_md5[ 16 ];
};

static bool operator< ( const FileData& a, const FileData& b ) {
  return FileData::LessThan( a, b );
}

bool FileData::operator< ( const FileData& b ) {
  return FileData::LessThan( *this, b );
}

bool FileData::LessThan ( const FileData& a, const FileData& b ) {
  unsigned long a_size = a.size();
  unsigned long b_size = b.size();
  if ( a_size == b_size ) {
    return memcmp( a.m_md5, b.m_md5, 16 ) < 0;
  }
  return a_size < b_size;
}

I'm not sure when it becomes either useful or necessary to define the operator both as a static and as a member function. I did both because I wasn't sure what was required.

Differences between C++ pointers and C++ references

I was curious about the difference between C++ pointers and C++ references, so I searched and found this which says that basically:

  1. It’s not necessary to initialise pointers at declaration time, but it is necessary to initialise references at declaration time.
  2. You can create an array of pointers, but you can’t create an array of references.
  3. You can assign null to pointers, but you can’t assign null to references.

Reading binary files in C++

I learned how to read binary files in C++ today. My function (which creates an MD5 hash of a file) ended up looking a little different to the example that I learned from:

  const int BUFFER_SIZE = 1024;

  int length;
  char buffer[ BUFFER_SIZE ];

  MD5_CTX ctx;
  MD5Init( &ctx );

  ifstream is( path.c_str(), ios::binary );

  while ( is.good() ) {

    is.read( (char *)buffer, BUFFER_SIZE );

    streamsize count = is.gcount();

    MD5Update( &ctx, (unsigned char *)buffer, count );

  }

  if ( is.eof() ) {

    // it's ok, we're at the end of the file

  }
  else if ( is.bad() ) {

    // bad bit is set
    cout << "Bad bit is set while reading '" << path << "'." << endl;

    cout << strerror( errno ) << endl;

    exit ( 1 );

  }
  else if ( is.fail() ) {

    cout << "Fail bit is set while reading '" << path << "'." << endl;

    cout << strerror( errno ) << endl;

    exit( 1 );

  }

  is.close();

Fatal error LNK1120: unresolved externals, caused by C functions in C++

I was receiving link error LNK1120 in Visual Studio after adding some C code to my C++ project, and the problem was, as I discovered here (and found more info on here), that I hadn’t declared my C code as “extern C”.

So basically I added #ifdef __cplusplus macros to check if it was C or C++ code and if C++ then outputting “extern C {” with a suitable “}” at the end of the file.

Don’t use memcmp to compare structs or classes

Structs and classes can be laid out with padding between the data members for alignment purposes, and this padding doesn’t get initialised unless you specifically zero it out yourself. So if you’re using memcmp and comparing memory at pointers to structs you might have a problem if you’re expecting structs with equal data members to always be equal.

Compiler Error C2662

I was forced to lookup Visual Studio’s Compiler Error C2662 because I was getting it in my code. Turns out this happens when your const’s don’t line up. In my case I had const in the client declaration but the member function I was calling didn’t have the const specification.

This little nugget of code from the referenced article shows how to fix up the problem:

// C2662.cpp
class C {
public:
   void func1();
   void func2() const{}
} const c;

int main() {
   c.func1();   // C2662
   c.func2();   // OK
}

File names on Windows

I’ve been reading up on file names in Windows because I’m having a problem with my C++ code processing a file with an odd TM character in the file name. I’m not sure why, but it seems that file names returned by the POSIX readdir function don’t necessarily exist when then given to ifstream.open, or some weird character encoding thing is going on. Hopefully I get to the bottom of it. It’s a complete fluke that I actually had a file this failed on available and did testing on it, lucky I guess.

I did some more reading and discovered that there are ‘wide character’ versions of the file functions for Windows that use UTF-16 encoded strings rather than ‘code page’ encoded strings, I guess. Anyway, I don’t think I’m going to bother with such things, if the file can’t be opened because it has a weird character in it then I’ll just fail with an error message and the user can look at fixing it. This program is only being “developed and tested” in Windows, there are no plans to actually run it on Windows, it will run on Linux, which won’t have this weird character encoding issue.

The difference between delete and delete[] in C++

I just learned about the difference between delete and delete[] in C++ by reading this article from StackOverflow. Basically you use delete[] to delete arrays, and delete for everything else. There was conflicting information about whether C++ runs the destructors on objects in an array when the array is deleted. Some people said it did, others said it didn’t. I should do an experiment to see one day. The reason for the difference between delete and delete[] seems to be that when C++ allocates an array it allocates memory for storing the size of the array as well as the array elements, and then returns a pointer to the first array element, which is beyond the start of the allocated memory, because the array size takes up the first bit of space.