Different people have different ideas of what is simple.
Contents
Interesting Things And Helpful Resources
-
A nice C++ resource, Halloween themed for some odd reason.
-
These samples of Cpp look super useful.
-
The ROS Cpp Style Guide seems sensible.
-
Need an online execution environment? Here’s one.
Various perspectives and opinions about Cpp.
-
The Grand C++ Error Explosion Competition is good stuff.
-
Linus Torvalds does not like Cpp.
Versions
As C was apparently not Good Enough, C++ developers lack the temperament to leave things well-enough alone. They’re constantly re-imagining the language as broken and trying to fix it. Every couple of years there is a major change in how things are done.
Wikipedia’s enumeration of C++ versions is ok. Here is a list of more exotic species. Here is a whole history of the genus. Here is my little catalog.
C++98
AKA - ISO/IEC 14882:1998
-
RTTI (dynamic_cast typeid)
-
covariant return types
-
cast operators
-
mutable
-
bool
-
declarations in conditions
-
template instantiations
-
member templates
-
export
-
locales
-
bitset
-
valarray
-
auto_ptr (now deprecated)
-
templatized string
-
iostream
-
complex
-
algorithms
-
iterators
-
function objects
C++03
AKA - ISO/IEC 14882:2003
Mostly bug fixes and subtle implementation standardization details.
-
Value initialization
C++11
-
AKA - ISO/IEC 14882:2011
-
AKA - C++0x
C++14
-
ISO/IEC 14882:2014
-
C++1y
-
Shared mutexes and locking
-
Heterogeneous lookup in associative containers
-
Standard user-defined literals
-
Tuple addressing via type
-
Smaller library features
C++17
Published: 2017-12-01
Here is the latest official C++ 2017 Standard - 1622 pages!
-
ISO/IEC 14882:2017
-
C++1z
-
optional (probably not optional for this version!)
C++20
As one might expect, this one is still being worked on in 2019.
-
concepts - Bjarne himself explains "Compile time predicates…do you have the properties I need?" Relating to things like template parameters to specify properties that must exist for a template to work.
File Extensions
What should we call our files containing C++ code?
-
foo.c
- Classic unplusplused C. Nice and simple. -
foo.c++
- It’s been tried, but no. Never do this. -
foo.C
- Popular in the early days with Unix people — which of course means: not popular. -
foo.cc
- I started with this and it seems fine. Short and clear. But it has slipped into relative obscurity. That’s fine but I’m more concerned with confusion over variables like$(CC)
(frommake
) that do not have anything to do with C++. Inmake
, "CC" is "C Compiler" or possibly "Compiler Collection" depending on your historical period. -
foo.cpp
- This is the obvious choice for the majority of people because it is the common preference of a popular tool chain in a regrettable OS. I have used this convention even though impoverished OSes do not concern me. But a better reason to avoid it might be the confusion withmake
variables like$(CPP)
which also has nothing to do with C++; that stands for "C Pre Processor" and I think that is fair to respect and avoid any confusion with. -
foo.cxx
- That leaves this weird one. I previously avoided it as contrived, cutesy, and unnecessary, however make variables once again provide clear guidance. The variable$(CXX)
is the variable for the C++ compiler, defaulting tog++
. With that in mind this seems like the clear choice. -
foo.h
- Unless the header contains specific C++ syntax, I’d continue to use this for headers. Normallyfoo.hpp
is seen in the wild butfoo.hxx
seems valid too.
Preprocessor
Tag brackets mean look for this file in the directory where all the standard libraries are stored.
#include <iostream>
Double quotes mean the same as tag brackets or the current directory if the specified file is found in it.
#include "main.hpp"
Namespace
The following line is an example of how kludgey C++ can be:
using namespace std;
I find this command to be awkward. It is cryptic, not part of your
train of thought when trying to get something to work and,
unfortunately, hardly ever unnecessary. This line simply must be
included to activate the correct name space for certain important
libraries. There may be others but <iostream>
and <string>
are
definitely some that need this command.
Without it you need something like this.
std::cout<<"This is suboptimal.";
Although this is an annoying requirement because most of the time it is used mechanically in the case shown above, the namespace mechanism is a C++ feature that allows better organization and protection of program elements.
Starting with C++2011 the using
keyword can have a different
meaning. It is used to set an "alias". As far as I can tell this works
much like a typedef
. Maybe something like this.
typedef std::map<std::string,int> mymaptype;
using mymaptype= std::map<std::string,int>; // Same thing basically.
Apparently it’s better to go with the second, newer, style. One reason
is that typedef
doesn’t handle templatization well. There may be
other reasons but it is at least good to know what you’re looking at
when you find code like this.
Comment Styling
In C, the comments are this bizarre syntax:
/* This is a classic C style comment. */
In C++ that is acceptable but there is another style that has a starting designator and runs to the end of the line. Like this:
// This is a C++ comment.
x++ // The beginning of this line is not a comment but this is.
Style
For style tips in general, you could do worse than to emulate these guys. (Do they not host it themselves anymore?)
Here’s another.
Note that OpenCV also has
a style
guide. They prefer names ending in .cpp
and .hpp
. And 4 spaces,
no tabs.
Variable Types
As with C, all variables must be explicitly declared before using. The point of this relates to C and C++ and their very explicit use of memory management. Note the acronym RAII stands for Resource Acquisition Is Initialization. RAII guarantees that objects are ready to go if they’ve been initialized. I think this is in contrast to C’s ability to make pointers to hypothetical things that may want a lot of resources later but come up short.
Constants
Like C, make a variable immutable with the const
keyword.
Hmm, a "read-only variable", like the C++ error messages say, is an
oxymoron, right? Still, it is a part of the C++ religion to
pray to the const
gods at every opportunity.
This is simple in the simple cases. But there is
all kinds
of mischief that can arise from this. The most absurd to me is the
mutable
keyword which can make const
declarations, uh, less const.
Wait a moment… This just in… I’ve received word that C++11
has introduced a new keyword into the language — don’t act all
surprised — the word is constexpr
. As far as I can tell,
const
can be assigned a value (that thence remains constant) at
runtime, but constexpr
is carved in stone at compile time. I
would imagine that things like this are a teensy bit more resource
efficient.
constexpr double PI= 3.14159265358979323846;
The normal const
could be applied to things like this.
int main(const int argc, char** argv){ ...
Here the number of arguments can be different depending on how the
program is executed, but once the value is set, argc
will be locked
for the duration of the execution.
It turns out that constexpr
can be used with functions too. This is
limited to simply return
(maybe with some awful
ternary
operator tricks). But C++14 expands the potential of
constexpr
functions. I’m thinking of these like old C preprocessor
macros that do simple things during compilation so that the
precomputed answers are injected into to the source code. But
C++ tries hard to bring that sort of thing into the language
proper. The limitation with constexpr
functions is that all
arguments must also be constexpr
for it to be effective. If they are
not, it just pushes it out to normal runtime execution. Another fact
to understand that helps explain it, is that such pre-computations at
compile time will likely make the compiling process take longer.
There is also a C++ specifier called volatile
which can be
used in very rare situations where you explicitly want the compiler to
refrain from performing any optimizations on this "special" memory’s
operations.
Type Details
Note
|
Actually in some situations the Cpp11 auto keyword (not to be
confused with the K&R C auto
compiler
hinting) allows the types of variables to be automatically deduced
from context. Some
more information
and examples. |
bool Exclusive to C++! |
Boolean |
1byte |
keywords |
char |
Character or small integer. |
1byte |
signed: -128 to 127 unsigned: 0 to 255 |
short int (short) |
Short Integer. |
2bytes |
signed: -32768 to 32767 unsigned: 0 to 65535 |
int |
Integer. |
4bytes |
signed: -2147483648 to 2147483647 unsigned: 0 to 4294967295 |
long int (long) |
Long integer. |
4bytes |
signed: -2147483648 to 2147483647 unsigned: 0 to 4294967295 |
float |
Floating point number. |
4bytes |
+/- 3.4e +/- 38 (\~7 digits) |
double |
Double precision float |
8bytes |
+/- 1.7e +/- 308 (\~15 digits) |
long double |
Long double float. |
8bytes |
+/- 1.7e +/- 308 (\~15 digits) |
wchar_t |
Wide character. |
2 / 4 bytes |
1 wide character |
Note that C++ is more strict about type casting than C.
Variable definitions in C generally must be at the beginning of a block whereas in C++ they can be anywhere in the block.
C++ provides a way to get a runtime check of what kind of type a
variable is. This is done with the typedef
function and is useful
when variables can be any of several types or objects.
Enum
C enum
values are always sized int
whereas in C++ they are a
distinct type and can have different sized values. This defines a
new user type DOW
.
enum DOW {Sun,Mon,Tue,Wed,Thu,Fri,Sat};
You can do a lot in the definition. Here the type declares a variable
(casual
) and sets it.
enum DOW {Sun,Mon,Tue,Wed,Thu,Fri,Sat} casual=Fri;
Some people believe that enums should be named as singular nouns and not plural. Although enums may define a collection of possibilities for, say, problem_types, the possibility will just be a problem_type when actually used.
Enum Scoping
In the old C++98 days if you defined an enum and then wanted to
use one of its member’s names for something else, that was a problem.
For example if you had already defined a DOW
enum as above, and then
wanted this, that would lead to confusion.
auto Wed= Wedding();
The solution is to create a "class enum" or, as it is known by its less obvious but more correct name, "scoped enum".
enum class DOW {Sun,Mon,Tue,Wed,Thu,Fri,Sat};
Now you can have a star named Sun which won’t get confused with the
day of the week enum member. Of course you need to refer to the enum
with something like sleep_in(DOW::Sun)
to prevent naming conflicts.
Literals
I was astounded to see some time calculations in C++ (14+)
that involved numbers with time units, stuff like 8m+43s
. This is
accomplished with the std::literals
namespace.
-
chrono_literals - 5d+3h+10m+18s style of time units.
-
complex_literals - 3+4i style of complex numbers.
-
string_literals - String decorating for explicit behavior.
Note the fun and confusion that could arise from something like this.
x= 10s // std::literals::chrono ten seconds
y= "10"s // std::literals::string the string - one, zero.
Oh, and don’t get confused by pair.second
which is the second member
variable (not function) of a pair or map.
This is part of C++11’s user-defined literals. They’re really
not too far different from things like 0xCEl
(a long) or 99u
(an unsigned int). Let’s say you had an Altitude class and wanted to
be able to set its object in feet, you might use something like this.
Altitude operator ""_ft(signed long n) { return Altitude{n, Altitude::FEET}; }
Then you could say stuff like.
auto tower= 50_ft
auto mountain1= 2732_ft + tower
I didn’t exactly check all of that but it is something like that.
Also note that C++14 started permitting the very continental European practice of sticking apostrophe’s where Americans put commas in long numbers. Apparently stuff like this is now legal.
constexpr auto USpop= 329'395'329;
Implicit Conversions
-
integer to float = OK
-
char to float = OK
-
float to int = OK (truncated obviously) John Carmack suggests to consider using rint().
-
float to char = silently does the wrong thing
Strings
Classic C Style - cstring
Because C++ is based on C, the spartan techniques of old school C work in C++ too. Note that as in C, double quotes signify strings and single quotes are characters.
#include <iostream> using namespace std; int main(){ char my_char_array[4]= {'a', 'b', 'c','\0'}; cout << my_char_array << endl; char unterminated_rubbish[3]= {'x', 'y', 'z'}; cout << unterminated_rubbish << endl; char fixed_len_msg[]= "Really just an array of simple chars."; cout << fixed_len_msg << endl; return 0; }
Note that in the example that unterminated_rubbish
is just that.
With no termination, functions like cout
just keep reading memory
until it finds a null. It can pick up all kinds of wacky stuff. In my
example I found that it printed the abc string too until it found its
termination byte.
abc
xyzabc
Really just an array of simple chars.
So think carefully about using this style of C character array strings
with C++ functions. However the cstring
library can be used with
this kind of string and it does work fine if done carefully.
#include <iostream> #include <cstring> using namespace std; int main(){ char fixed_len_msg[13]= "Ok C string."; char msg_copy[13]; strcpy(msg_copy,fixed_len_msg); cout << fixed_len_msg << endl; cout << strlen(fixed_len_msg) << endl; cout << strcmp(msg_copy,fixed_len_msg) << endl; cout << msg_copy << endl; cout << strcat(msg_copy,fixed_len_msg) << endl; cout << strcmp(msg_copy,fixed_len_msg) << endl; // <0 if A<B, >0 if A>B cout << *strchr(msg_copy,'C') << endl; // returns ptr to pos of char 'C'. cout << *strstr(msg_copy,"Ok") << endl; // returns ptr to pos of string "Ok". printf("The length is: %04d\n",strlen(msg_copy)); return 0; }
That code produced this output.
Ok C string.
12
0
Ok C string.
Ok C string.Ok C string.
-28
C
O
The length is: 0024
string Class
In C++ the string
class is a more elaborate mechanism for working
with strings. Strings are generally used like this:
#include <iostream> #include <string> using namespace std; int main(){ string mystring("Defined in constructor."); string somedots(4,'.'); cout << mystring << endl; mystring= "Redefined by assignment."; cout << mystring << somedots << endl; cout << somedots + "and concatenation." << endl; cout << mystring.size() << endl; return 0; }
Produces this.
Defined in constructor.
Redefined by assignment.....
....and concatenation.
24
Note that because it is a class (string
) it can be
initialized like a new object as an initialization function parameter.
Subsequent redefinitions use the overloaded =
operator to simulate
the way normal people would expect a programming language to work.
Member
functions for string
class include:
-
S.length()
-
S.size()
- seems identical toS.length()
-
S.empty()
- 0 if not empty and a 1 (or maybe some other nonzero) when it is indeed empty. -
S.clear()
- force empty() to return true by erasing any previous content. -
S.append("suffix")
- A lot like+
. -
S.assign()
-
S.replace(change_position,change_length,replacement_string)
-
S.find(str2,pos)
- Find the first occurrence of an entire second string’s position within string S. Thepos
parameter is to start looking only at or after a certain position; 0 is search all. -
S.rfind()
- Like find but finding the last occurrence. Andpos
is the maximum character to search. A value greater than or equal toS.length()
searches the whole thing. "r" is reverse in these functions. -
S.find_first_of()
- Searches a string S for any characters supplied. Position limits are likefind()
. -
S.find_last_of()
- Same asfind_first_of()
but the likerfind()
. Seerbegin()
below. -
S.find_first_not_of()
- Invert the match checking offind_first_of
. -
S.find_last_not_of()
- Inverted matching offind_last_of
. -
S.compare()
-
S.erase()
- Erases particular characters specified by 1. position and length, 2. iterator position, or 3. starting and ending iterators. -
S.back()
- Access the last character. -
S.push_back(char)
- Append a character. -
S.reserve()
- Request a change in capacity. If you’re looking for how to reverse a string, see below. -
S.substr(position,length)
- Returns a new extracted string. -
S.stTYPE
- Type conversion functions where type is i (int), ui (unsigned), ll (long long), ull, f (float), d (double), ld
For find operations, if the target is not found, it will return
string::npos
which on my system produced 18446744073709551615
(2^64-1). Checking the return value for this is how you check if
something was found.
Repeated Characters
Need a long line of dashes for something? Just create a string with an integer first argument. Here is some content between two lines of 20 dashes.
string(20,'-') << endl << "Content" << endl << string(20,'-')
Casting To Strings
There is a function called to_string(non_string)
that makes strings
out of many things, numbers mostly. One thing I found odd was that it
did not seem to have a prototype that accepted char
types. You’d
think that would be pretty easy. But the answer to that riddle was
just covered in the Repeated Characters section above. Just use
string(1,mychar)
.
Truncating Strings
This is rumored to only work in C++11. Here is how to drop the end N characters in a string. For example, N=4 to get rid of the file extension such as ".txt" in a filename.
for (int i=0;i<N;i++) string_object.pop_back();
Reversing Strings
Or the equivalent to piping to the Unix rev
command. Just define a
new string with reverse iterators from the old one.
string backwards(x.rbegin(),x.rend());
Splitting Strings
To show a good example of strings in action, here is a
C++ technique to achieve the functionality of Python’s
split()
function or the Unix cut
command.
#include <iostream> #include <sstream> #include <vector> using namespace std; vector<string> split(string s,char sep){ // Split string s on separator sep. vector<string> parts; string part; stringstream streamed_s; streamed_s.str(s); while (getline(streamed_s,part,sep)){ parts.push_back(part); } return parts; } int main(int argc, char **argv){ string teststring= "+11_0.22_-33_4.4"; vector<string> nums; nums= split(teststring,'_'); for (vector<string>::const_iterator i= nums.begin(); i != nums.end(); ++i) cout << *i << endl; return 0; }
Replacing Strings
Here’s a pretty effective way to do simple string substitutions. This replaces globally in the string, not just first. You can also replace with "" and this works as a character deleter too.
string text_replace(string& s, const string& bad, const string& good){
size_t p= 0;
while( (p= s.find(bad, p)) != string::npos) {
s.replace(p, bad.length(), good);
p += good.length(); }
return s; }
// Example use to get rid of evil tabs.
line= text_replace(line, "\t"," ");
Fancy Strings For People Using Weird Letters
Apparently C++11 introduced the
char16_t which is
a double byte char
(the ordinary stuff a classic std::string
is
made of).
char c{ 'a' }; char cu8{ u8'a' }; // cu8 == c wchar_t wc{ L'a' }; char16_t c16{ u'a' }; char32_t c32{ U'a' };
Initialization and Definition
When I started programming with C++ in ancient times, if you
wanted x to be 3 you said int x=3;
sort of like you do with classic C.
The problem with this in C++ is that it can be slightly
ambiguous, or at least inconsistent. Note how I’m basically using a
couple of ways to simply define things in the following code.
#include <iostream> using namespace std; class Fancy { public: Fancy(){x=0;} Fancy(int N){x=N;} Fancy operator=(int X) { x= X; return *this; } private: int x; }; int main(){ Fancy f(99); f= 33; Fancy g= Fancy(100); return 0; }
Also note that if you tried to initialize a special type with an empty
argument list like Fancy f();
you’d have basically just declared a
function. Today with C++11 and beyond there is a thing called
"uniform initialization". This uses braces and although it has its
problems, it is a bit more consistent.
This would basically look like this:
Fancy h{101};
It can also be used to initialize more complex objects.
std::vector<int> v{9,8,7};
Input And Output
C uses scanf
and printf
for input and output. C++ can use these
but idiomatic C++ programs use a different mechanism not available
in C.
cin>>
cout<<
These are available from include #iostream
. You probably also want
to turn on using namespace std;
to cut down on trivial scope chatter.
These are really not exactly functions but more like named pipes (in
the std
namespace) which get data sent in and out of them (<<
and
>>
are overloaded operators normally used for bit shifting).
The >>
operator is the "extraction" operator and reads input "items"
which are expected to be delimited by whitespace (space, newline,
tab).
if (fileStream.is_open()) while (fileStream.good()) { string word; fileStream >> word; // Do something with "word" }
Here is a complete output ("insertion" operator) example:
#include <string> #include <iostream> using namespace std; int main () { string thestr("String objects can be output like so."); cout << thestr << endl; return 0; }
The cout
doesn’t just dump a \n
; it also performs cout.flush()
.
Here are some good notes on C++ IO.
Standard Error - cerr
Sometimes you want normal output to go to the standard place, for
example, a unix pipe, but you also want to generate some error
messages and have them not go to the pipe. To write to /dev/stderr
or stream 2 (I think) just use cerr << data << endl;
just like you’d
use cout
.
cin and getline
It seems that cin
is good for reading space delimited fields one
field at a time (or something like that), but it doesn’t read whole
lines.
#include <iostream> #include <string> int main() { std::string myLine; std::cout<<"Enter the whole line: "; std::getline(std::cin, myLine); std::cout<<"Your line is: "<<myLine<<"\n"; return 0; }
Here is an example of reading each character of standard input into a string.
#include <iostream> #include <string> using namespace std; int main () { string docstr; while (!cin.eof()) { docstr.push_back(cin.get()); } docstr.pop_back(); // Ditch the last thing read, the End Of File FF. cout << docstr; cout << docstr.length() << endl; return 0; }
iomanip - Manipulators
Instead of old C stdio.h printf
formatting, C++ prefers you to use
something called a "manipulator". The classic endl
is such a thing.
For more complicated manipulations, you’ll need to use <iomanip>
.
Depending on how wild you get you may want to preserve stream formatting settings before mucking them up and then restoring them when you’re done. To save a copy of the current settings, use something like this.
// Include these... #include <iostream> #include <iomanip> // Save existing settings. ios:fmtflags preserved_flags= cout.flags(); int preserved_precision= cout.precision(); // Do some crazy stuff. // Restore settings with this. cout.flags(preserved_flags); cout.precision(preserved_precision);
To pad with leading zeros (or any character) use something like this.
cout << setfill('0') << setw(10) << mynum << endl;
Here is a lesson about manipulators.
#include <iostream> #include <iomanip> int main () { std::cout<<std::setw(15)<<"Padded 15" <<std::setw(20)<<"Padded 20"<<std::endl; return 0; }
Besides the normal setw
, there is also setfill
, setprecision
,
and setbase
. In Cpp11 there are some manipulators for money
and time. (See here.)
See operator overloading below for examples, of how
you can make your own objects do the right thing with cout
.
stringstream
#include <iostream> #include <string> #include <sstream> int main () { std::string stringifiednumber; float mynumber = 0; std::cout << "Enter number: "; std::getline (std::cin,stringifiednumber); std::stringstream(stringifiednumber) >> mynumber; std::cout<<"Plus one would be: "<<++mynumber<<"\n"; return 0; }
File Operations
#include <iostream> #include <fstream> #include <string> using namespace std; int main(){ string line; // Create output stream for writing in append mode. ofstream f2write ("input.txt", ios::app); if (f2write.is_open()) { f2write << "\nThe End.\n"; f2write.close(); } else cout << "Unable to open file for writing"; // Create input stream for reading. ifstream f2read ("input.txt"); // The file is opened at ifstream creation. if (f2read.is_open()) { while ( getline (f2read,line) ){ cout << line << '\n'; } f2read.close(); } else cout << "Unable to open file for reading"; return 0; }
I like to send output either to a file or if one is not specified, standard output. How can that be done? This is a setup that has worked for me.
void output_document(Obj &content,ostream &os) { os << content; // Will be stdout or file. } if ((outfile[0] == '-') && (outfile[1] == '\0')) { // The standard output dash? output_document(TheContent,cout); // Use cout as file stream. } else { // File name provided. ofstream f(outfile); // Create file stream f. if (f.is_open()) { // Open without problems? output_document(TheContent,f); // Send f as the stream. f.close(); // Take proper care of f. } else // Something went wrong. cout << "Error: Problem writing to:" << outfile << endl; } // End real output file name provided.
Read Filenames Of A Directory
Note that while this does seem to work fine and it illustrates some
interesting techniques, it takes 39x longer to run than simply doing
ls /tmp
. YMMV.
#include <iostream> #include <string> #include <vector> #include <iterator> #include <dirent.h> using namespace std; void read_directory(const string& name, vector<string>& v) { DIR* dirp = opendir(name.c_str()); struct dirent * dp; while ((dp = readdir(dirp)) != NULL) { v.push_back(dp->d_name); } closedir(dirp); } int main() { vector<string> v; // Vector to contain strings for each file in dir. read_directory("/tmp", v); // 1. string of dir 2. vector of strings of contents. copy(v.begin(), v.end(), ostream_iterator<string>(cout, "\n")); // Display them. }
Note also that there is a std::filesystem::directory_iterator
(ref)
but that required turning on compiler options last I checked.
Memory
Some C++ specific keywords, new
and delete
, help with memory
allocation and deallocation issues.
Functions
In C it is good practice to prototype functions (describe their input and output before using them).
Unlike C, in C++ functions can be overloaded. This means that their meaning can be redefined.
Also C++ can use functions inside of structures. This is not common but it hints at the more C++ way of organizing functionality, classes, discussed in the next section.
Unlike C, C++ allows the program to define default values for function arguments. This is valid.
int sum(int x, int y,int z=0,int w=0) { return x+y+z+w; }
sum(1,2); sum(1,2,3); sum (1,2,3,4);
If the number of arguments changing makes you apprehensive hold on to your hat because things get even more exciting with…
Variadic Arguments ...
Need a variable number of arguments? C++ can do that with
variadic function parameters. To do this use ...
(three periods) as
the last parameter "name". Then in the function itself you can refer
to some special variables related to the call.
Then you can use va_list
, va_start
, va_arg
, va_copy
, va_end
which are explained
here.
There is some typical C++ syntax messiness with this feature. This sentence from the official reference helps one understand what they’re looking at when they come across multifarious syntax for variadic arguments in the wild.
This syntax for variadic arguments was introduced in 1983 C++ without the comma before the ellipsis. When C89 adopted function prototypes from C++, it replaced the syntax with one requiring the comma. For compatibility, C++98 accepts both C++-style f(int n…) and C-style f(int n, …).
Fortunately that’s all there is to know about ...
syntax. Ha!
Just kidding! Have a look over at
template
parameter pack syntax which also uses ...
!
Deleted Functions
Sometimes you might see something like this.
class Something : public Something_base { public: definitely_floats(float x) {return x*2;} definitely_floats(int x) = delete; };
This is a C++11 style which is similar (but superior I’m told) to declaring a private function and then just neglecting to define it. This is to prevent it from being used. This way, by being explicit about it, you can get more sensible compiler errors.
Increment Operators
Prefix operators (e.g. ++var
) increment the value and then return a
reference.
Postfix operators (e.g. var++
) create a copy of the variable,
increment the value, then return the copied, unincremented value.
Also C++ has +=
and other such operators as found in Python.
Inline Functions
C++ discourages the use of preprocessor complexity. Instead, the
idiomatic C++ way to do preprocessor functions is with the keyword
inline
.
Functor
I’m not sure if this is really a real thing or just a way to refer to a kind of usage but I’ll note it here in case it comes up. A functor essentially seems to be a class which includes a ‘type operator()(type arg){ def; }` overload function, i.e. overloading the parentheses that usually signal a function. This gives the class name the ability to look like a function and do this thing defined in the overload. The difference here between other function strategies is this functor can now carry around state in the class’ member variables.
Here’s a simple example of how this might look.
#include <iostream> using namespace std; template<typename T> int changeit(T process, float x) { return process(x); } class Half { public: float operator()(float x) { return x/2; } }; class Double { public: float operator()(float x) { return x*2; } }; class TimesLastResult { public: float last=1; float operator()(float x) { last*= x; return last; } }; int main() { Half decrease; Double increase; TimesLastResult warpspeed; cout << decrease(decrease(4.0)) << endl; // Returns 1 - Simply calling the functor. cout << changeit(decrease,2.0) << endl; // Returns 1 - Passing desired functionality... cout << changeit(increase,0.5) << endl; // Returns 1 - ...and necessary args. cout << warpspeed(2) << endl; // 2 cout << warpspeed(2) << endl; // 4 cout << warpspeed(2) << endl; // 8 - This function is remembering its state. cout << changeit(warpspeed,2) << endl; // 16 - Can still be used modularly. return 0; }
This example shows normal functors, how to use them in a modular
interchangeable way and finally how the state features could be used.
The TimesLastResult
multiplies the supplied argument by whatever the
last answer this function supplied was. That’s a strange kind of
function, but possibly useful (the classic Fibonacci example might
have been more "useful"). Normally a trick like this is just hidden in
some big class but the point here is that it doesn’t have to be.
Lambda
Lambda expressions, also called "function literals", showed up in
C++11. Lambda functions are anonymous functions in the same
way that 2.5 in x=2.5
might be an anonymous float. Everything a
lambda can do can be done other more verbose ways. For normal things
that lambdas do, function pointers suffice just fine, however there
are some added tricks to lambdas. The apparent similarity of lambdas
to function pointers comes from the fact that a lambda creates a
class which has a operator()
so that it can look like a function
and do the job of a function pointer. But this class can do some other
things too like "capture" state, optionally by reference. The runtime
object of this class is technically called a "closure" and holds the
references or copies of the captured variables. The class is called a
"closure class". If the capture is empty then the compiler may just
create a function pointer. Note that proper function pointers will
work anywhere while lambdas are scoped to where they are defined — a
bad thing sometimes but usually probably a good thing.
In practice lambdas are mostly used to create little expressions for
things like find_if
, remove_if
, count_if
, sort
, lower_bound
,
etc. They’re commonly used in thread programming and for special
behavior with shared and unique pointers.
Lambdas can usually be spotted with a distinctive empty capture []
.
[capture_clause](parameters)->return_type{statements;}
-
capture_clause - which outside variables are available to the lambda statements and whether to copy or reference them. See below.
-
parameters - even an anonymous function can have input. Even messy variadic input.
-
return_type - normally not explicitly specified and properly deduced for simple functions with less than 2
return
statements. When the exit logic gets messy, this can be used. -
statements - normal code with normal semi-colon terminations.
Nothing too complicated about that except for the capture clause. It is usually empty (capture nothing), but it can get absurdly complex. There are some quirky syntax tricks:
-
[&x]
means x by reference. -
[&x,y]
means x by reference y by value. -
[=]
means capture all by value by copying. Probably unwise. -
[=,&x]
means copy all except x by value. -
[&]
means capture all by reference. Probably unwise.
C++14 has even more baroque "features" and syntax.
-
[x=x]
make sure your external capture variables are copied to the closure’s variable. -
[x=std::move(x)]
on the other hand ensures a move.
Note that the scope on the left and right of the =
here are
different (left x is in the closure, right x is in the code defining
the lambda). This is called "generalized lambda capture" and allows
one to capture the result of an expression. Looks like a mess to avoid
if possible.
auto mylambdafn= [](){ cout << "doing mylambdafn..." << endl; }; mylambdafn();
Control Flow
if (x>=y) {dothis();} else if (x<y) {dothis();} else {dothis();} // Optional.
int choice=1; switch (choice) { case (0) : cout<<"no"<<endl; break; case (1) : cout<<"yes"<<endl; break; default : cout<<"huh?"<<endl; }
for (int i=0; i<10; i++) { std::cout<<i<<endl; }
#include <cstddef> for (std::size_t i=0; i<10; i++) {
There is also a newish (C++11) range-based for
more like Python or Bash.
for ( declaration : range ) statement;
The declaration is a named variable whose type is the type of the sequence’s elements. The range is some kind of container or array or sequence containing the type of the declaration variable. Something like this.
int a[] = {0, 1, 2, 3, 4, 5}; // A simple array. for (int n : a) { action(n); } // Do stuff with each n. for (char c : "xed.ch") { action(c); } // Iterate over each character.
This works for complex container objects too.
while (conditional) { do_stuff(); // Keep it up! } do { do_stuff(); } while (conditional); // Make sure at least one happens.
Both break
, to exit the loop immediately, and continue
, to
conclude the current loop body execution and go on to the next, are
valid.
To get an infinite loop that needs a break
to get out of (or not)
here are options.
for (;;){... }
while(1){... }
Threads
There are three kinds of threads worth knowing about.
-
Hardware threads - Provided by the hardware to do computations on each core.
-
Software threads - Managed by the OS to keep the hardware threads loaded properly.
-
C++ threads - These objects from
std::threads
act as a handle to threads in the same way that programmed file handles act as handles to OS files.
You should consider using std:threads
directly if you are trying to
optimize a single program on a single platform. But for everyone else
in the land of portable software that plays nicely with others, use
the "task" oriented approach. Such an approach will look something
like this.
auto fut= std::async(some_asynchronous_computation)
When used with the default launch policy, this spares you a lot of
gruesome low level details and provides better return value (fut
)
and exception functionality. The default launch policy is to run the
task asynchronously or defer it, whichever the compiler thinks best.
This is equivalent to the previous async
call.
auto fut= std::async(std::launch::async|std::launch::deferred,some_asynchronous_computation)
This should make it obvious how to constrain behavior to one or the
other. Constraining to async
seems to go with the spirit
of it.
Pointers
int I=20, *pI;
pI= &I; // Pointer to I is the address location of I.
cout << *pI; // Dereference returning value pointed to by pI.
One thing to note about C++ is that using 0 and NULL
used to
be ok to refer to a null pointer. This is no longer polite. This
can cause problems when overloaded functions can’t figure out if you
mean an integer (as in 0) or a null pointer. For null pointers, the
correct modern C++ way is to use nullptr
.
Smart Pointers
A smart pointer was first part of C++98. In C++11, there are now three more types of smart pointers. These have pretty much all of the same functionality as normal pointers but with a lot of baggage to try to help keep things under control.
-
std::auto_ptr
- Deprecated — only consider using for legacy requirements. -
std::unique_ptr
- Replacesauto_ptr
but with correct copying. Actually, copying is not even a thing with this pointer but move is. If you could copy it, it wouldn’t be unique, would it? It is a move-only type. Converting tostd::shared_ptr
is ok. Destruction normally usesdelete
but can also use a custom destructor. -
std::shared_ptr
- Has reference counting for multiple ptrs with the same target. Double the size of raw pointers to store a pointer to a reference count (actually the "control block" where the reference count and other things are stored which makes it more than double). Must be atomic or bad things happen. Also creating multiple shared pointers for the same object is a bad idea; there will be multiple reference count accounting and the deletes will be more or less random. You can copy shared pointers for mostly the same intended (but correctly functioning) effect. Shared pointers have no normal array features, e.g. nooperator[]
; generally usestd::array
orstd::vector
instead. -
std::weak_ptr
- Like shared, but doesn’t increase reference count. Needs to check if object is still there. Usually created fromstd::shared_ptr
. Valid when a something like a shared_ptr is needed but it may dangle. More can be found about when this might be applicable here.
auto mysharedptr= std::make_shared<MyType>(); // Ref count is 1. std::weak_ptr<MyType> myweakptr(mysharedptr); // Ref count is 1. mysharedptr= nullptr; // Ref count is 0. The MyType destroyed. if (myweakptr.expired()) { // Do something... // Points to nothing so expired.
Note that the function is framed as expired()
because testing for it
being still valid is tenuous; this is because at any moment (e.g.
between you checking and doing something about it) it could expire.
You can get that behavior only by creating a std::shared_ptr
from
the weak one. That is an atomic check and grab which will be null if
the std::weak_ptr
has expired. Do that like this.
std::shared_ptr<MyType> ptrcheck= myweakptr.lock();
Smart Pointer Creation
To allocate smart pointers you could do something like this.
std::unique_ptr<MyClass> my_unique_ptr(new MyClass);
This makes you have to repeat the type and apparently has other problems. The modern recommended way is to do it like this with special functions designed for the purpose.
auto my_unique_ptr(std::make_unique<MyClass>());
Works with make_shared<Type>()
too. (make_unique
is C++14
and make_shared
is C++11).
These functions work better almost always but may have problems if you
need to have custom memory management, like your own operator new
and
operator delete
. But that’s super exotic.
References
References are a feature specific to C++ not found in C. Although the subtleties get complex, the greatest utility of references is simply as a way to pass by reference to functions easier. You can still move things around by pointers if you want but references often allow function calls to be less complicated.
-
In Unix terms, I think of pointers as symlinks and references as hard links. The reference is the object accessed differently, not a pointer to an address that may not contain anything.
-
You can also think of a reference as a constant pointer.
-
Pointers are variables that contain memory addresses while references are additional definitions of compiler tracked objects. (The compiler thinks: "Ah, he’s calling x another thing, y, now too.") Of course the compiler can use the same machinery it uses for pointers for this but references can be implemented more efficiently. That’s up to the compiler.
-
References point to objects (whose address a reference shares), while pointers can point to other pointers, ad infinitum.
-
References can not point to other references. (You are forbidden from specifying any such thing, however, if a compiler causes this to happen, say by some weird template instantiation or
auto
usage or messytypedef
, the two references "collapse" using a baroque heuristic.) -
References can (should) not normally be
NULL
. -
References can not iterate using pointer arithmetic.
-
Pointers need to be dereferenced with
*
, or->
for class members, while references can be dereferenced with.
like the referent ("automatic indirection").
#include <iostream> using namespace std; // Function with reference parameters. void one2theother(int &from, int &to) { from--; to++; } // Function with reference parameters returning a reference. int &add2together(int &a, int &b) { int s; int &r2s=s; r2s= a+b; return r2s; } int main () { int var= 10; int var2= 99; int &ref2var= var; cout << var << endl // 10 << ref2var << endl; // 10 ref2var++; cout << var << endl // 11 << ref2var << endl; // 11 var++; cout << var << endl // 12 << ref2var << endl; // 12 one2theother(var,var2); // Function using references. cout << var << endl // 11 << ref2var << endl // 11 << var2 << endl; // 100 cout << add2together(var,var2) << endl; // 111 return 0; }
Here
is a lot of expertise on the topic for those who really need it.
And once you think you know how great references are, you can start to
doubt yourself by wondering if passing a reference to a single char
is actually made less efficient than simply passing the value
directly — here
are some people who have pondered this quandary. Non-theoretical
practical answer: doesn’t matter.
Rvalue References
Newish C++ is littered with double ampersands as in this
parameter definition: MyClass&& x
. What the heck is that all about?
These are "rvalue references". They are also sometimes called
"forwarding references".
An rvalue (see values below) reference is mostly used to bind to temporary objects in order to make them more efficient. They also are useful in "move semantics" and "perfect forwarding" which is about juggling items in memory in a sensible way. They can only bind to objects for which moving in plausible.
This apparently is very useful in template code. In template argument
deduction there is a concept called "universal references" which is
subtly different but uses the same double ampersand. There something
like MyClass&& x
could refer to a reference or the real object.
These arcane subtleties are discussed
here.
One general rule is that if there will be magical type deductions as
with auto
or templates, then the &&
syntax is a universal
reference and not necessarily constrained to an rvalue reference.
An important implication is that if you use this kind of trick it’s
best not to make the universal reference an argument of overloaded
functions. This will tend to try to fit the universal reference before
fitting what you really wanted it to fit. Ah, automagical — not
always so auto or magic.
If you want to make sure these automagical helper monkeys stay in
their cages, you can use the explicit
keyword which will prevent
implicit conversions and copy initializations. Another way to get
universal references to behave "explicitly" in your overloaded
functions is to use what’s called "tag dispatch" this is where you add
bogus arguments so that the function signature is certain to be the
correct match.
Values
In C++, especially the modern species, you see the term
"lvalue" and "rvalue" pop up a lot. For some reason it is taken for
granted that programmers will instinctively know the etymology of
these words. I used to think it was "left value" and "right value" as
in which side of the equals sign something like x=3;
was on.
Wikipedia hints, misleadingly I
suspect, that lvalue might have something to do with "location".
-
Traditionally on the left side of an assignment expression.
-
Its address can (usually) be taken.
-
Designates a function or object (usually).
-
Traditionally on the right side of an assignment expression.
-
Its address can (usually) not be taken.
-
A temporary object or some other kind of value not associated with an object.
This lvalue and rvalue stuff is bad enough to have to think about but modern C++ spirals out of control with three more types.
There are now the following value types.
-
rvalue - Fun fact: rvalue references returned from functions are themselves rvalues.
-
lvalue - A "parameter" is always an lvalue.
void f(MyClass&& x);
the parameterx
is still an lvalue even if its type says otherwise. -
glvalue - Generalized lvalue, a subspecies of lvalue.
-
xvalue - Expiring value referring to an object whose resources can be moved because it is nearing its destruction. Perhaps as function return values which are exotic reference things which necessarily will be gone after the return. Something like that.
-
prvalue - Pure rvalue. Like an xvalue but for simple rvalue non-reference things (themselves).
The horrendous gory details can be explored here.
Move and Forward
The functions (function templates to be exact) std::move
and
std::forward
are interesting in that they do not move or forward
(respectively) anything. They are compile time mechanisms for
elaborate casting which I feel is the most important thing to
understand about them — they do nothing at runtime. It seems that
move
should have been named something like rvalue_cast
. Since
rvalue objects are the kind that can be properly moved, the
std::move
function does provide a running start to that actual
functionality.
So what do they do? The std::move
casts an argument to an rvalue.
The std::forward
does the exact same thing but only in limited
cases.
If you’re fighting with these mechanics, also keep in mind
std::remove_reference
which strips the l- or r-valueness.
Move requests on const
objects are silently transformed to
copy operations (losing any efficiency you may have envisioned).
The std::forward
function (template) is mostly used in a function
template taking a universal reference parameter that will be
forwarded to another function. Since arguments are lvalues, if you
have an argument that needs to stay an rvalue and get passed on as
such, that’s what std::forward
is good (known) for. The condition it
checks for is if the argument it is looking at is an rvalue. If not,
then it just passes it on without any exotic casts. In theory the
std::move
function could always be replaced by the more
comprehensive std::forward
. But subtle differences in "clarity", I
suppose, have produced both.
Classes
C++ features organization by classes.
Special member functions are the ones that C++ can create implicitly.
default constructor |
X() |
destructor |
~X() // C++11 forces |
copy assignment |
void operator=(X &src) |
copy constructor |
X(X &src) |
move constructor |
X(X&& src) // new in C++11 |
move assignment |
X& operator=(X&& src) // new in C++11 |
In C++11 there is a way to actively produce these special
member functions which, under some circumstances (not ever used, etc),
don’t get auto-generated. This uses the keyword default
.
X(const X&) = default; // Normal copy ctor is fine, make one for X.
X& operator=(const X&) = default; // Normal copy assignment ctor is ok too.
X(X&&) = default; // Normal moving.
X& operator=(X&&) = default; // Normal moving assignment.
This might just be easier to comprehend as a reminder that these things can (and now will) autogenerate.
// cxe- Wed May 12 12:22:10 PDT 2004 // This is a test of using inheritance to create a containable class (or array // in this example) of heterogenous objects. The goal will be to store 2 // classes in the same array in such a way that they are usable. #include <string> #include <iostream> using namespace std; class Number { // This is the base class. public: Number() {} virtual void show() { cout << "No base class data!" << endl; } private: }; // end class Number class Roman: public Number { public: Roman operator=(string p) { pretium= p; return *this; } void show() { cout << "Roman:" << pretium << endl; } private: string pretium; }; // end class Roman class Arabic: public Number { public: Arabic operator=(int v) { value= v; return *this; } void show() { cout << "Arabic:" << value << endl; } private: int value; }; // end class Arabic int main() { Number z; //can't do anything with z Arabic a; a= 19; Roman r; r= "XIX"; cout << "Objects as themselves, no container ----------" << endl; z.show(); a.show(); r.show(); Number n[3]; // Array of base class objects n[0]= z; n[1]= a; n[2]= r; cout << "Objects in container of baseclass ----------" << endl; n[0].show(); n[1].show(); n[2].show(); Number *p_n[3]; // Array of pointers to Numbers p_n[0]= &z; p_n[1]= &a; p_n[2]= &r; cout << "Objects in container of baseclass pointers ----------" << endl; p_n[0]->show(); p_n[1]->show(); p_n[2]->show(); return 0; } // end main
This outputs the following.
Objects as themselves, no container ----------
No base class data!
Arabic:19
Roman:XIX
Objects in container of baseclass ----------
No base class data!
No base class data!
No base class data!
Objects in container of baseclass pointers ----------
No base class data!
Arabic:19
Roman:XIX
Note that the default access is private so by listing them before public, you can save that keyword. I think it’s better to just be explicit though.
Declarations will often need the class namespace. This looks like this.
retVar ClassName::somefunction(int x,int y);
The protected
access control specifier means that the item specified
is treated like public
by derived classes and private
from
unrelated ones.
Slicing Problems
C++ has a troublesome topic of discussion called the "slicing problem". This has nothing to do with Python slicing. It looks a bit like this.
class A {int alpha;};
class B:public A {int beta;};
B b; A a= b;
In this code, b
has an alpha and a beta, but a
does not have a beta
even though it was assigned from an object with one. That beta is
considered to have been "sliced" out of the object.
const
Member Functions
If you have a situation like this
const Mything the_unchanging_thing= Mything("Always this."); the_unchanging_thing.dump();
The dump member function probably won’t compile because the compiler
can’t be sure that the dump()
function won’t mess with the calling
object. And since this particular calling object,
the_unchanging_thing
, is constant, that would be wrong.
The way to handle this is to define the dump()
function as a "const
Member Function" (that’s the official way to refer to it). Add the
const
keyword to the end of the declaration and definition.
void dump() const;
friend
Sometimes you want to give access to other functions that are actually
not part of your class. This is what friend
functions can do.
Use friend
keyword in the declaration, not the definition.
Confusingly, you must put the friend function declaration in the class
it doesn’t belong to prefixed with the friend
keyword.
friend MyClass operator+(double x, const MyClass &mc);
This now can go in the MyClass declaration even though it’s not part
of MyClass. It’s just a helpful random function that can now access
MyClass as if it were a part of it. Note that this example helps for
this order of arguments. If the order is reversed, the MyClass
operator+
can just be overloaded the normal way. It seems common to
use friend
functions when overloading operator<<()
.
Classes can be friends too. By doing something like this.
class Body { public: friend class Clothes; ...
This type of arrangement will allow Clothes to access Body’s private parts.
virtual
The virtual keyword is confusingly used for different purposes depending on context. Here are two of the uses. There may be more.
-
A function declared virtual means that an existing overriding function in a derived class is invoked.
-
An inheritance relationship declared using keyword virtual between classes
Derived1
andDerived2
that inherits from classBase
means that another classDerived3
that inherits fromDerived1
andDerived2
still results in the creation of only one instance ofBase
during instantiation of typeDerived3
.
See Abstract Base Classes for an explanation of "pure virtual functions".
One slightly tricky thing that can plague setups that want to leverage virtual functions is something like the following example where I want to have different backend output systems for some internal data. Simplified for illustrative purposes, here’s a plausible way to set something like this up.
#include <iostream> using namespace std; class Backend { public: virtual string make_bold(const string &l) { return "*"+l+"*"; } }; class Backend_HTML:public Backend { public: string make_bold(const string &l) { return "<b>"+l+"</b>"; } }; class Backend_TeX: public Backend { public: string make_bold(const string &l) { return "{/bf "+l+"}"; } }; string do_someformatting(Backend outputEngine){return outputEngine.make_bold("The text.");} int main() { Backend_HTML outputEngine; // The goal is to control output style simply by how this is typed. cout << do_someformatting(outputEngine) << endl; return 0; }
Because I declared the outputEngine
variable to be a Backend_HTML,
you might expect the do_someformatting()
function to produce the
HTML version. But it does not. It produces the virtual function’s
*The text.*
. What is going on? It’s subtle but you must change the
function declaration of the function that works with these overridden
objects so that they show up as references. Note the &
.
string do_someformatting(Backend &outputEngine);
Without this, the function insists that the HTML outputEngine object
supplied be hammered into the generic base class. By using a reference
to the &outputEngine
the result is <b>The text.</b>
.
Virtual Data Member Attributes
Nope. Don’t think it can be done. Use a Rube-Goldberg getter function to supply your derived classes refined values. Or use Python.
Operator Overloading
One of the many idiosyncratic things about C++ is that it is
one of the few languages that allow you to muck about with redefining
much of the core syntax. Don’t like how +
adds numbers? Well, you
can change it. Obviously this can produce horrendous obfuscation, but
generally it’s quite handy.
One of the most common uses for operator overloading is to make a
function that can allow your custom classes to be included in normal
stream operations. In the following example, I have a Block class
that I’d like to be able to "print out" using cout
.
ostream &operator <<(ostream &s, Block b){ s << "Block (" << b.queryBlockType() << "):"; return s; }
Note that this is not a member function. This is just a global function. That said, you need to give this global function access to the data it will likely be using from the custom class. That is done with a friend function (see also that section). That looks like this.
friend ostream &operator <<(ostream &,const Block &);
I’m not sure that’s technically a proper declaration; it’s more like a decoration referencing a function template that must be spelled out like a declaration. Whatever it is, it does go in the class definition.
Need to worry about postfix vs. prefix? Lucky you! This example is probably wrong, but it’s in the right direction.
X &operator ++ (int) {X t; t.v= v++; return t;}
X &operator ++ () {X t; t.v= ++v; return t;}
The &
is optional but almost always present to lighten up the call
by making the default argument a reference.
Note that there are some things you probably should not think about
overloading.
-
::
scope specifier -
.
as inobject.member
-
.*
pointer-to-member selection operator, however apparently->*
can be overloaded. What a horrendous "feature". -
?:
(ternary)
Override (The Keyword)
When a base class function is declared virtual
its corresponding
derived class functions should override it. It turns out that
sometimes subtle errors can happen if the derived class has some
defect in its correspondence to the base class. To make the errors
less subtle and more obvious to the compiler such that sensible
errors will be produced, C++11 offers the override
declaration. Basic usage looks like this.
class Universal { public: virtual void myfn(); }; class Specific: public Universal { void myfn() override; // Optional to begin with "virtual" here too. };
One bizarre caveat on the override
specifier is that it only is a
keyword if it is in a proper place at the end of a member function
declaration. This means that if you want a function called
override()
or a variable or whatever, you can do that. Obviously
that’s going to be misleading these days to those in the know — but
not to those really in the know. Obfuscated C++ writes
itself! Gah!
A similar specifier is final
which indicates that the function or
class should not be overridden. It is in its final form. Or
something like that — smells like Java.
Reference Qualifiers
Getting into crazy weird stuff is easy with C++ and my favorite example of "ok, you’ve gone too far" is reference qualifiers. I’m not going to pretend to understand the gory details but essentially you can specify if derived classes can override (not overload) base class functions depending on whether or not the member function is an lvalue or an rvalue.
Clearly that is ridiculous and the whole language should be scrapped if we need to regularly deal with ridiculous error bait like that. But I’m including it for completeness so that I can hopefully recognize this for what it is if I ever stumble across it in the wild. Here’s the syntax that should tip you off to this "feature".
Class Myclass { public: void weirdfunction() &; // Only when *this is an lvalue. void weirdfunction() &&; // Only when *this is an rvalue. }; Myclass x; // Ordinary instantiation which is lvalue.
Note that if you ever use reference qualifiers you need to make sure all overloads use them otherwise the ambiguous ones may take precedence and screw things up.
I don’t even know when the function’s context would ever be rvalue (factory functions?) but that should be enough of a syntax hint to send you off in the right direction for proper research into the topic.
Abstract Base Classes
Taking some kind of obnoxious programming test? I had never heard of
this and I suspect it has something to do with Java where abstract
is a keyword; it is not in Cpp! But there are abstract base classes,
sometimes referred to as ABC, in Cpp. An abstract base class is really
just a class that contains one or more pure virtual functions.
Class declarations containing at least one pure virtual function
will not allow objects to be instantiated from that class.
A "pure virtual function" is one that ends with =0
like this.
void Move(int dx,dy) = 0;
That’s it. The =0
in the prototype means that the ABC doesn’t need
to define the function. Also note the const
member function version.
void Show(int dx,dy) const = 0; // But don't modify the calling object.
Member Initialization Lists
In class definitions the constructor can have a list of members with
their correct initial values defined. These are placed after the
constructor name and parameters, followed by a :
and before the body
of the constructor. The following are very similar. The first is
preferred since it avoids unnecessary copies.
MyClass(int some_init_args):member1(5){}
MyClass(int some_init_args){member1=5;}
#include <iostream> class Parent { public: Parent( int x ) { std::cout << "Parent's constructor" << " called with " << x << std::endl; } }; class Derived: public Parent { public: Derived() : Parent( 10 ) { // With "Member Initialization List". std::cout << "Derived's constructor" << std::endl; } }; class Strclass { public: Strclass() : thestr("This is the init string.") { std::cout << thestr << std::endl; } private: std::string thestr; }; int main() { Derived a_derived_instance; Strclass somesc; }
Exceptions
Recovering from errors in C is done by explicitly planning for every specific erroneous condition. In C++ there is a system of "exceptions" where (specifically) unanticipated errors can be treated reasonably.
While it is critical to understand exceptions I personally have some misgivings about them which are well documented in my Python notes. Without going into details here, I’ll sum up my personal opinion about C++ exceptions by quoting the Google C++ Style Guide which simply says, "We do not use C++ exceptions."
Here is a rough idea of how C++ exceptions work.
#include <iostream> using namespace std; void thefunction() { throw 99; } int main (int argc, char *argv[]) { try { if (argc > 1){ thefunction();} else { throw '?';} } catch(int e) { // The catch is chosen based on its type. cout << "Exception number " << e << " has occurred." << endl; } catch (...) { // If the type sent by throw is not defined, go here. cout << "Unknown exception has occurred." << endl; } return 0; }
When this code is run with arguments it returns:
Exception number 99 has occurred.
When this code is run without arguments it returns:
Unknown exception has occurred.
Note that the catch statement is chosen based on the type specified by the throw command. In this simple example, this is not remarkable, but when defining your own types with classes, this mechanism can be very useful in organizing code to react to errors arising from different causes.
There are much fancier ways to use exceptions. You can define your own by inheriting standard exceptions. You can also catch and handle standard exceptions your own way. They can be nested such that the inner exception passes the information through to the outer ones.
Trying to document all weird syntax one might encounter, note that
there exists a C++11 declaration specifier, i.e. a keyword,
called noexcept
. This is used to declare functions that are
guaranteed not to ever emit exceptions. The point of this is that
when the compiler must worry about exceptions, performance can be hit.
This feature can allow the compiler to do the best thing. The syntax
looks like this.
ReturnType myfunction(parameters) noexcept; // Most optimizable. ReturnType myfunction2(parameters) throw(); // C++98 style. ReturnType myfunction3(parameters); // Exceptions ahoy!
That throw()
syntax is valid but not exactly the same as the
noexcept
syntax. Gah! Best to just leave the whole topic alone
unless you step in a pile of it in someone else’s code, or it
otherwise causes trouble.
Templates
template <typename T> T functionName (T parameter1,T parameter2,...) { /* Function template. */ } template <typename T1, typename T2> T2 functionName (T1 parameter1,T2 parameter2,...) { /* Multiple types. */ }
template <class T> class Number { private: T value; }; IntArray::IntArray(){ /* Normal constructor. */ } template<class Type> Array<Type>::Array(){ /* With template. */ }
Debugging
Since C++ is famous for having millions of lines of error output it’s
good to intercept this where possible. The static_assert
command
will print a message if a condition is met at compile time.
For example, this checks to see if negative integers round down when divided.
static_assert(-3/2==-1)
static_assert(-3/2==-1, "Negative division rounds towards positive.")
The normal assert
is for ensuring things are in order for run time
and works the same way.
Standard Template Library
The near universal detest for STL in game development makes each studio’s code a silo of private libraries, which is unfortunate.
You don’t use STL where you need max performance, but performance isn’t the top priority for most code, even in games.
I don’t like STL much and rarely use it, but I do really like standards. The library situation with C++ is really a shame.
— John Carmack
Both C and C++ are very spartan languages and in order to do anything useful in either you pretty much need to write some tools to manage simple flexible data structures. This has been thought out in C++ with the Standard Template Library or STL.
Note that in modern times the STL is actually a subset of the C++ standard library that was inspired by an ancient template library that got integrated. Another way to say "STL" is roughly something like "containers/iterators of the standard library".
STL mostly provides templates that allow programs to easily create simple but powerful data structures for all kinds of pragmatic uses. This is the kind of stuff that higher level languages like Javascript and Python take for granted.
-
priority_queue
-
queue
-
stack
iterators
Iterators are pretty important in normal C++ and are pretty much how you rummage through STL containers of data. And lots of other things. You can have an iterator type that looks like this.
typedef std::vector<int>::iterator MyIterType;
One interesting thing that seems bonkers to me is that you can have, and should prefer, constant iterators. It seems to me that if an iterator’s whole point is going from one thing to another, that’s fundamentally not very constant. Here’s an example.
typedef std::vector<int>::const_iterator MyConstIterType;
My limited understanding
(here’s
more) is that the const_iterator
type points to different stuff,
it’s just that the stuff is immutable. Still look out for it and, in
theory, if you can use it, the const
gods will smile upon you.
Vector
A vector container is essentially a dynamic array. It has most of
the advantageous properties of regular C arrays, mostly contiguous
memory that can be accessed with predictable strides. Unlike regular C
arrays, a vector can be dynamically resized as needs grow. This
implies that vectors hog more memory than you may actually need them
to, just in case you eventually want to use more than you thought you
would. This also implies that once you use more than even this amount
of memory, that an abrupt allocation of yet more memory may perhaps be
needed along with a physical reshuffling of the contents; I don’t know
if this can inject weird choppy performance issues, but it’s something to
think about. The strategies used by vector
to allocate additional
memory are implementation dependent but seem to try to keep log(size)
performance (adding twice as much as you had before upon running out).
The following sample program shows the basic STL container usage with a "vector" template.
#include <iostream> #include <vector> // <---- Don't forget! using namespace std; int main() { vector<int> V1; // Define a vector of integers. V1.push_back(9); V1.push_back(6); V1.push_back(3); // Load vector. for (int i= 0; i<V1.size(); i++) { cout << V1[i] << endl; } // Display values. vector<int>::size_type v_st= V1.max_size(); // Note namespace to use size_type. cout << "Max number of elements in a Vector<int>: " << v_st << endl; }
Methods for vector
include:
-
empty
- Tests if container is doing any containing. -
push_back
- In software engineering English "append". Relatively quick. Cpp11emplace_back
follows the plan ofemplace
. -
pop_back
- Remove item from end. Relatively quick. -
insert
- Arbitrary insertion. Maybe slow unless near the end and reallocation can be minimized. There is also Cpp11emplace
which allows construction and insertion. -
emplace
- Insertion functions take objects to be inserted but emplacement functions take constructor arguments for objects to be inserted. This can matter, for example, when dealing with string literal constructor arguments vs. actualstd::string
objects. Note also things likemyvector.emplace(myvector.begin(),"new winner");
which can be used where you might expect an "emplace_front". -
erase
- Arbitrary removal (not just clearing, itemsize()
is decreased). Also maybe slow unless near the end. I’m not sure if memory is reallocated down ever with this method, but it could be. -
size
- How many elements defined and present. -
capacity
- How many elements have been automagically provisioned for before the next reallocation would be required. -
resize
- Tries to set the number of items to that specified. If there were more items, they get harshly jettisoned. -
reserve
- Like a faux gearshift lever for an automatic transmission, you can ask to manually meddle in the automatic memory allocation. Or how about Cpp11’sshrink_to_fit
which may do what’s on the tin (including remapping the whole thing) or, interestingly, it may do nothing.
Deque
The double-ended queue is similar to a vector in that
elements can be accessed with random access indexing. The major
difference is that items can be inserted at the beginning of the array
while vectors allow only the end to expand. As a consequence of this
feature another difference is that the deque
is not guaranteed to
live in contiguous memory. This means that offset pointers can’t be
used, as with vectors, to access other items; if you try it will
produce random nonsense behavior. The deque
uses complicated chunks
of memory that it grows as needed where needed. By splitting the
container internals, better edits may be possible from the middle of
the object and excellent edits are expected at either end. This kind
of structure might be ideal for a FIFO queue.
Methods for deque
include:
-
push_back
,push_front
- Add an item to the end or beginning. -
emplace_back
,emplace_front
- Compose and add item to end or beginning. -
pop_back
,pop_front
- Remove an item from the end or beginning. -
insert
- Add item to arbitrary location. -
emplace
- Compose and add item to arbitrary location. -
erase
- Remove item from arbitrary location. -
clear
- Remove all items from container. -
swap
- Exchange contents of twodeque
objects. -
size
- Returns number of items. -
empty
- Check if container is containing. -
resize
- Set the container to a limited specified number of items, losing any surplus. -
shrink_to_fit
- Asks the container to try and refactor to minimize memory usage so that it is just sufficient to contain the current items. Note that this is mostly a courtesy and not guaranteed to do anything.
List
An STL list is a doubly-linked list. It’s quick to insert one or many anywhere, but slow to find particular items deep in the chain. This means that inserting can be slow if you need to find the insertion point first. However it also implies that editing the ends of the chain is going to be quick.
Don’t forget to #include <list>
.
Methods for list
include:
-
size
- How many elements (not storage/bytes).max_size
for architectural top end. -
assign
- Fill in a particular value for the whole list or just a range,three6s.assign(3,6)
. Can be used to do a deep copy with something likemytargetlist.assign(mysrclist.begin(),mysrclist.end());
Playing with the iterators there seems to be the way to get slice functionality. -
insert
- Add existing element.emplace
to construct and add (allemplace
functions are Cpp11). Inserts before current item. This can also put multiple elements into a list; the args are iterator position of target, iterator position start of source (item included in operation), and iterator position of end (item not included). -
push_front
,push_back
- Replacepush
withemplace
to construct and insert. -
pop_front
,pop_back
- This returns nothing, so it’s really a "drop" in how I’d think of these things. -
back
- likeend
but not an iterator to one past the end, and simply returns a reference to the last item. -
begin
,end
- returns an iterator to first, and just past last. If you’re atend
, you’ve already done all of them. -
advance
- Moves the iterator the number of int provided. E.g.auto myliststart=mylist.begin();std::advance(myliststart,3)
-
erase
,clear
- Removes an element or range. Or remove all content. Useempty
to check if empty. -
remove
andremove_if
- Removes elements that match specific values or other conditions. -
unique
- Remove duplicates. -
reverse
- Change the order of elements. -
sort
- Unadorned, uses<
. Can use custom comparison function. Note the difference between this template method and the generic STLsort
which requires a random access container. Stick with the built in list sort method for lists. -
merge
- Combine ordered lists into another ordered list. -
splice
- Move elements between lists.
Here is an example of using a linked list. One helpful feature is that
you can insert into the beginning of the list without actually
remapping everything else. This is a rough implementation of the Unix
tac
command which reverses lines of input. The reversing is done
simply by inserting each line read into the list of lines instead of
appending each.
#include <iostream> #include <fstream> #include <list> using namespace std; void readit(istream& ins,list<string>& lines){ string line; while (getline(ins,line)){lines.push_front(line);} } int main(int argc,char** argv){ list<string> lines; if (argc>1){ // A file name argument should be present. ifstream ifile(argv[1]); if (ifile.is_open()) { // Double check that it opened ok. readit(ifile,lines); ifile.close(); } else // Can't open the file for some reason. cout<<"Error: Unable to open file for reading!!"<<endl; } else { // No arguments? Use standard input then. readit(cin,lines); } for (list<string>::iterator i=lines.begin();i!=lines.end();i++) cout<<*i<<endl; return 0; }
Note also this program roughly shows how to handle reading standard
input or a file specified. Also note that the reverse
method could
have been used, perhaps with slightly more clarity of purpose.
Another quirk of lists is that because it’s more of a linked chain — importantly, of arbitrarily sized items — it’s kind of weak on other
simple features like spitting out what position a program is currently
iterating over. Since it just moves around from node to node on the
linked graph, it actually doesn’t really know much about the big
picture. This means that some obvious strategies may not work as in
this example where I want to know if the iterator, i
is on the 2nd
item.
if (i - myvector.begin()) == 1) {... // Totally fine and quick, for random access iterators only! if (i - mylist.begin()) == 1) {... // This will not compile! if (distance(mylist.begin(),i) == 1) {... // This will be slow, but will work. i2= mylist.begin(); i2++; if (i == i2) {... // Not sure if this is a reasonable approach.
Other Lists
I have notes about a singly-linked slist
but this appears to be
nonstandard.
And it looks like Cpp11 has forward_list
for that purpose.
Reserved Keywords
asm, auto, bool, break, case, catch, char, class, const, const_cast,
const_iterators, continue, default, delete, do, double, dynamic_cast,
else, enum, explicit, export, extern, false, float, for, friend, goto,
if, inline, int, long, mutable, namespace, new, operator, private,
protected, public, register, reinterpret_cast, return, short, signed,
sizeof, static, static_cast, struct, switch, template, this, throw,
true, try, typedef, typeid, typename, union, unsigned, using, virtual,
void, volatile, wchar_t, while
Option Parsing
My C notes have a reasonable example of option
parsing using getopt_long()
. It’s a perfectly good way to do option
parsing and works fine in C++. It uses old C style strings — it
does not know about booleans, and is not consistent with normal
C++ style. I wrote a more idiomatic version that despite being
three times larger is arguably easier to use.
The main weirdness to get over is that what I’d really like is a
function called optval()
that returns the option’s value no matter
what type it may be. However, you can not overload functions on just
return type alone. So the work around is to pass in a variable of the
correct type. This first of all assures you (and the compiler) know and
have a good handle on what exactly that type is, and second allows you
to just pass the variable you want to set as a reference and it will
get set. The value is also returned if that’s helpful. If you don’t
have a specific bool or whatever that you want set, you can pass in
nonce member variables that exist just to be passed for this purpose.
This is shown in the main function of the example.
#include <getopt.h> #include <iostream> #include <string> #include <cstring> #include <vector> using namespace std; class Config { // == Defining == public: Config(const int argc,char **argv); // Note default ctor works too; it does not parse. void parseopts(const int &argc,char **argv); // This function actually does the parsing. string optval(char c,string &val); // Use a short opt letter to set and return its string. int optval(char c,int &val); // Use a short opt letter to set and return its int. bool optval(char c,bool &val); // Use a short opt letter to set and return its bool. vector<string> arglist(); // Args not related to options (e.g. often filenames to process). int i= 0; string s= string(); bool b= false; // Generic types. Use with optval() if no ref handy. string help(); // Where the help text is generated. private: // Put all command line controlled settings with their defaults here. bool verbose= false; int debug= 0; string outfile= "-"; vector<string> filelist; // This created from the non-option argument list. }; // End class: Config string Config::help() { // == Describing == string h= "usefulprogram - Chris X Edwards - 2021\n"; h+= "-h,--help This message.\n"; h+= "-v,--verbose Verbose messages.\n"; h+= "-d[L],--debug[=L] Set debug level, 1 when active and unspecified.\n"; h+= "-o,--output <file> Output file, default is standard output.\n"; return h; } // End function: Config::help() Config::Config(const int argc,char **argv) { // Special constructor. parseopts(argc,argv); // Sensibly parses the options if given the argc and argv. } // End Config::parseopts() void Config::parseopts(const int &argc,char **argv) { // == Processing == while (true) { static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, // Bools work well in C++. {"verbose", no_argument, NULL, 'v'}, // Verbose output. {"debug", optional_argument, NULL, 'd'}, // With -d3 or --debug=3 specify level. Or -d to activate at defatult. {"out", required_argument, NULL, 'o'}, // Output file. - is stdout which is default. {0, 0, 0, 0} // Signal end of options with a set of zeros. }; // End opt struct data. int o= getopt_long(argc, argv, "hvqd::b:eo:f:S:", long_options, NULL); // Returns option index. if (o == -1) break; // Signals end of options. switch (o) { case 'h': cout << help(); exit(EXIT_SUCCESS); case 'v': verbose= true; break; case 'd': if (optarg){ debug= atoi(optarg); } else { debug=1; } break; // Arg optional! E.g. `-d3 --debug=3` case 'o': outfile= optarg; break; default: cout << "Unknown option:" << o << endl; return; } // End switch construct. } // End while loop. while (optind < argc) { // Load any remaining command line arguments (not options). string s= string(); // Create a new empty C++ string. for (int i=0;i<=strlen(argv[optind]);i++) s.push_back(argv[optind][i]); // Construct string. filelist.push_back(s); // Add string to vector of arguments. optind++; } // End while still arguments. return; } // End function: Config::parseopts() bool Config::optval(char c,bool &val) { // == Accessing == switch (c) { case 'v': val= verbose; break; // Can't override just on returns. default: cout << "Bad flag." << endl; // Bad. // So a correct type is passed in. }; // End switch. return val; } // End function: Config::optval() for string string Config::optval(char c,string &val) { switch (c) { case 'o': val= outfile; break; default: cout << "Bad string option." << endl; // Bad. }; // End switch. return val; } // End function: Config::optval() for string int Config::optval(char c,int &val) { switch (c) { case 'd': val= debug; break; default: cout << "Bad numeric option." << endl; // Bad. }; // End switch. return val; } // End function: Config::optval() for int vector<string> Config::arglist() { vector<string> v; for (string s:filelist) v.push_back(s); return v; } // End function: Config::arglist() int main (const int argc, char **argv) { // == Using == Config C= Config(argc,argv); cout << "Verbose is:" << C.optval('v',C.b) << endl; // A bool. cout << "Debug level is:" << C.optval('d',C.i) << endl; // An int. cout << "Output file is:" << C.optval('o',C.s) << endl; // A newfangled C++ string. for (string s:C.arglist()) cout << s << endl; } // End main.
cmath
Fancier math. Trig, logs, exp
, sqrt
, ceil
, floor
, abs
. And
wacky stuff like isnan
.
#include <cmath>
M_PI // Is pi.
std::pow(base,exponent) // Exponents.
NAN // Is constant.
Set
A set
is a container whose elements are always ordered (not sorted)
and unique. Sets can be handy when trying to count unique items, even
mildly complex ones as shown here with pair
containers.
#include <iostream> #include <utility> #include <set> using namespace std; int main(void) { set<pair<int,int>> s; pair<int,int> p; // Not strictly necessary here. cout << s.size() << endl; // Outputs: 0 p.first= 0; p.second= 0; s.insert(p); // 1 p.first= 1; p.second= 2; s.insert(p); // 2 p.first= 3; p.second= 4; s.insert(p); // 3 // Showing different way to compose set. s.insert(make_pair(5,6)); // 4 s.insert(make_pair(1,2)); // Redundant, ignore. s.insert(make_pair(3,4)); // Redundant, ignore. cout << s.size() << endl; // Outputs: 4 return 0; }
-
clear
- Remove all elements from object. -
insert
- Put an item into the container. There is no sense of front or back since the item itself defines its place in the structure. -
emplace
- Composes and inserts an item. Also noteemplace_hint
which is similar but also takes a likely neighbor (one higher) and if that neighborhood is actually correct, the insert can be a lot quicker. -
erase
- Removes an item. This can be specified with the position index or by value which, in aset
are pretty much the same thing.
Unordered Set
An unordered_set
does not maintain an order. These can be quicker
than regular sets. This is a C++11 thing.
Multiset
Allows duplicates.
-
find
- Allows you to find the one or many items that match what you’re looking for.
Unordered Multiset
If you don’t want a strict ordering and you want duplicates to be valid, the unordered_multiset is a Cpp11 thing. For simplicity, imagine a Python dict where the keys are combined with an invisible nonce and there are no values; something like that.
Pair And Tuple
The pair container — which came first — is a specific case of a std::tuple with two elements. Or thought of another way, a tuple is a generalization of std::pair. It is basically a way to store heterogeneous items as, well, a pair. Tuples make me wonder what the advantage is over a vector of structs besides everyone in 2011 (C++11) having forgotten C by then. I guess there are a bunch of helper functions and so on; though interestingly many seem to have been removed for C++20. Oops!
It looks like pair is basically a struct:
template<class T1,class T2> struct pair;
Anyway, if you’re using pair<blah,blah> X
, you mostly need to know
about X.first and X.second. Those are not extraction functions
so no parentheses. Also X.swap()
, which is a function, might
possibly be useful for graphics stuff.
Map
Related to a pair in some subtle way is a map. A very simple demonstration of the most basic map syntax.
#include <iostream> #include <map> #include <string> using namespace std; int main () { map<string,int> piechart; piechart["C++"]=38; piechart["C"]=43; piechart["Python"]=19; cout << "C++'s value: " << piechart["C++"] << endl; cout << "C's value: " << piechart["C"] << endl; cout << "Python's value: " << piechart["Python"] << endl; }
A nice property of the map container is that it is some kind of a tree that stores values in a way that guarantees the keys to be sorted. It can be used to sort complex data on arbitrary fields by making those fields the key.
Here’s a way to iterate over all the items contained in a map object.
for (std::map<char,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it) std::cout << it->first << " => " << it->second << '\n';
Note that if you ask for my_hash[not_really_there]
it will create
a new pair with a key matching what you asked for and a value of empty
(or whatever your default constructor dreams up). If you like
exceptions, the my_hash.at(key)
will not do this, but it will throw
an exception if the key is missing. It could be smart to look for the
key first as in something like:
if (my_hash.find(key) != my_hash.end()) cout << my_hash[key] << endl;
Algorithm Functions
The STL algorithms can be so bizarre conceptually and with such baroque syntax that it’s hard to even know what they do. But with a decent example, it all starts to make sense. Here was a project I worked on where I had a simple struct L that contained two variables, x and y. I had them in a vector and I needed them sorted on x but made unique with respect to y. Here’s what I used for that.
#include <algorithm> void sort_unique(vector<L> &vL) { if (vL.size() < 2) return; // No need to bother if there is only one or zero items found. std::sort(vL.begin(), vL.end(), [](L a, L b) { return a.x < b.x; }); // Sort. vector<L>::iterator unique_resize_iter; // This iterator is needed to resize after confusing removals. unique_resize_iter= std::unique(vL.begin(), vL.end(), // Remove duplicates using... [](L a,L b){ return a.y == b.y; }); // ...this lambda predicate function. vL.resize(std::distance(vL.begin(),unique_resize_iter) ); // Repair container so it properly takes effect. } // End function: sort_unique()
You can read more about this and how its speed compares to the unix command line in this blog post I wrote.
Notable interesting functions include the following.
-
sort
-
merge (sorted ranges)
-
unique
-
replace
-
fill, generate (from a function)
-
for_each
-
reverse
-
min, max, minmax (both!)
-
search, find, find_if, search_n (multiples)
-
random_shuffle, shuffle (choose your own method)
-
set_union, set_intersection, set_difference
There is a whole bunch of stuff like that which can be found in the reference documentation.
Time
auto now= std::chrono::system_clock::now();
Random
Newish C++ has a civilized library for random number generation. To
use this fine library, I needed to compile with -std=gnu++11
.
#include <random> std::default_random_engine generator; std::uniform_int_distribution<int> distribution(0,255); int rand_red_value= distribution(generator);
This library seems pretty comprehensive. It features different PRNG engines and a bunch of distributions. See the official documentation for details.
Another way I’ve seen is something like this.
#include <time.h> #include <cstdlib> int main() { srand(time(NULL)); int target= rand()%100; // 0-99 std::cout << target << std::endl; return 0;}
Eigen Library Matrix Operations
-
Nice reference from official website.
VectorXd my_vector(2); // Vertical vector of two elements. my_vector << 10, 20; // Comma initializer sets coefficients. cout << my_vector << endl; // cout prints the vector MatrixXd my_matrix(2,2); // Initialize Matrix with 2x2 dimensions. my_matrix << 1, 2, 3, 4; // Set matrix to contain [[1,2],[3,4]]. cout << my_matrix << endl; // cout prints a matrix my_matrix(1,0) = 11; // Access by position. 2nd row, 1st column. my_matrix(1,1) = 12; // 2nd row, 2nd column my_matrix.row(i) = my_matrix2.col(i); // Get/set row/col vector. MatrixXd x x.setZero(rows, cols); // Fill with zeros. Also a `setOnes()`. x.setConstant(rows, cols, value); // Fill with value. x.setRandom(rows, cols); // Random values. MatrixXd my_matrix_t = my_matrix.transpose(); // Transpose. MatrixXd my_matrix_i = my_matrix.inverse(); // Inverse. MatrixXd my_mtimesv= my_matrix * my_vector; // Matrix multiplication.
Here’s a nice polynomial fitting routine which uses Eigen.
double polyeval(Eigen::VectorXd coeffs, double x) { double result = 0.0; for (int i = 0; i < coeffs.size(); i++) {result += coeffs[i]*pow(x, i);} return result; } Eigen::VectorXd polyfit(Eigen::VectorXd xvals, Eigen::VectorXd yvals, int order) { assert(xvals.size() == yvals.size()); assert(order >= 1 && order <= xvals.size() - 1); Eigen::MatrixXd A(xvals.size(), order + 1); for (int i = 0; i < xvals.size(); i++) { A(i, 0) = 1.0; } for (int j = 0; j < xvals.size(); j++) { for (int i = 0; i < order; i++) { A(j, i + 1) = A(j, i) * xvals(j); } } auto Q = A.householderQr(); auto result = Q.solve(yvals); return result; } Eigen::VectorXd xvals(6); Eigen::VectorXd yvals(6); xvals << 9.261977, -2.06803, -19.6663, -36.868, -51.6263, -66.3482; yvals << 5.17, -2.25, -15.306, -29.46, -42.85, -57.6116; auto coeffs= polyfit(xvals,yvals,3); std::cout<< "Y(16)=" << polyeval(coeffs,16) << endl;
This excellent resource explains a low impact way to do "Cubic Spline interpolation in C++ ".
Makefile
Here’s a very simple Makefile
that can be used on very simple
C++
programming tasks.
ALLSRCS:= $(wildcard *.cc) ALLEXEC:= $(ALLSRCS:.cc=) .PHONY:all clean all: $(ALLEXEC) %: %.cc g++ -o $@ $< clean: rm $(ALLEXEC)
Profiling
Wondering what the heck is taking so long in your program? You can get hints about what your program spends its time doing.
sudo apt-get install linux-tools
sudo bash -c "echo 0 > /proc/sys/kernel/kptr_restrict"
perf record ./yourprog --your-opts
perf report
18.28% yourprog yourprog [.] Plist::querypoint() ▒
13.85% yourprog yourprog [.] shallowanglecull(Llist*, Tlist*, float,▒
8.70% yourprog yourprog [.] Llist::querypointA() ◆
7.00% yourprog yourprog [.] Llist::querypointB() ▒
6.73% yourprog yourprog [.] Vector::compareto(Vector) ▒
5.76% yourprog yourprog [.] searchlinelist(Plist*, Plist*, Llist*) ▒
5.69% yourprog yourprog [.] operator==(Vector, Vector) ▒
5.55% yourprog yourprog [.] Vector::~Vector() ▒
3.92% yourprog yourprog [.] Tlist::BBinterfere(Llist*) ▒
3.59% yourprog yourprog [.] searchtrilist(Plist*, Plist*, Plist*, T▒
2.96% yourprog yourprog [.] Tlist::querylink() ▒
2.88% yourprog yourprog [.] Llist::queryA() ▒
2.26% yourprog yourprog [.] BBinthelper(float, float, float, float,▒
1.89% yourprog yourprog [.] Vector::gg_valX() ▒
1.25% yourprog yourprog [.] Llist::querylink() ▒
0.92% yourprog yourprog [.] Tlist::querypointC() ▒
0.91% yourprog yourprog [.] find_matching_pnum(unsigned int, Plist*▒
0.90% yourprog yourprog [.] Vector::Vector() ▒
...
Or if that isn’t good enough check out gprof.
Actually this hack is quite clever too. See the other suggestions there too (e.g. Valgrind).
Resources For Optimizing
-
A big list of dos and don’ts (e.g. "Prefer stdio [printf] to iostream [cout].")
-
Another list of recommendations. (e.g. "Jumps/branches are expensive.")
-
Another list of recommendations. (e.g. "Prefer preincrement over postincrement.")
-
An entire online book on the topic of C++ optimizing.
-
Bjarne and Lex talk about turning off the optimizer to get cleaner errors. Interesting talk throughout. 1h7m45s