triptico.com

Un naufragio personal

From C to C++: A quick reference for aging programmers

So you were a great system programmer, back in the old days; your low level programs were celebrated by clever people, you loved C pointers and some days even considered Assembler as an option. You were happy and self-confident. But somehow you screwed something, got trapped in a time vortex and you ended today, trying to maintain or develop a program using that pesky Object Oriented Programming model in something called C++. I understand you; follow this guide and learn a bunch of things that will put you out of your misery and understand this brave new world.

Structs

Structs are just like in C; you know, use and love that little things. You have them in C++, with a little advantage: they can contain functions.

 struct rectangle {
 	int x;
 	int y;
 	int width;
 	int height;
 
 	int surface(void)
 	{
 		return width * height;
 	}
 };

Be honest and say that you always wanted that. And another thing you always desired: the struct keyword is not needed in the declaration:

 int main(int argc, char *argv[])
 {
 	rectangle r;
 	int s;
 
 	r.x = 0; r.y = 0;
 	r.width = 100; r.height = 50;
 
 	s = r.surface();
 }

A function defined inside a struct is called a method. Take note that it can access struct members directly.

But you, the clever and experienced C programmer, will had never defined the struct that way; you are used to have your structs in a header file, and your code in a pure source file. Well, do it:

 /* in a .h file */
 struct rectangle {
 	int x;
 	int y;
 	int width;
 	int height;
 
 	/* just the prototype here */
 	int surface(void);
 };
 
 /* in a .cpp file */
 int rectangle::surface(void)
 {
 	return width * height;
 }

You are ready to understand classes.

Classes

Yes, classes are those things young programmers are all talking about. But they are nothing more than a special kind of structs, where you must say how visible their components are to the outside world. So, you can write the above code this way:

 /* in a .h file */
 class rectangle {
 public:
 	int x;
 	int y;
 	int width;
 	int height;
 
 	/* just the prototype here */
 	int surface(void);
 };
 
 /* in a .cpp file */
 int rectangle::surface(void)
 {
 	return width * height;
 }
 
 /* in your main file */
 int main(int argc, char *argv[])
 {
 	rectangle r;
 	int s;
 
 	r.x = 0; r.y = 0;
 	r.width = 100; r.height = 50;
 
 	s = r.surface();
 }

Few things have changed; the keyword was changed from struct to class, and a new public: keyword was introduced. This leads us to...

Encapsulation

Components in a class are usually not visible from the outside. The scope can be set with the following keywords:

public
All components under this keyword can directly accessed from anywhere. This is the same as in old world structs. Only methods are recommended to be public (i.e., not variables), but anything can be.
private
All components under this keywords are accesible only by methods in its own class.
protected
All components under this keywords are accesible only to methods in its own and derived classes. We'll talk about that later.

A class variable can be static; this means that the same copy of that variable will be shared among all components of a class. Useful to implement object counters and such. It must be initialized from outside the class.

Regarding private or protected variables, you'll hear those strange people talking about 'getters/setters' or even 'accessors/mutators': don't panic, those are just methods to set and get values from those hidden variables. You always did the same in object files.

Constructors and destructors

Wouldn't it be good if you could create a rectangle instance this way:

 rectangle r (10, 10, 40, 50);

Well, you can; you just have to define a constructor, that is, a method that will be called when the instance is constructed. You just name it after the class:

 rectangle::rectangle(int a, int b, int w, int h)
 {
 	x = a;
 	y = b;
 	width = w;
 	height = h;
 }

Other initialization can be done there as well. Remember that you must include the constructor prototype in your class.

The opposite is the destructor; a method called when the object is destroyed. Usually it's not needed, but you can release resources in it, as open files and such. Just name it prepending a tilde to the class name.

 rectangle::~rectangle()
 {
 	fclose(log_file);
 }

It's common practice, if a count of objects is needed, to increase a static class variable from the constructor and decrease it from the destructor.

This

When you saw the previous declaration of rectangle(), you didn't like to have different names for arguments and variables. Though you find useful to be able to access class variables directly, you would like to have a way to distinguish those variables from others, as for example method arguments. You can do it with this:

 rectangle::rectangle(int x, int y, int width, int height)
 {
 	this->x      = x;
 	this->y      = y;
 	this->width  = width;
 	this->height = height;
 }

This magical pointer always points to the current object.

Inheritance

It's possible to create classes that share features from another, possibly overwriting / adding new attributes and methods to the original one. This is called inheritance. Classes that inherit from others are called derived.

