Class vs. struct
Using class or struct keyword is a matter of taste together with the 'feeling' it produces on the reader. Technically they are equivalent, but readability is better if structs are used for PODs and C-struct types and classes for anything else.
Basic things that should go in a C++ struct: constructor that initializes the data (I dislike using memset, and it can later bite back if the POD evolves into something different) or construction from other types but not copy constructor.
If you need to define a copy constructor or assignment operator because the compiler generated is not good enough, make it a class.
It is common to use structs also for functors that will be passed to STL algorithms and template metaprogramming, as in
struct square_int {
int operator()( int value )
{
return value*value;
}
};
std::transform( v.begin(), v.end(), v.begin(), square_int() );
or
// off the top of my head
template <typename T>
struct is_pointer { enum { value = false } };
template <typename T>
struct is_pointer<T*> { enum { value = true } };
Member methods vs. free functions
Besides what I have said before, that do not add to what others already answered, I wanted to put some focus on other types of functions that you comment in your post, as comparison operators and the like.
Operators that are meant to be symmetric (comparison, arithmetic), insertion and deletion operators and transformations are usually better implemented as free functions regardless of whether you declare it as a class or struct.
Symmetric operators (with regard to data types) are not symmetric if they are implemented as member functions. The lookup rules won't cast the left hand side to call a member function, but it will apply the same cast to match a free function.
// Example of symmetry with free functions where method would be asymmetric
int main()
{
std::string( "Hello " ) + "world"; // compiles as free / member function
"Hello " + std::string( "world" ); // compiles with free function, fails with member function definition of +
}
In the code above, if operator+ were a member method of std::string the compiler would fail to compile as it cannot cast the const char* literal into a std::string to use the member method.
Insertion and extraction from streams must always be implemented as free functions as the stream is always the left hand side of the operation.
Keeping transformations as free functions decouple the two different types. If A and A' can be converted into one another and you decide to implement transformations as members of A, then A must know A' and all uses of A will depend on A' whether you use it or not. If you define the transformation as a free function, A is complete without A' and the coupling between the two classes/structs will be smaller. The same goes for transformations to/from network, serialization and deserialization. When you implement them inside the class/struct you are forcing all users to know about those transforms.