I won't enter this OOP discussion. But from my experience, setters are useful for maintaining the coherence of an object. Let's suppose that I have a class, Oriented_bounding_box. It has a Vector3 member variable, position_, a private Matrix33 member variable orientation_, and a Vector3 member variable extents_.
If I want the variable orientation_ to always be set to a rotation matrix( with a given tolerance ), I could simply define a member function:
void Oriented_bounding_box::set_orientation( Matrix33 const & orientation ){ assert( transpose( orientation ).is_equal( inverse( orientation ), tolerance_ ) && std::fabs( determinant( orientation ) - 1.0 ) <= tolerance_ ); orientation_ = orientation; }
I can enforce that condition on orientation_ wherever I want inside the Oriented_bounding_box class.
Somebody could say that the same could be done with a free function taking an Oriented_bounding_box structure instance, but then, there is no way to enforce the coherence of its orientation_ throughout the lifetime of that instance. Well, I am talking about encapsulation rather than OOP.