Let's create a class for a rectangular prism:

 class rect_prism : public rectangle {
 public:
 	int z;
 	int depth;
 
	rect_prism(int, int, int, int, int, int);
 };
 
 /* constructor */
 rect_prism::rect_prism (int a, int b, int c, int w, int h, int d)
 {
 	x = a; y = b; z = c;
 	width = w; height = h; depth = d;
 }

So you can create rect_prism objects, and as they inherit from the rectangle class, they can use the surface() method.

A better way of writing the constructor is using also inheritance:

 /* constructor */
 rect_prism::rect_prism (int a, int b, int c,
 			 int w, int h, int d) : rectangle(a, b, w, h)
 {
 	z = c;
 	depth = d;
 }

So rectangle() is called with the appropriate arguments, and then the rest of the arguments assigned.

You can also create a square class as a special kind of rectangle:

 class square : public rectangle {
 public:
 	square(int, int, int);
 };
 
 /* constructor */
 square::square (int a, int b, int wh) : rectangle(a, b, wh, wh)
 {
 }

This way of calling a function from the base class is useful, but it can only be used in constructors and is always executed before any other code. But you can call a method from the base class at any time by explicitly naming the class:

 int derived_class::do_something(int value1, int value2)
 {
 	/* do things not related to the base class */
 	this->value2 = value2;
 
	/* then call the base method */
 	base_class::do_something(value1);
 
 	/* more code, if needed */
 	/* ... */
 }

Virtual methods

And what if you want to call a method from a base class function that can be redefined (or not) in a derived class? You just add virtual to its definition. That virtuality means that calls are not hardcoded inside the binary file, but an object indirection and resolving is done on every call. As you imagine this adds a little space and execution time overhead, but who cares.

Overloading

Function and method overloading

You can use the same name for a set of functions if they vary in the number or type of their arguments:

 double absolute(double x)
 /* double floating point version */
 {
 	return fabs(x);
 }
 
 int absolute(int x)
 /* integer version */
 {
 	return x > 0 ? x : -x;
 }

The compiler will construct the correct call for any use. I know you'll love this.

Operator overloading

The operators can also be overloaded. For example, you can overload the + (plus) sign to mean concatenation for strings):

 char * operator + (char *one, char *two)
 {
 	char *r = malloc(strlen(one) + strlen(two) + 1);
 	strcpy(r, one);
 	strcat(r, two);
 
 	return r;
 }

This function returns a newly allocated string that is the concatenation of the two ones sent as arguments.

 char *full = "Hello " + "There!";

This sounds cool, but it's wise to handle with care.

References

It's now possible to declare a reference to variable. This means that everytime one of the variables change, the other do as well, as they really point to the same storage.

 {
 	int a = 5;
	int &b = a;
 
 	b = 10; /* a is also 10 */
 }

A reference cannot be changed to point to another variable afterwards: they are constant.

Yes, all this can be done with pointers. I find it particularly confusing, but you'll need to know what is about when you see that funny & there.

It can also be used for function arguments so they are real pass-by-reference.

 void add_to_me(int &a, int b)
 {
 	a += b;
 }
 
 int v = 10;
 add_to_me(v, 20); /* v is now 30 */

Yes, this can also be done with preprocessor macros, but here you have all type checking and such.

Exceptions

Exceptions can be seen as sophisticated goto/switch control structures. They are implemented by using the try / throw / catch construction.

 try {
 	if (some_condition) {
 		/* do important things */
 		throw 1;
 	}
 	if (another_condition) {
 		/* do another set of important things */
 		throw 2;
 	}
 
 	throw 0;
 }
 catch (int r) {
 	/* do something amazing with the result */
 }

Templates

I'm sure at least once in your long programmer life you found yourself implementing two or more different versions of a function that does the same but for different types of arguments (e.g. some for integers, another one for floats). You probably ended up writing clever and cumbersome preprocessor macros to avoid having copies of the same algorithm.

Templates will help you no longer feeling miserable.

See how I implement a multiply function for any kind of argument:

 template <class ttype>
 ttype multiply(ttype a, ttype b)
 {
 	return a * b;
 }

Everytime a call to multiply() is written in your code, a special version of the function is compiled in.

Other features

Default values for function arguments

C++ allows for default arguments to be defined:

 int sum(int a = 0, int b = 0)
 {
 	return a + b;
 }

so sum() can be called with two, one or no arguments and always work.

New and delete

These functions are like malloc() and free(), but when applied to objects, they also call constructors and destructors.

See also


Angel Ortega - angel@triptico.com

Related

Visitor comments

Mike
2010-11-29
Very clear and very valuable document. Thank you very much.

Anonymous
2011-01-05
The templates section should be extended, but it's very good overall.

Anonymous
2011-10-15
Really nice. Probably the most compact C++ tutorial I've seen. (2 typos though! fix them and achieve perfection :)

