Hello, and welcome to "C++ Weekly."
I'm your host, Jason Turner.
I'm available for code reviews, contracting,
and on-site training.
In this episode, I am going to tell you everything that you
really need to know about C++98.
These are the most important features of the language
and the things that really stand out and make
it different from other programming languages.
This is specific to C++98 and basically '03.
But most people don't talk about '03.
And then I will have later episodes for '11, '14,
'17, et cetera.
Obviously, the first thing that we need to mention when I say
C++98, what does that even mean, or C++03?
Now, I am using the compiler explorer.
This is at godbolt.org.
So the very first thing that we have is that C++ is
an international standard.
It is a standard that is agreed to
by an international standards organization ISO.
And every time there's a change to the language that
becomes an official part of the standard,
it has to be voted on, and then papers are written,
and committees decide things.
And you're never going to see a change in the C++ standard that
is taken without a lot of consideration and discussion.
You might not always agree with the changes that they made,
but there was always a lot of thought and effort
that was put into them.
So C++98 was standardized in 1998.
That's not very surprising.
C++03 was standardized in 2003.
Now, most people when they say C++98,
they probably actually really mean C++03 because there's just
a few little tweaks to the languages.
I don't believe that there's, well,
very many people who are actually stuck in a fully C++98
compatible compiler.
You're probably, if you're doing something pre-C++11,
it's probably C++03.
Now, the next thing that sets C++ apart is deterministic
object lifetime.
This is different from most other programming languages.
There are a few that are similar to C++ in this regard.
But it comes down to something like this
that I can demonstrate.
So I'm going to show what deterministic object
lifetime really means.
I've created a string on the stack here called S.
I know because this is C++ that S is created here.
And I know that S is destroyed here.
We can rely on this.
The order of construction is guaranteed.
The order of destruction is reverse
the order of construction because I created S first,
and then I created S2.
I know that S2 is going to be destroyed,
and then S is going to be destroyed.
And this is something we can rely on when working with C++.
And they're going to be destroyed
at the end of a particular scope.
So I can create an explicit scope here like this.
And now outside of this scope, if I
wanted to do something with one of these variables,
that's impossible.
Its object has ended, its lexical scope has ended,
this object is absolutely gone.
And building on this, one thing that we really like in C++ is
we can create this file, we can do something with it,
we can write to it.
And we know when the enclosing scope
ends that that file is going to be closed.
Its data is going to be written.
These are things that we, again can rely on in C++.
So this is going to continue to fail to compile here
because it's supposed to.
So deterministic object lifetime,
this is also known as RAII.
This is Resource Acquisition Is Initialization.
It is a terrible name, but an amazing concept.
It helps us to write very clean, well-maintainable code,
and really be able to reason about what
our program is doing.
Now, a part of what makes deterministic object lifetime
powerful is destructors.
And this is really--
RAII is not possible without destructors.
So we can create our own types, and they
behave like any built-in type or any type that's
created for us by the standard.
Now, I will point out immediately
that the code that I am writing here
is terribly flawed for many reasons
that I'm not going to go into at the moment.
But I'm going to use this just as a tool
to demonstrate what RAII is about.
So I can create this thing called obj of my type.
And the constructor is going to be called
when the object is constructed.
That it's going to allocate an int on the heap with new.
And then right here when it goes out of scope,
I know that this destructor is going to be called.
And since we're using the compiler explorer,
we can directly look at what it is doing here in fact.
So we see this highlighted pink bit here
is corresponding to this call to operator
new one lines three and four of the disassembly.
And this is our function, myTypeTest.
And then it sets up what it needs to know for the call
to the destructor.
And in fact, we can see the destructor is called.
It has been end lined, and we get this delete right here.
Now, if I were to take back the level of optimization
a little bit to zero, I am going to see
that the constructor and destructor do not
get end lined.
But it becomes quite a bit more difficult to read at this point
because we've got all of the other files, code,
and whatever in here.
So we can right click on anything and the compiler
explore and say, go to the linked code.
So this is the constructor.
The destructor's going to be after it.
Let's go ahead and scroll here.
So in the function call to myTypeTest,
it calls the constructor and it calls the destructor
exactly where we would expect it to when
the scope of the function exits.
So destructors make the other tools possible.
Now at this moment.
I'll take a moment to say in modern C++ today,
if you are writing a destructor, you're probably doing something
more complicated than you need to.
In C++11.
We have many tools that help us automatically
deal with a lifetime of objects, and we should almost never need
to write our own destructors.
So just keep that in mind.
But this is C++98 we're talking about.
Everything that I have done so far is just straight C++98.
And now the final thing that sets C++ apart is its
templates.
We have an incredible ability to write very generic code in C++.
So I can create a pair.
And if I want a pair that is an int, another int,
it's going to look something like this.
Well, what if I want a pair of doubles,
or a pair of an int and a double?
And this starts to become just a lot more code
than it needs to be.
This is an extraordinarily high level overview of what
templates are in C++.
But this is one of the most powerful tools that we have.
This gives us things like the Standard Template Library.
So I can create a pair that is an int and a double.
And I've got this awesome tool available to me.
As I just mentioned the Standard Template Library,
let's just start with vector.
It might not be immediately obvious here,
but this pair of an int and a double
is exactly as efficient as if I had created
a pair of an int and a double.
There is no boxing an unboxing overhead here as there might be
in languages like Java or C# or something like that.
This is-- you're asking the compiler right here,
please generate a new type for me that is exactly
as if I had created my own pair that
contains an int and a double.
And that becomes, again, very powerful if we
want to do something like a vector of integers.
That's awfully handy.
Now we have a standard library built
in way of operating on a collection of things.
I can create a vector of doubles.
I can create a vector of pairs of ints doubles.
Our types work and behave exactly
as if they had been types that we'd
been provided to us by the standard library,
very powerful here, very high performance options.
It does add some compile time overhead.
But if you're programming in C++,
it's probably something you're willing to live with.
Now, templates also make possible things
like the ability to apply algorithms to them.
So the standard library and the standard algorithms set
lets us do things like--
although, I must admit that I just got this not 100% right
because the ability to call begin and end as a free
function, that was added in C++.
But in C++98, we're not too far off here.
So this, again, all C++98, standard algorithms,
standard containers, it's all very powerful,
these four things that I have listed here.
The fact that it is a standard, we have deterministic object
lifetime, destructors, and templates.
And templates, this is--
templates include the STL, and algorithms, containers,
et cetera.
These all fall out of the ability to have templates.
So we should probably do a quick overview of that.
If you are not familiar with cppreference.com,
I suggest that you become familiar with it.
It is an outstanding resource for these things.
So we can click here on our algorithms library.
And we can see what are the algorithms?
How do they operate?
We can see what standard they were added in.
As you can see, many of them were added before C++11,
and it continues to grow.
We've got them added in '11 '14, '17, '20, et cetera.
Similarly, if we click here on the Containers library,
we've got vectors, deck, lists, sets, maps,
all of these things, many of them added before C++11 again.
So use these containers with the algorithms with your custom
declared types and take advantage of this deterministic
object lifetime, and you'll have a really good grasp on really
what sets C++ apart from other languages and what are the key
features of C++98 that you need to be paying attention to.
So thank you for watching this episode of "C++ Weekly."
I hope you enjoyed it.
Be sure to subscribe.
I will have more episodes similar to this coming
over the next several weeks.