I read Scott Meyers\' article on the subject and quite confused about what he is talking about. I have 3 questions here.
Question 1
To expl
I suppose the general point is that it is beneficial to always implement things in terms of other things if you can. Implementing functionality as non-friend free functions, ensures that this functionality doesn't break if you change the class representation.
In real life, I guess it might have a problem: you might be able to implement something in terms of the public interface with the current implementation, but if there are changes to the class, this might not be possible any more (and you'll need to start declaring things friends). (E.g when it comes to algorithmic optimization, the free function might benefit from some extra cached data, that shouldn't be exposed to the public.)
So the guideline I'd derive from it: use common sense, but don't be afraid of free functions. They don't make your C++ code less object-oriented.
Another things is probably an interface consisting entirely of getters and setters. This hardly encapsulates anything.
In case of Point in particular, you might get the temptation to store the data as int coords[2]
instead, and in this respect the getters and setters might have a meaning (but one might also always consider the ease of use vs ease of implementation).
But if you move on to more complicated classes, they should do something (some core functionality) other than just giving access to their data.
When it comes to vector, some of its methods could have been free functions: assign (in terms of clear + insert), at, back, front (in terms of size + operator[]
), empty (in terms of size or begin / end), pop_back
(erase + size), push_back
(insert + size), end (begin + size), rbegin and rend (begin and end).
But if taken rigorously, this could lead to rather confusing interfaces, e.g
for (vector<T>::iterator it = v.begin(); it != end(v); ++it)
Furthermore, here one would have to consider the capabilities of other containers. If std::list cannot implement end as a free function, then std::vector shouldn't either (templates need one uniform pattern for iterating over a container).
Again, use common sense.
The meaning f needs type conversions on it left-most arg is as follows:
consider following senario :
Class Integer
{
private:
int num;
public:
int getNum( return num;)
Integer(int n = 0) { num = n;}
Integer(const Integer &rhs)) { num = rhs.num ;}
Integer operator * (const Integer &rhs)
{
return Integer(num * rhs.num);
}
}
int main()
{
Integer i1(5);
Integer i3 = i1 * 3; // valid
Integer i3 = 3 * i1 ; // error
}
In above code i3 = i1 * 3
is equivalent to this->operator*(3)
which is valid since 3 is implicitly converted to Integer.
Where as in later i3 = 3 * i1
is equivalent to 3.operator*(i1)
, as per rule when u overload operator using member function the invoking object must be of the same class.
but here its not that.
To make Integer i3 = 3 * i1
work one can define non-member function as follow :
Integer operator * (const Integer &lhs , const Integer &rhs) // non-member function
{
return Integer(lhs.getNum() * rhs.getNum());
}
I think you will get idea from this example.....
Question : 2
Scott Meyers has also suggested following thing if u remember :
--> Keep the class interface complete and minimal.
See following scenario :
class Person {
private: string name;
unsigned int age;
long salary;
public:
void setName(string);// assme the implementation
void setAge(unsigned int); // assme the implementation
void setSalary(long sal); // assme the implementation
void setPersonData()
{
setName("Scott");
setAge(25);
selSalary(50000);
}
}
here setPersonData()
is member function but ultimately what its doing can also be achieved by making it non - member function like this and it will keep interface of class minimal and does not bloat class with plenty of member function unnecessarily .
void setPersonData(Person &p)
{
p.setName("Scott");
p.setAge(25);
p.selSalary(50000);
}
Question 1
In this case, following Meyers's algorithm will give you member functions:
operator<<
or operator>>
? No.His advice is to only make them friends when they really need to be; to favour non-member non-friends over members over friends.
Question 2
The SetXXXX
functions need to access the internal (private) representation of the class, so they can't be non-member non-friends; so, Meyers argues, they should be members rather than friends.
The encapsulation comes about by hiding the details of how the class is implemented; you define a public interface separately from a private implementation. If you then invent a better implementation, you can change it without changing the public interface, and any code using the class will continue to work. So Meyers's "number of functions which might be broken" counts the member and friend functions (which we can easily track down by looking at the class definition), but not any non-member non-friend functions using the class through its public interface.
Question 3
This has been answered.
The important points to take away from Meyers's advice are:
Take a good look at the STL algorithms. sort
, copy
, transform
etc operate on iterators and aren't member functions.
You're also wrong about his algorithm. The set and get functions can't be implemented with Point's public interface.
Until we keep the method signature intact when class implementation changes, no client code is gonna break and it is well encapsulated, right? The same applies for non-member functions as well. So what is the advantage non-member function provides?
Meyers is saying that a class with many methods is less encapsulated than a class with fewer methods, because the implementations of all those internal methods are subject to changing. If any of the methods could have been non-members, that would reduce the number of methods that could be affected by changes internal to the class.
What he meant by f needs type conversions on its left-most argument?
I think he's referring to operators, functions that would have an implicit left-most argument if they were member functions.