X
2011-10-15
In the 'references' section, in both code examples, the comments are incorrect. They should read

a is also 10

v is now 30

Angel Ortega
2011-10-15
Thanks to both.

cmiN
2011-10-15
Great!

North Hollywood Asp Programmer
2011-10-15
how can i share this to my facebook wall? thanks!

-http://www.imagineit.com

Angel Ortega
2011-10-15
@North Hollywood Asp Programmer: you have here links to publish this article in Facebook and Twitter, under the "Share" section.

mayo
2011-10-15
It's gonna help me a lot :) Thank you

Aging programmer ..
2011-10-15
Aging programmer? Huh? Well .. first fuck you and your pathetic little "blog". Second: C++ has been around since before 1990. Third: just about every feature you mention here has been available since the mid 90's.

Angel Ortega
2011-10-15
@Aging programmer: Hahaha! You are so funny!

Anonymous
2011-10-15
Thank you this is great!

Bob
2011-10-15
Haha, I write device firmware. I've been meaning to pick up C++. This just taught it to me in five minutes.

Porthia
2011-10-16
Aging programmer, if you disagree, it's OK; but when your first comment is an insult, you're only demonstrating being an asshole.

dubya
2011-10-16
In operator overloading, the "malloc(strlen(one) + strlen(two) + 2)" should have just "+1", as there is still only only null byte.

Does newer C++ have optional garbage collecting? The operator+ you define returns a string, which is super handy, but it's never free()'d. Or if you return an actual C++ string, is it free()'d when it goes out of scope or must that be explicit?

Jon Purdy
2011-10-16
The operator+ you define won’t compile because it doesn’t have a class or enum argument.

Angel Ortega
2011-10-16
@Jon Purdy: You're true, it's a typo. I'll fix it. Thank you.

Angel Ortega
2011-10-16
@dubya: certainly, one more byte will suffice. The example just tries to illustrate how to use operator overloading; if used as defined, you should have to free() the new string when you finish with it. It's just an example, not a styling guide. Thanks for your note, though.

An ageing programmer
2011-10-17
I met a very nice, highly intelligent, fellow some years ago who ran a successful business until, in his own words he "got the object sizes too large". A major contract was terminated without payment, his business went also house and so wife with kids. Playing with code is fine as long as you know the outcome in advance. With C you do, it will work and if it doesn't you can fix it.

Danny
2011-10-17
Nice, if brief, article.

"A function defined inside a struct is called a method"

No it's not. It's a member-function. This is not Java.

"These functions are like malloc() and free(), but when applied to objects, they also call constructors and destructors."

They do a lot more than that. They initialize the vtables required for polymorphism, create the base classes etc.

Never, ever use malloc() and free() with polymorphic classes.

Jerry
2011-10-17
Just what I wanted as an aging programmer, I'm writing some microprocessor code, but want to graduate to OO languages. The rude writer below should be condemned to writing Basic or machine code in all eternity

ddbug
2011-10-17
Nope, not good. All this one could easily get form any c++ textbook. A really useful c++ intro for old craps would IMHO start from highlighing subtle points where C++ departed from backward compatibility with C. Then, introduce STL and algorithms. Features such as templates, overloaded functions, default parameters could be explained from POV of gradual migration from c to c++ code. But thanks anyway.

WhiteAngel
2011-10-17
In C++ you also can define variable inside for loop (for example): for (int i = 0; i < 10; i++) ...

Angel Ortega
2011-10-17
@WhiteAngel: But you can also do this is plain C, can't you? I'm sure I've done it. Or is it a GCC extension?

Jerry
2011-10-17
Yes, you can declare a loop variable in C, not in ANSI-C as defined in the New Testament, but after C99. for (int i = 0; i < 10; i++)

WhiteAngel
2011-10-17
Exactly in gcc you are not allowed to do this.

Angel Ortega
2011-10-18
@WhiteAngel and @Jerry: thanks to both. I remember having seen this construction somewhere, don't remember where, but I'm pretty sure it was in a C code project. It seems it's C99, which surely took it from C++.

sl@sh
2011-10-18
Hello, As an experienced C++ programmer this article is not for me, but I had a look at it because I find it often hard to recommend good material to fellow C programmers trying to make the jump.

I like the to-the-point mentality of your article and how it sums up the most prominent differences, but I couldn't help noticing A few things:

1. The class rect_prism is a very bad example of object design. It shouldn't inherit from rectangle, instead it should have a rectangle as an attribute. The simplified rule is to decide whether a relation expresses 'is a' or 'has a'. The former suggests inheritance, the latter composition.

