Defaults and Deletes tend to be needlessly confusing for new C++ developers. Trying to learn this topic is why so many put on a diaper and go code Python. Hopefully this example series and discussion will allow you to man up. This is a continuation of my C++ series to assist junior/new developers so they can do something meaningful with their lives. No, you can’t use Python for a medical device.
Remember:
In the real world of IT, if you need a language to manage memory for you then you aren’t really a programmer.
Crippled by Baby Steps
Too many students and junior programmers don’t bother to learn the history of the language. It clearly explains why things are kind of hodge-podgy when it comes to classes and OOP in C++. Your source of confusion is because Bjarne Stroustrup didn’t want to write a compiler while creating C++. He can bitch at me all he wants for saying that, but it’s the truth. The name, C++, didn’t exist initially. C With Classes was the early name. To add objects to C you used the Cfront “compiler.” This wasn’t even a compiler, it was a translator which generated C code and then utilized the C compiler.
It is difficult for you coming to this in 2024 to imagine the birth of the software industry. I was there when DEC Midrange computers ruled and when dual floppy x86 based desktop computers cost around $5K. The brutal change-over from CP/M to DOS happened at this time as well. I entered the world of IT in 1982 and the Cfront “compiler” came out in 1983. There is a slightly different history found here taking C With Classes back to 1979 and the release of Cfront 1.0 in 1985. We can all agree that Bjarne didn’t just poop the C With Classes specification and compiler out in a single day and not quibble over these dates.
Cfront Crippled by Using C++
As much as that will confuse you, it is true.
DEC Midrange systems had BASIC, COBOL, FORTRAN, and DIBOL in their order of popularity at the time. There were other languages that came along, but the PDP line which came before the VAX almost always came with BASIC. Yes, the MACRO-32 Assembly language was also available. With the exception of MACRO-32, none of the compilers were “free.”
You will recall that Dennis Ritchie created the C programming language and the first version of UNIX on a DEC PDP computer in the 1970s. He also wrote and published The C Programming Language with 1978 as its first publication date.
While BASIC on the PDP under RSTS/E would have made string parsing a dream, it did not exist on UNIX. There he had Assembler, C, and B. I do not know for certain what the early code of Cfront was written in, but will wager it was not Assembler. String handling in C couldn’t hold a candle to BASIC. It was brutal yet better than FORTRAN IV and some other languages. BASIC and C were the only two early languages spread across the industry featuring dynamic strings. Sadly, BASIC had a different flavor on every system. You couldn’t transfer code between machines.
Everybody Made This Mistake
Once constructors and destructors with implicit defaults and enough of “the language” was functional the Cfront “compiler” made the move to being written in C With Classes. This made porting Cfront to other platforms a real PITA. Even if someone gave you the source you couldn’t compile it without a C With Classes compiler. You had to find the last version written with the previous language and a compiler for said language, then start compiling each previous iteration of the compiler. You couldn’t just jump to the end because new features were being added along the way.
Once the PASCAL language definition got far enough along the PASCAL compiler was written in PASCAL as well. This was the academics “proof of language” ideology.
If your language is valid you should be able to write a compiler in your language for said language.
Proof of language ideology
There was a belief in the industry that if you had only one language it should be able to do everything. Today most script-kidee languages have a VM originally written in C. Why? C was the only portable Assembler.
Defaults
Please allow me to show you one class created for this example program.
/*
* implicitclass.h
*
* Created on: Feb 1, 2024
* Author: roland
*/
#ifndef IMPLICITCLASS_H_
#define IMPLICITCLASS_H_
#include <string>
/*
* Our ImplicitClass is basically a struct. It has a compiler generated
* constructor and destructor. std::string provides its own constructors
* and destructor.
*/
class ImplicitClass
{
public:
int fred {25};
double ethyl {3.14159}; // yes, I used PI
std::string statusStr {"Married"};
};
#endif /* IMPLICITCLASS_H_ */
Since I used Eclipse and created this within a project it also created a cpp file.
/*
* implicitclass.cpp
*
* Created on: Feb 1, 2024
* Author: roland
*/
#include "implicitclass.h"
This isn’t the final program but you probably need to see this much now.
#include <iostream>
#include "config.h"
#include "implicitclass.h"
void dump_implicit( std::string label, ImplicitClass &ic)
{
std::cout << label << " fred: " << ic.fred << " ethyl: " << ic.ethyl
<< " status: " << ic.statusStr << std::endl;
}
int main(int argc, char **argv) {
std::cout << "DefaultsAndDeletes" << std::endl;
std::cout << "Version " << DefaultsAndDeletes_VERSION_MAJOR << "." << DefaultsAndDeletes_VERSION_MINOR << std::endl;
ImplicitClass lucy;
dump_implicit( "lucy", lucy);
ImplicitClass young {8, 9.6, "kids next door"};
dump_implicit( "young", young);
ImplicitClass ricki {lucy};
lucy = young; // be young again
dump_implicit( "second lucy", lucy);
dump_implicit( "ricky", ricki);
return (0);
}
Running it shows this
DefaultsAndDeletes
Version 0.1
lucy fred: 25 ethyl: 3.14159 status: Married
young fred: 8 ethyl: 9.6 status: kids next door
second lucy fred: 8 ethyl: 9.6 status: kids next door
ricky fred: 25 ethyl: 3.14159 status: Married
Yes, when I was writing this something came on about The Lucy Show or some iteration of it. Those who have done any amount of C programming will note that our class is little more than a struct.
struct ImplicitStruct
{
int fred;
double ethyl;
std::string statusStr;
};
The Cfront Legacy
Make no mistake, I program in C++ a lot, it’s a great language. It gives you assembly level access for embedded systems hardware. Because it was originally C With Classes instead of a ground up OOP (Object Oriented Programming) language, it has some, for the time, necessary warts and tumors. Classes were being bolted onto C and C had a struct. One of the first things the classes had to have were implicit constructors and destructors. The compiler had to know enough to generate defaults for these.
Destructors weren’t so bad. C already had the ability to free the memory of a struct . . . as long as it didn’t have pointers to dynamically allocated memory.
Seven Features Required for Pure OOP Language
The idea of OOP was really just percolating when C With Classes was being created. Ultimately we got to the list of seven:
- Encapsulation/Data Hiding
- Inheritance
- Polymorphism
- Abstraction
- All predefined types are objects
- All operations are performed by sending messages to objects
- All user defined types are objects
No pure OOP language can have “native” data types supported directly via hardware. Integer, float, double, memory address, etc. must all be objects. Pure OOP is worthless as a portable assembly language. Very few Pure OOP languages compile to a native binary. They usually run inside of a VM either interpreted or P-compiled to byte code like Java (which is not pure OOP).
Smalltalk and Effel programming languages were considered to be “pure OOP” and created during the 1980s. Visit simplyhired.com and search for just how many jobs there are for those programmers today. Now search for C++ jobs. Enough said about those two languages. Java will soon follow them into the dusts of time. Already Java is in massive decline.
Default Constructors
There are two lines of code which may well shock noobs.
ImplicitClass young {8, 9.6, "kids next door"};
ImplicitClass ricki {lucy};
Admittedly life using the C++17 standard has gotten better. Our ImplicitClass can have its default values established in the header. Unlike a C struct, we don’t have to worry about uninitialized member variables. What about the above two lines? How did they work?
Easy. Defaults are created for the copy and full initialization constructor while creating a general “default” constructor without parameters. I did not test, but, unless your compilation environment turns off implicit conversions entirely, the 9.6 and 8 parameters could be swapped and we would just get different results.
For a totally implicit class like this, compiled using the C++17 standard, you get the following defaults:
ImplicitClass(); // default constructor
ImplicitClass( const ImplicitClass& other); // copy constructor
ImplicitClass( all member variables in order of declaration); // full data constructor
operator =() // assignment operator
You do not get default equality/comparison operators.