#include /* Let's try to simulate oop in C We known C doesn't have a very sophisticated representation of functions (just pointers to memory addresses containing code), but we can explicitly pair a function with a structure, thereby simulating the idea of closures. */ typedef struct accountstruct * account; struct accountstruct { int balance; char name[60]; }; account newaccount(int bal) { account a = (account)malloc(64); a->balance = bal; return a; } void deposit(account A, int amt) { A->balance += amt; } int inquiry(account A) { return A->balance; } int main(int arg, char **argv) { account myaccount, youraccount; myaccount = newaccount(500); youraccount = newaccount(600); deposit(myaccount,40); deposit(youraccount,50); printf("my balance is %d\n", inquiry(myaccount)); printf("your balance is %d\n", inquiry(youraccount)); exit(0); } /* Is this enough to be called object-oriented programming? In a way, we are simulating the idea of closures by making sure that each "method" takes an addition parameter that points to a struct - the struct is our explicit representation of "environments" that are automatically maintained in Scheme. In fact, if you compile a C++ program (with classes) and look at the assembly, you'll find that the methods are actually implemented in the same way: they take an additional parameter. But we're not programming in assembly. What's missing here that you would find in "real" oop languages? 1. Encapsulation. There is nothing to prevent you from accessing myaccount->balance anywhere in the program. There are further consequences of the lack of encapsulation. 2. Global functions. I can't have "inquiry" and "deposit" be any other functions. In Scheme, these functions are local variables, which are only accessible through the interface function. There is thus naturally no confusion between functions of the same name in different objects. In Java/C++, you can certainly have functions of the same names in different classes. But there is a more significant consequence of having only global functions: polymorphism. This word is loaded - it can mean many different things so treat it with respect - don't throw it around without thinking. Suppose I have two kinds of accounts: savings accounts and checking accounts. The bank may have two methods of depositing/withdrawing from these accounts. For example, depositing into the savings account could mean you are awarded a small bonus while depositing into checking does not. You'll therefore need two distinct pieces of code - but you'll want to call both functions "deposit" because in most of your program, you don't care if you're depositing into a savings or checking account. If you could not call both functions by the same name, you'll have to place a tag somewhere that indicates explicitly the type of the account, and then check the value of the tag to determine which "deposit" function you should call: struct taggedaccount { unsigned char accounttype; // 0 means checking, 1 means savings int balance; char name[60]; }; ... if (myaccount->accounttype == 1) deposit_savings(amount) else deposit_checking(amount); ... Cumbersome code. There is no AUTOMATIC DISPATCH! You might be thinking that one way to get around this problem is to implement something called overloading - that is, allow functions to be called by the same names, as long as the types of their parameters are different. This is a relatively simple thing to add to a programming language (but traditional C doesn't allow it). Then we would simply define two different structs, one for checking accounts and one for savings accounts. The "right" deposit function can then by disambiguated by the type: savingsaccount myaccount; checkingaccount youraccount; deposit(myaccount,30); deposit(youraccount,40); // calls a different function automatically! Defining two different structs also have other benefits. For example, savings accounts may have an interest_rate field while checking accounts do not. This would help a lot, but it's still not enough! What else is missing? The checking and savings accounts will have a lot in common, such as the inquiry function. So why would you have to reinvent the wheel for each type of account? That is, you don't want to have to define two inquiry functions, one for savings and one for checking accounts - because they'll do the same thing! As you might have heard, one why of solving the problem is to have inheritance (but there are other ways). That is, have both types of accounts inherit a common set of features from a parent (or "super") structure. Overloading is not enough (since there's only one inquiry function, there's nothing to overload!). Can we accomplish this in C? Not very well. We can try to capture explicitly the concept that within every instance of a subclass there's an instance of the superclass: typedef struct savingsaccount * savings; struct savingsaccount { double interestrate; account super; // literally contain an instance of // "superclass" }; savings newsavings(int bal, double intrate) { account super = newaccount(bal); // create super instance savings sub = (savings)malloc(12); } Now, in order to "inherit" the functions of the superclass, we can create a "proxy interface". A proxy is an intermediary or *relay*: (remember we're assuming we have overloading also) int inquiry(savingsaccount A) { return inquiry(A->super); } That is, for every function we wish to inherit, we create a function that take the sub-structure as an argument and invoke the corresponding function on the super-structure contained within the substructure. This is cumbersome, but it simulates inheritance UP TO A POINT. We still have other things to worry about: Sometimes, we want to create a generic "account" variable, that can be instantiated with EITHER a savings account or a checking account. The only way to do this, however, would be to again use an explicit tag inside the structure. Overloading won't work. Our data structure may then have to contain union structures so that values of different types can be inserted into the structure. This is both cumbersome and inefficient. It would be better if we had a way of automating all these features, instead of hand-coding them ourselfs. Thus we need languages such as Java/C++/C#. */