The class square is just fine in that respect: a square 'is a' (specialized) rectangle. Since this article isn't about object design, you might just kick the prism. Or you leave it as an example of bad design, to show the reader when to use inheritance, and when not.

2. Your example of operator overloading is explicitely forbidden! You can only overload operators that have at least one user-defined parameter. Pointers are not user-defined! (consider the consequences if you could define an operator+(char*,int) : it would silently break a lot of code using pointer arithmetic!)

3. Regarding references: your example may compile on older compilers, but not those adhering to the newest standard! Local variables of type reference do have to be const! This is a subtle difference to function parameters that are passed by reference.

4. Since you mention new and delete, please do mention new[] and delete[] as well! This is a common area of misunderstanding and source of many memory errors and leaks!

There may be better and more detailled guides, but none that I know can be digested in about half an hour - and that is in my experience the biggest hurdle to get someone started! Thanks for your effort.

sl@sh
2011-10-18
@Danny: Regarding your comment about the term 'method': Both the author and you are correct! Every function *associated* with a class (or struct) is a method. In Java that equates all class functions. In C++ it equates all class functions, plus all global functions that are somehow associated with a class, e. g. operators that take a class as a parameter.

So, while the author didn't quite give the exact definition of the term 'method', he didn't say anything wrong either. :-)

Angel Ortega
2011-10-18
@sl@sh: Regarding the "method" definition, you're true, I've heard it also applied to C++ code, not only Java (which I don't know a word of nor I'm interested) as @Danny says. Anyway, I just mentioned the term in my article because C programmers will hear it and probably don't know what they are. I personally call it "object functions", which is kludgy but more to the point.

And, yes, my operator overloading example and other ones are awful and not very orthodox, but they are what they are: examples for a first, air-view of C++ for people accustomed to C. They are not examples of good C++ practice (though they may inspire otherwise bad practices, by the way, as some readers complained).

Your comments are very valuable. Thank you.

sl@sh
2011-10-18
@Ángel Ortega: 'object functions' isn't cludgy at all, in fact I find it's very concise. But as you said, it's helpful to stick with the lingo generally associated with modern C++ programming, especially since many helpful articles do use them.

I've learned C++ at a time when there were no compilers for templates yet, and those that existed had a precompiler that translated C++ classes into C code. Terms like 'method', 'getter' or 'setter' weren't coined yet, much less the terminology of modern object design, such as the common design patterns by Gamma et al. I've found that it was sometimes incredibly hard for me to understand young people who've learned all of this at university - not because I wouldn't know of any of the things they talk about, just because I was not familiar with the terminology!

It was frightening at times, because I thought they've learned stuff that I should know about, when in truth it was just a basic methodology I've been using for decades! I've gotten better after I started looking up these terms. Wikipedia is your friend!

Maybe that is also something you cannot stress enough: learn the terminology! Nothing is more frustrating than the feeling you have no inkling about what others are talking about, when in truth you just don't understand a few terms.

Angel Ortega
2011-10-18
@sl@sh: You're absolutely right.

Qt
2011-10-18
I strugled with C++ for a while, because my teacher mixed C and C++ concepts. This is cruel challenge to your brain and bad, really bad code as a result. First forget everything you lernead about C. Real C++ is diferent beast entirely, whatever the name implies. Learn RAII(Resource Acquisition Is Initialization) programing idiom. Forget dynamic allocation, naked pointers, C strings, and arrays, you need that only in specialized situations. Those things are roots of common bugs. You must embrace std:string's, STL containers and smart pointers. Personally I really understod OOP only after reading Bjarne Stroustrup's "The C++ Programming Language". Hope it helps somebody, and sorry for my horible english.

Ángel Ortega
2011-10-18
@Qt: Your english is fine. Thanks for your contribution.

Michael
2011-10-18
By reading this from the bottom up, you can learn C from C++!

Whoever
2011-10-20
Such language is helpful, but I don't very much like the condescending feel of it. In other words you're being pretty clear but you sound arrogant. The exception bit is a ridiculous simplification that doesn't help people at all to get the point. So Good Luck!

Luc Pattyn
2011-10-24
It is very dangerous to write these types of articles, because it is misleading.. C and C++ are two entirely different languages concept-wise, and it is not wise to point out some simple techniques to port your C code to C++, novice programmers may get even more misleaded regarding the OOP concepts

Derek Hunter
2011-12-07
I have written millions of lines of C code in my thirty five years of programming and have several C++ books on my shelf (including a 1985 edition of Stroustrup's "The C++ programming language") that I have barely looked at.

This article was exactly what I needed (as an experienced C programmer) to put a C wrapper around a C++ library that I unfortunately needed to use.

Thank you.