Sunday, March 23, 2008

Friends in C++

Friend functions and classes are not the most common of C++ features but there are situations where the friend keyword is useful.

A friend is something that allows your class to be accessed by a different class or function. The friend keyword can be used in 2 cases, to declare a friend function or a friend class.

When you declare a friend function in your class, then it can do anything that a member function of the class can do. So in the following, friendFn() can do anything to the object passed to it that memberFn() can do.


At the end of the above example, the private popObject data mData is 6. Note that the object passed is only changed as it is passed as a reference. If the function declaration for friendFn was as follows:
friendFn(PopularClass pObject, int x);
then friendFn() would have made a copy of the object passed and changed its member variable.


Friend Classes follow along the same principle, they allow another class to have access to the data in a class. So in the following, PopularClass allows the class FriendClass access to its private data. So when fObject.addVals(popobject) gets its hands on popObject it is allowed access to the private data so that it can add the private data from both its own class (which it, of course, always had access to) and the passed in popObject which is an object of a different class.



So friends can be useful if you treat them right (sorry, way too corny I know...)

Saturday, March 1, 2008

C++ inheritance oddity

There are plenty of things that can make C++ a complicated language. But in general the creators of C++ would have gone though a painstaking process to ensure that the language as intuitive and useable as possible (without sacrificing on functionality of course). However, when I came accross the following C++ behaviour I was confused.

Lets just say that I have a base class and a derived class. The base class has 3 member functions, each called doSomething() as below.

The functionality of the doSomething() methods isn't important right now, but assume that the methods are implemented. What is important is that you can create an object of type DerivedClass and it can use the 3 doSomething() methods defined in the base class. This is all straightforward inheritance stuff you say, and you'd be right, but wait, the craziness is about to begin...

Now, lets say I want to redefine one of the doSomething() methods, but I want to do it in the derived class, i.e. I want to do the following:
Again, assume that the DerivedClass's doSomething() method is implemented. Now, assume I make an object of type DerivedClass. If I call the bare doSomething() method everything is ok, but if I try to call doSomething(int x) or doSomething(double x) the program won't compile!!

This is because if you re-implement any of the functions in a base class, all other functions with the same name are now un-callable.

So what, you say. Sure re-implementing non-virtual functions in derived classes is a bad idea anyway. But this behaviour is the same even if the three doSomething() functions are virtual. I'll say that again: this behaviour is the same even if the three doSomething() functions are virtual. So the following will see the exact same behaviour.


The derived object is only able to call the doSomething() method that it defines itself, and not the other 2 defined in the base class. This seemed really strange to me, as I would have assumed that if I the doSomething() functions that accept the int and double could be called. After all, they are all virtual functions, which means that they are supposed to be selectively re-definable!!

The reason for this strangeness is so that you do not get caught out inheriting from distant base classes by mistake. But, I'd still prefer if it was not the case as it seems kind of counter intuitive.

There is a simple way around this, and it is to add in a using BaseClass::doSomething; in the derived class definition. This will work for both the virtial and non-virtual cases above, and will allow the DerivedClass to re-implement some of the doSomething() functions and still allow the other ones to be used.

--
Lurning Man
--