This is a compilation of stuff that I wish I knew about C++ when starting to use it. If you know Java, you'll be fine, and it's mostly written from that perspective. This was made entirely in spite of my horrible data structures and algorithms class. I have nightmares about the professor on a regular basis and I turned into a full on insomniac. One day I went to bed late because I was working on a massive program I had due, then I couldn't fall asleep because of the program, then I finally fell asleep and dreamed of the program, and then I woke up before my alarm and thought of the program more. Someone said they have an A in the class once, and they were politely told that they should be hit by a bus. I got an A somehow. Please help me.
It's nice to see some output, so that's first! cout is responsible for output, and cin for input. Put << after cout and >> after cin. You can attach endl to the end to make a new line, or simply include \n in your quotation marks.
The first line is simply importing part of the standard C++ library. On the second line, "using namespace std" does not refer to sexually transmitted disorders (something you will never suffer from as a programmer), but instead refers to the namespace of the standard library. Without that, you would need to type std:: before several things, like cout, cin, vector, sort, etc. People battle to the death over whether to use it or not, but I'm just here to get a degree, so we're using namespace std. (People who actually use C++ do not recommend this. I am not a good programmer. Please keep this in mind throughout this guide.)
And yes, int main() is really just that. If you're like me and only got to work with Java for the beginning portion of your university career, this change will be very welcome. C++ requires semicolons after every line, so do keep that in mind.
#include <iostream> //for input/output using namespace std; int main() { int years; cout << "Enter any integer: " << endl; cin >> years; cout << "Cats can live up to " << years << " years!" << endl; }
Oh yeah, we actually have to compile it. Save this as a .cpp file, and then run g++ filename.cpp in the console. It'll tell you if you have syntax errors. Then, run it with ./a.out. If you hate the fact that it's called a.out, you can change this with g++ filename.cpp -o desiredname. As expected, this will read the integer you enter as an int, then output it saying cats can live up to that many years.
Since we're doing input and output, let's do file writing too - it's similar, and pretty easy. Just like before, you can use << in order to write directly into it, using whatever variable names or escape sequences that you can use with cout.
#include
#include <iostream> using namespace std; int main(){ //outputting to the file ofstream coolFactsOut; coolFactsOut.open("facts.txt"); coolFactsOut << "Debian was named after Ian Murdock's wife, who is named Debra. He put their names together, and out came Debian." << endl; coolFactsOut << "...Unfortunately, they divorced!"; coolFactsOut.close();//reading from the file ifstream coolFactsIn; coolFactsIn.open("facts.txt"); string line;//getline reads from coolFactsIn, and writes the line it sees to "line" getline(coolFactsIn, line); cout << "The first line of 'facts.txt': " << endl << line << endl; getline(coolFactsIn, line); cout << "The second line of 'facts.txt': " << endl << line << endl; coolFactsIn.close(); }
If you'd like to write to a file, use ofstream as the type. For reading, use ifstream. For both, do fstream. Make sure to close your file when you're done. You can get just one line at a time by using getline(). There are lots more details for files, though, so read the docs for this one. This is just the bare minimum!
#include <iostream> using namespace std; int main(int argc, char** argv) { cout << "Arguments entered according to argc: " << argc << endl;
// note that you have to start at 1 to get actual arguments for(int i = 1; i < argc; i++) { cout << "Argument " << i << ": "; cout << argv[i] << endl; }// checking for at least 1 argument if (argc >= 2) { // atoi casts strings to ints int maximumMeows = atoi(argv[1]); cout << "Maximum meows allowed before persecution: " << maximumMeows << endl; } }
Run this, and add as many extra int arguments as you'd like. This will output every argument that was entered.
argc is the total number of arguments entered, but note that simply inputting the executable name already counts as an argument. If you do ./a.out 1 2 3, argc's value will be 4. In the loop, we start i at 1 to make sure only our actual arguments are printed. Lastly, we use atoi when assigning the maximumMeows value to cast the string argument into an int. maximumMeows will not be declared or initialized if enough arguments haven't been entered.
addToNum() adds 3 to the given parameter. Its return type is an int, and it takes an int as a parameter.
#include <iostream> using namespace std; int main() { int i = 5; i = addToNum(i); cout << i << endl; } int addToNum(int toAdd){ return toAdd + 3; }
Try to compile this, and you'll get an error message like this:
test.cpp: In function 'int main()': test.cpp:5:9: error: 'printAddition' was not declared in this scope get fucked loser!!
Turns out you can't really call a function before it's defined. The C++ compiler runs top to bottom, so it doesn't know that the addToNum() function even exists yet when called. There are two ways to fix it:
1. Move the whole printAddition() function above the main function.
2. (Which is better): rip out the method signature, put it above the main line, and then define the whole function beneath main still, like this:
#include <iostream> using namespace std;
// this is the method signature int addToNum(int toAdd); int main() { int i = 5; i = addToNum(i); cout << i << endl; }// this is the actual definition of the method int addToNum(int toAdd){ return toAdd + 3; }
This is called a prototype. The compiler reads top to bottom, so if you try to call printAddition without a prototype or definition, the compiler simply has no clue what you're on about. (We can also move the function to a separate file altogether, which I'll talk about later.)
Here is a modified version of the above that returns no value, but modifies the value of toAdd instead:
#include <iostream> using namespace std; void addToNum(int &toAdd); int main() { int i = 5; addToNum(i); cout << i << endl; } void addToNum(int &toAdd){ toAdd = toAdd + 3; }
This is called passing by reference, and it lets you directly modify the value of the argument. You just need to use & in your method signature to do it.
#include <iostream> using namespace std; int main() { int size = 10; int arrayOfHatred[size]; for(int i = 0; i < sizeof(arrayOfHatred) / sizeof(arrayOfHatred[0]); i++){ cout << "Array of Hatred's value at " << i << ": " << arrayOfHatred[i] << endl; } }
Compile this and run it. Note that it compiles perfectly fine and has no runtime errors. Run it a few more times. You may get different values each time. Welcome to C++. If you forget to initialize a variable, it will not tell you, and whatever happens is up to the compiler. In this case, we only declared the array, but did not initialize any of the values inside it.
Don't forget that this can happen when you're starting out, especially if you've noticed that you're getting different results every time you run the same program.
(They're not that bad, I promise!)
Pointers do not store data. Instead, they store memory addresses. To make a pointer, you use *. To dereference a pointer and get the data that it refers to, you use * again. & grabs the actual memory address of a data value. Confusing in text? Yeah, a little bit. Here's an example.
#include <iostream> using namespace std; int main(){ int age = 20;
// using the asterisk to make a pointer: int* ageReference = &age; cout << "Age: " << age << endl;// using the asterisk to deference: cout << "Referenced age: " << *ageReference << endl; }
That prints out 20 for both. So what happens if we increment by one and print that?
age++; cout << "Age: " << age << endl; cout << "Referenced age: " << *ageReference << endl;
Now both will print out 21. Age has been incremented by one. Because ageReference is a pointer to the memory address of age, it will just read out whatever age's value is. Now we'll intentionally create a dangling pointer. Dangling pointers point to something that used to exist, but does not anymore. If it's not obvious, these aren't good.
int main() { int* reference; for(int i = 0; i < 5; i++) { reference = &i; } *reference++; cout << *reference << endl; }
Reference is not initialized when initially declared. Instead, inside the code block, we assign reference to the value of i 5 times. Then, when going out of the for loop, i no longer exists. We try to increment the value of reference and print it out, but the memory address that reference was pointing to isn't there at all. So, we get undefined behavior, and a random garbage number typically prints out instead. Be careful of this!
I will find a better link eventually, but for now, here is why pointers matter and when they would be used.
There is no god, but there is Valgrind, which is good enough. Install it on your Linux distro of choice - it has no Windows port.
Go back to the undefined behavior or dangling pointer snippet and compile it, but add a -g flag like this: g++ -g filename.cpp. That flag adds more debugging information - Valgrind will run without you doing this, but this lets it report specific lines to you. Now, do valgrind ./a.out. Valgrind is lovely in that it'll actually tell you that you're using an uninitialized value, and you'll see that it has tons to say about both of those snippets.
If you're getting a segfault error at runtime, try running Valgrind with it. Usually Valgrind will know what's up. I'm not the best at using it, but it's so very helpful. We'll also use Valgrind in a little bit to check for memory leaks.
C++ has objects. Here is a Cat class.
#include <string> using namespace std; class Cat { private: int age; string name; public: Cat(int age, string name){ this->age = age; this->name = name; } int getAge() { return age; } void changeName(string name) { this->name = name; } };
Whatever attribute or function you put in the private section will be, unsurprisingly, private, and cannot be accessed outside of the class. Whatever goes in public can be accessed normally. If you don't specify it, everything will default to private. Note that there is a semicolon at the end of the class declaration too.
Imagine you have a larger Cat class. You add a fluffiness attribute or maybe a hair color attribute. You want to write more functions for it, maybe a meow() or purr() function, because you wish you had a cat and you don't, so now you have this sad virtual cat. But it would get a little bit annoying to work with, no? Wouldn't you want to maybe split it into different files?
Here is a header file, named Cat.h:
#ifndef CAT_H #define CAT_H #include <string> #include <iostream> using namespace std; class Cat { private: int age; string name; public: Cat(int age, string name); int getAge(); void changeName(string name); void meow(); }; #endif
Here is an associated Cat.cpp file to go with that header file:
#include "Cat.h" Cat::Cat(int age, string name){ this->age = age; this->name = name; } int Cat::getAge() { return age; } void Cat::changeName(string name) { this->name = name; } void Cat::meow() { cout << name << " says: MEOW MEOW MEOW MEOW"; }
This is a lot more readable once you get the hang of it!
When writing a header file, you mostly strip out function definitions, and instead put in function prototypes. This is done exactly the same way you would put a function prototype at the top of main. You write the prototype in the header, and then define it in the cpp file. When you do define it, make sure to put the class name in front of the given function name. This is why Cat:: is in front of every function in the .cpp file. :: is the scope resolution operator, which specifies which scope you're looking in.
Look at the top and bottom of the header file. #infdef, #define, and #endif make up what is called a header guard. When you write a .cpp file for a header file, you have to include the given header file with #include <HeaderName.h>. The header guard's goal is to protect you from accidentally including the same header files multiple times, which is especially important when you start having subclasses - it's easy to lose track and forget of what is imported in each file.
Header files aren't just for classes. You can move any function prototypes you'd like there, structs, more #includes, and function definitions if you'd like.
Do you know Java? Do you remember the new operator whenever making an object? You don't have to use it as freely in C++. But, for every new operator you do use, you need a delete operator, or you will have a memory leak. A memory leak is when memory that has been allocated (for example, to create a new class or initialize a variable) isn't properly released, meaning the memory is simply lost.
Let's cause a memory leak using the Cat class from before.
#include "Cat.h" using namespace std; int main() {
// perfectly fine! Cat mei = Cat(5, "Mei");// memory leak! Cat* suki = new Cat(5, "Suki"); }
We can use Valgrind to check for leaks using valgrind --leak-check=full ./a.out. Sure enough, Valgrind is not happy about Suki:
==4321== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==4321== at 0x4843F73: operator new(unsigned long) (vg_replace_malloc.c:483) ==4321== by 0x10A2E9: main (test.cpp:9)
But Mei is totally fine. We didn't need to use the new operator to make the Cat object in this case. If we do use new, that becomes dynamically allocated memory that the programmer is then responsible for cleaning up instead. We can clean it up with delete suki.
Classes may need destructors, copy constructors, and copy assignment operators. Destructors live up to their name, and destruct any dynamically allocated data (data that you called new on). This may look like iterating through every node in a linked list and deleting them, or like deleting a dynamic allocated array.
I would highly recommend going off of documentation for this topic. That introduces the rule of three - if a class needs a destructor, a copy constructor, or a copy assignment operator, it will require all three the vast majority of the time.
Are you going to college? Do they forbid you from using normal, sane things from the standard library, and force you to do everything via arrays instead? If yes, skip this, because it'll hurt to read (and it hurts to write it). Whenever you search something related to arrays, you'll notice very quickly that every response is simply "use vectors instead". This is because vectors are amazing. You should use them instead of arrays.
Vectors automatically grow and shrink as needed and automatically manage their own memory. Arrays force you to not only know the size of your array at creation, but also to initialize every value in them when creating them.
#include
#include <iostream> using namespace std; int main() { vector oddNumbers = {1, 3, 5}; oddNumbers.push_back(7); oddNumbers.push_back(9); oddNumbers[4] = 9; cout << "The first odd number is: " << oddNumbers.front() << endl; for(int i = 0; i < oddNumbers.size(); i++) cout << oddNumbers[i] << endl; cout << "As far as I know, there are only " << oddNumbers.size() << " odd numbers in the world." << endl; }
You can add numbers to it! You can access it by index! You can get just the front or just the back! You can do more! What else is there to ask for? Go read more about them!
When outputting, do keep in mind that \n and endl (both ways to output a new line) are similar, but not identical. endl, though, flushes the output buffer every time, and \n does not. This has two minor applications. First, if you're debugging by outputting a bunch of print statements to find where you're crashing, you should seriously use Valgrind instead or any other way. But, if you won't, you should make sure that you're using endl to output your debugging statements. You may not get print statements up to the crash if you're using \n. Second, if you're writing to a file and speed matters, use \n, not endl. There is a noticable speed difference.
Please tell me if I did something wrong or used a horrible practice.
Here are two general resources on C++. Both are very readable and well made:
- https://en.cppreference.com/w/cpp
- https://cplusplus.com/doc/tutorial/
Here is a page on the C++ standard library headers, as well as all associated classes and functions:
- https://en.cppreference.com/w/cpp/standard_library
Here is a more tutorial-like resource:
- https://www.learncpp.com/