This statement should strike you as obvious for many reasons, syntax being the most prominent. However, it does bear repeating on occasion for some of the lesser-known differences between the two languages. At their heart, most OO languages have a lot of similarities between one another. After all, just how many different ways are there to encapsulate data and logic?
One major cross-over feature of all object oriented languages is the concept of a class constructor. The compiler inserts some magical code for you whenever you create a new instance of a class that automatically calls this special function. This way, the programmer can be assured that there is no possible way to allocate a valid class instance without calling this special method. It's perfect for initialization tasks that need to run before everything else.
However, this ubiquitous concept does come with some very subtle differences. Namely -- how do constructors work with inheritance?
Let's take a look at a contrived example in C++ to see how constructors work.
class Test {
public:
Test() { printf( "Test.Constructor\n" ); }
virtual void Wahoo() { printf( "Test.Wahoo" ); }
};
class Awesome : public Test {
public:
Awesome() { printf( "Awesome.Constructor\n" ); }
virtual void Wahoo() { printf( "Awesome.Wahoo" ); }
};
class SubTest : public Awesome {
public:
SubTest() { printf( "SubTest.Constructor\n" ); }
virtual void Wahoo() { printf( "SubTest.Wahoo" ); }
};
In this example, if the programmer were to call new SubTest, what would you expect to see printed out on the command line? The answer is: Test.Constructor, Awesome.Constructor, SubTest.Constructor. In C++, objects are created as their base type first, and then walk down the inheritance chain. This means that when Test::Test is called, the "this" pointer it uses has a type of Test. In Awesome::Awesome, the type of "this" is Awesome. And so on.
This has an extremely important ramification that many C++ programmers gloss over. You should never call a virtual function from a C++ constructor! Since the actual type morphs as the object is constructed, it means that you will end up calling the wrong virtual function! When executing Test::Test, the type of "this" is Test, so if you were to call this->Wahoo, what you would end up calling is Test::Wahoo instead of SubTest::Wahoo. You may not think that's a major deal, but watch how quickly it can spiral into strange crashes:
class Test {
Test() { Wahoo(); }
virtual void Wahoo( void ) = 0L;
};
class SubTest {
virtual voild Wahoo( void ) { printf( "Wahoo!\n" ); }
};
If you're lucky, your compiler will give you a very obtuse linker error about Wahoo. If you're not lucky, it'll gleefully link against a function that does not exist at runtime!
Now, contrast this with the way constructor functions work in REALbasic. In REALbasic, an object starts its life out as its most derived type, as determined by the new keyword's operand. So if you made a new SubTest, then the object would start its life out as a SubTest. What's more, the most derived type's which has a constructor will be called automatically, and nothing else. It is up to the programmer to pass the construction up the chain, as they deem appropriate. Since the type is fully constructed by the time the first constructor is called, any virtual function call (which is any instance method call in REALbasic since all non-shared methods are always virtual) will work as expected and without issue. You can never get into the situation where you are calling a function you don't expect to call -- or worse, calling a function that doesn't really exist!
So if that C++ example were converted to proper REALbasic syntax, what would be printed on the command line is SubTest.Constructor, Awesome.Constructor, Test.Constructor. But proper is the key word in that sentence.
The downside to the REALbasic way is that the constructor call chain is not automated for you. If you don't call Super.Constructor, then nothing will. The reason is because it's usually better to err on the side of being explicit. REALbasic tries to ensure that the programmer has to worry about the language as little as possible, while getting things right automatically as often as possible. What's more, REALbasic tries to follow the BASIC philosophy in terms of how its language constructs work. But in this case, it's a very tough decision to make any which way you look at the problem.
If the compiler were to automatically call the superclass' constructor, then it would have to be very smart about the case where the programmer called a constructor explicitly, and be sure not to also call the default constructor. (Note that C++ doesn't do this either -- if you call the superclass constructor from within the derived class constructor yourself, C++ will still have called the superclass' default constructor.) If the RB compiler didn't do this, then you will have some people scratching their heads going "why is Foo() being called in addition to my call to Foo( X )?" Giving the compiler the kind of smarts it would need to accomplish this would make things a lot harder to explain to users, and have some edge cases where it would behave contrary to expectations. Either that, or it would require some decidedly not BASIC syntax, akin to the C++ colon operator.
Instead, we force the programmer to always be explicit about their intentions with construction. This makes constructors a lot less magical at the expense of a few more keystrokes. The one unfortunate problem with this is that the programmer cannot always call Super.Constructor because the base Object type has no default constructor -- the programmer has to have extra knowledge of their superclass. However, that could change in the future, too...
REALbasic isn't meant to be a clone of any language, especially not C++. So the next time you find yourself holding C++ up as the gold standard of OOP language design (especially when filing feature requests), keep in mind that C++ is a complex beast that has some very strange pitfalls. It may be an excellent language, but it's not REALbasic.
I can find my ways around in different enviroments but as a RealBasic newbie,I find these tidbits absolutely golden!
@mikita -- I'm glad you find my blog useful!
C++ the paragon of OO ... ick !!!!!!!!
Smalltalk maybe :)
@Norman -- you'd be amazed at the number of feature requests we get from people who want us to port C++ language features straight into REALbasic. It's incredible.
It goes back to a post I made ages ago about "tell us what problem you cannot solve and let us come up with the way to solve it."
well the day you add inline assembler ... :)