0% found this document useful (0 votes)
590 views

C++ Cheat Sheet

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
590 views

C++ Cheat Sheet

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 27
C++ Syntax Cheat Sheet Preface Since the C++ language varies so heavily between versions (e.g. C++0x, C++11, C+4+17, etc), | will preface this cheat sheet by saying that the majority of the examples here target C+#0x or c++11, as those are the versions that | am most familiar with. | come from the aerospace industry (embedded flight software) in which we purposefully don't use the latest technologies for safety reasons, so most of the code | write is in C++0x and sometimes C++11. Nevertheless, the basic concepts of C++ and object oriented programming still generally apply to both past and future versions of the language Table of Contents © C++ Syntax Cheat Sheet © Table of Contents © 1.0 C++ Classes = 1.1 Class Syntax = 1.1.1 Class Declaration ( .h file) = 1.1.2 Class Definition ( .cpp file) = 1.13 Class Utilization (Another «cpp file) = 1.14 Getters and Setters 1.2 Inheritance = 1.2.1 Rectangle Declaration ( .h file) = 1.2.2 Rectangle Definition ( .cpp file) = 1.2.3 Rectangle Utilization (Another .cpp file) 1.3 Class Polymorphism = 1.3.1 Motivation = 1.32 Virtual Methods 1.4 Special Methods (Constructor, Destructor, ...) = 1.4.1 Constructor and Destructor = 1.4.1.1 Use of explicit in Constructors = 1.4.1.2 Member Initializer List = 1.42. new and delete = 1.43. Copy Constructor and Copy Assignment = 1.4.4, Move Constructor and Move Assignment 1.5 Operator Overloading 1.6 Templates © 2.0 General C++ Syntax 2.1 Namespaces 2.2 References/Pointers 2.3 Keywords = 2.3.1 General keywords = 2.32 Storage class specifiers = 2.33 const and dynamic Cast Conversion 2.4 Preprocessor Tokens 2.5 Strings 2.6 iterators 2.7 Exceptions 2.8 Lambdas 1.0 C++ Classes 1.1 Class Syntax 1.1. Class Declaration ( .h file) Here's a simple class representing a polygon, a shape with any number of sides. The class declaration typically goes in the header file, which has the extension .h (or, less commonly, .hpp to distinguish from C headers). The declaration gives the class name, any classes it may extend, declares the members and methods, and declares which members/methods are public, private, or protected. You can think of the declaration as sort of saying: “there will be a thing and here's how it will look like”. The declaration is used to inform the compiler about the future essence and use of a particular symbol. // File: polygon.h include class Polygon { // Private members and methods are only accessible via methods in the class definiti private: int num_sides; // Nunber of sides // Protected members and methods are only accessible in the class definition or by ¢ protected: std tring name; // Name of the polygon // Public menbers and methods are accessible to anyone who creates an instance of th public: // Constructors Polygon(const int num_sides, const std::string & name); // <--- This constructor // Getters and Setters int GetNumSides(void) const; void SetNumSides(const int num_sides) ; std::string & GetNane(void) const; void SetName(const std::string & name); 3 J] <==» Don"t forget the semicolon! 1.1.2 Class Definition ( .cpp file) The class definition typically goes in the .cpp file. The definition extends the declaration by providing an actual implementation of whatever it is that you're building. Continuing the example from the declaration, the definition can be thought of as saying: "Right, that thing I told you briefly about earlier? Here's how it actually functions”. The definition thus provides the compileable implementation, // File: polygon. cpp #include J] é--> Required for std::string include "polygon.h" // <--- Obtains the class declaration // Constructor // You must scope the method definitions with the class name (Polygon: :) // Also, see the section on the ‘explicit’ keyword for a warning about constructors Polygon: :Polygon(const int num_sides, const std::string & nane) ‘this->num_sides = num_sides; // ‘this’ is a pointer to the instance of t! ‘this-oname = name; // In this case you need to use ‘this->... // Get the number of sides int Polygon: :GetNunSides(void) const { // The ‘const’ here tells the conpiler that return this-snum_sides; // Set the number of sides void Polygon: :SetNunSides(const int num_sides) { this->num_sides = num_sides; // Get the polygon name string & Polygon: : return this->name; // Set the polygon name void Polygon: :SetName(const std: this-pnane = name; The getters and setters here don’t do much, but you could imagine limiting the number of sides such that it must have at least 3 sides to be a useful polygon, in which case you could enforce that in Polygon: :SetNunSides() . Of course, you'd also need to modify the constructor, which could then call setnunsides() instead of setting the variable directly. NOTE: Regarding the use of this-> ina class definition, there are places where it's strictly necessary for readability, e.g. when your method parameter shares the exact same name as a member variable, you use this-> to avoid what's called shadowing, However, some prefer to always use this-> explicitly regardless of whether it's necessary. 1.1.3 Class Utilization (Another .cpp file) // File: main. cpp include #include #include "Polygon.h” // <--- Obtains the class declaration int main(int argc, char * argv(]) ¢ // Create a polygon with 4 sides and the name “Rectangle” Polygon polygon = Polygon(4, “Rectangle"); // Check number of sides -- Prints “Rectangle has 4 sides” std::cout << polygon.GetNane() << " has " << polygon.GetNumSides() << " sides"<< // Change number of sides to 3 and rename to “Triangle” polygon. SetNunSides(3); polygon. SetNane("Triangle") ; » 1.1.4 Getters and Setters A shortcut often used for Getters/Setters is to define them in the class declaration ( .h ) file as follows: // File: car-h #include class Car { private: int year; std::string make; public: us int GetYear(void) const { return this->year; } void Setvear(const int year) { this->year = year; } std::string & GetNeke(void) const { return this->make; } void SetMake(const std::string & make) { this->make = make; } This is often used for very basic getters and setters, and also for basic constructors. In contrast, you'll nearly always find more complex methods defined in the cpp file, One exception to this is with class templates, in which the entire templated class declaration and definition must reside in the header file. Another important consideration: If you have getters and setters for all of your members, you may want to reconsider the design of your class. Sometimes having getters and setters for every member is indicative of poor planning of the class design and interface. In particular, setters should be used more thoughtfully. Could a variable be set once in the constructor and left constant thereafter? Does it need to be modified at all? Is it set somewhere else in another method, perhaps even indirectly? 1.2 Inheritance A class can extend another class, meaning that the new class inherits all of the data from the other class, and can also override its methods, add new members, etc. Inheritance is the key feature required for polymorphism. It is important to note that this feature is often overused by beginners and sometimes unnecessary hierarchies are created, adding to the overally complexity. There are some good alternatives such as composition and aggregation, although, of course, sometimes inheritance is exactly what is needed Example: the class Rectangle can inherit from the class Polygon . You would then say that a Rectangle extends froma Polygon , or that class Rectangle is a sub-class of Polygon In plain English, this means that a Rectangle is a more specialized version of a Polygon Thus, all rectangles are polygons, but not all polygons are rectangles. 1.2.1 Rectangle Declaration ( .h file) // File: rectangle.h #include // <--> Explicitly include the string header, even though pc include “polygon.h” // <--- You must include the declaration in order to extend // We extend from Polygon by using the colon (:) and specifying which type of inher // will be used (public inheritance, in this case) class Rectangle : public Polygon { private: int length; int width; // <--> NOTE: The menber variables ‘num_sides' and ‘name’ are already inherited WW it's as if we sort of get them for free, since we are a sub-class public: J/ Constructors explicit Rectangle(const std::string &nane) Rectangle(const std::string &name, const int length, const int width); // Getters and Setters const int GetLength(void) const { return this->length; } void SetLength(const int) { this->length = length; } const int Gethlidth(void) const { return this->width; } void Sethidth(const int) { this->width = widths } // <--> NOTE: Again, the getters/setters for ‘num_sides' and ‘name’ are already // Other Methods const int Area(void) const; oH NOTE: The inheritance access specifier ( public , protected, or private ) is used to determine the type of inheritance. If this is omitted then private inheritance is used by default. Public inheritance is by far the most common type of inheritance. 1.2.2 Rectangle Definition ( .cpp file) // File: rectangle. cpp #include “rectangle.h” // <--- Only need to include ‘Rectangle’, since ‘Polygon’ is // This constructor calls the superclass (Polygon) constructor and sets the name an Rectangle: :Rectangle(const std::string &name, const int length, const int width) : ‘this->length = length, this-owidth = width; // This constructor calls the superclass (Polygon) constructor, but sets the length // The explicit keyword is used to restrict the use of the constructor. See section explicit Rectangle: :Rectangle(const std::string &name) : Polygon(4, name) this->length = 1; ‘this-owidth = // Compute the area of the rectangle int Rectangle: :Area(void) const { return length * width; // <--> Note that you don't explicitly need 1.2.3 Rectangle Utilization (Another .cpp file) J/ File: main. cpp #include Hinclude "Rectangle. int main(int argc, char *argv[]) { Rectangle rectangle = Rectangle("Square", 6, 6); // Prints “Square has 4 sides, and an area of 36" std::cout << rectangle.GetName() << " has " << rectangle.GetNunSides() << side 1.3 Class Polymorphism Polymorphism describes a system in which a common interface is used to manipulate objects of different types. In essence various classes can inherit from a common interface through which they make certain guarantees about which methods/variables are available for use. By adhering to this common interface, one can use a pointer to an object of the base interface type to call the methods of any number of extending classes. Using polymorphism one can say "I don't care what type this really is; | know it implements Foo() and Bar() because it inherits from this interface”, which is a pretty nifty feature. The virtual keyword is used to ensure runtime polymorphism for class methods. Additionally, an overriding method can be forced by the compiler by not providing a default implementation in the interface, which is done by setting the method to = @, as, will be shown later. 1.3.1 Motivation Let’s consider a similar class hierarchy using shapes as previously discussed. Considering a shape to be any 3 or more sided polygon from which we can compute certain attributes (like the shape's area), let's extend from it to create a rectangle class from which we can set the length/width and a circle class in which you can set the radius. In both cases, we want to be able to compute the area of the shape. This is a key observation that we will expand upon later. For now, this (poorly implemented) shape class will suffice: // File: shape.h include // needed for MPI constant class Shape { // We'll leave Shape enpty for now... not very interesting yet h class Rectangle : public Shape { private: double length; double width; public: // Constructor using a member initializer list instead of assignment in the meth Rectangle(const double w, const double 1) : width(w), length(1) ( // Compute the area of a rectangle double Area(void) const { return length * width; b class Circle : public Shape { private: double radius; public: explicit Circle(double r) : radius(r) {} JJ Compute the area of a circle double Area(void) const { return MPI * radius * radius; // pitr*2 u NOTE: As shown here, you can put multiple classes in a single header, although in practice unless you have a good reason for doing so it's probably best to use a separate header file per class. NOTE: I'm not using default value initialization for member variables (i.e. double Jength = 8; ) and I'm using parentheses () instead of braces {) for the initializer list since older compilers (pre-C+ +11) may not support the new syntax. So, we have our two classes, Rectangle and circle , but in this case inheriting from shape isn't really buying us anything. To make use of polymorphism we need to pull the common Area() method into the base class as follows, by using virtual methods 1.3.2 Virtual Methods Imagine you want to have a pointer to a shape with which you want to compute the area of that shape. For example, maybe you want to hold shapes in some sort of data structure, but you don't want to limit yourself to just rectangles or just circles; you want to support all objects that call themselves a ‘Shape’. Something like: Rectangle rectangle(2.0, 5.0); Circle circle(1.0); // Point to the rectangle Shape * unknown_shape = &rectangle; // Could point to *any* shape, Rectangle, Circle unknown_shape->Area(); // Returns 10. // Point to the circle unknown_shape = &circle; unknown-shape->Area(); // Returns 3.14... The way to achieve this is to use the virtual keyword on the base class methods, which specifies that when a pointer to a base class invokes the method of an object that it points to, it should determine, at runtime, the correct method to invoke. That is, when unknown_shape points to a Rectangle it invokes Rectangle: : points toa Circle itinvokes Circle: :Area() . rea() and if unknown_shape Virtual methods are employed as follows: include class Shape { publi // Nirtual destructor (VERY IMPORTANT, SEE NOTE BELOW) virtual ~Shape() {} // Virtual area method virtual double Area() const { return @. h class Rectangle : public Shape { private: double length; double width; publi Rectangle(double w, double 1) : width(w), length(1) () // Override the Shape::Area() method with an implementation specific to Rectangl double Area() const override { return length * widths class Circle : public Shape { private: double radius; publi explicit Circle(double t) : radius(r) {} // Override the Shape::Area() method with an implenentation specific to Circle uw // NOTE: there is an ‘override’ keyword that was introduced in Cr#i1 and is opti // to enforce that the method is indeed an overriding method of a virtual base m // and is used as follows: double Area() const override { return MPI * radius * radius; // pitrs2 Bb es , NOTE: It is very important that a default virtual destructor was included after adding the virtual area() method to the base class. Whenever a base class includes even a single virtual method, it must include a virtual destructor so that the correct destructor(s) are called in the correct order when the object is eventually deleted. This is called runtime polymorphism because the decision of which implementation of the Area() method to use is determined during program execution based on the type that the base is pointing at. It is implemented using the virtual table mechanism. in a nutshell: it is a little more expensive to use but it can be immensely useful. There is also compile-time polymorphism. Here is more on the differences between them. In the example above, if a class extends from shape but does not include an override of Area() then calling the Area() method will invoke the base class method which (in the implementation above) returns 0.0 . In some cases, you may want to enforce that sub-classes implement this method. This is done by not providing a default implementation, thus making it what is called a pure virtual method. class Shape { public: virtual ~Shape() () virtual double Area() const u In general a class with only pure virtual methods and a virtual destructor is called an abstract class or interface and is typically named as such (e.g. suttontnterface , or similar) An interface class guarantees that all extending classes implement a specific method with a specific method signature. 1.4 Special Methods 1.4.1 Constructor and Destructor All classes have at least one constructor and a destructor, even if they are not explicitly defined. The constructor and destructor assist in managing the lifetime of the object. The constructor is invoked when an object is created and the destructor is invoked when an object is destroyed (either by going out of scope or explicitly using delete ). The constructor establishes a class invariant, a set of assertions guaranteed to be true during the lifetime of the object, which is then removed when the destructor is called. 1.4.11 Use of explicit in Constructors The explicit keyword should be used in single-argument constructors to avoid a situation in which the constructor is implicitly invoked when a single argument is given in place of an object. Consider the following Array class: class Array { private: int size; public: // Constructor Array(int size) { this-psize = size; // Destructor wArray() {} // Print the contents of the array Print(const Array & array) { Wives h The following is now legal but ambiguous: Array array = 12345; It ends up being the equivalent of this Array array = Array(12345); Perhaps that's okay, but what about the following array.Print (12345); Uh-oh. That's now legal, compileable code, but what does it mean? It is extremely unclear to the user. To fix this, declare the single-argument Array constructor as explicit class Array { int size; publi. explicit Array(int size) { this-psize = size; Ws Now you can only use the print method as follows: array.Print (Array(12345)); and the previous array.Print(12345) is now a syntax error. 1.4.1.2 Member Initializer Lists Member initializer lists allow you to initialize member variables in the definition of a method. This turns out to provide some performance benefits for class-type member variables, since a call to the default constructor is avoided. For POD (plain old data) like ints and floats, though, itis the same as initializing them in the body of the method class Car { private: int year; int miles; std::string make; publi Car(const int year, const int miles, const std::string & make) : year(year), mil h Using the initializer list is basically the same as the following more verbose constructor n, notwithstanding the note above regarding performance: plement: Car(const int year, const int miles, const std this->year = years this-omiles = miles; ‘this-omake = makes string & make) { Since C++11 initializer lists have some added functionality and curly braces {) can be used instead of parentheses () in the initializer list, but to maintain compatibility with older compilers you may want to use parentheses. The same applies in general to initialization syntax when creating objects. Many people prefer braces, and in some cases it’s necessary (e.g. vector containing [100, 1] or a vector of one hundred 1s?), but to support older compilers you may consider using parentheses. 1.4.2 new and delete The new and delete operators (and their array counterparts, new{] and delete[]) are operators used to dynamically allocate memory for objects, much like C's malloc() and free() More on these operators can be found here. When manually allocating memory dynamically, itis the responsibility of the programmer to manage the memory and properly delete objects that have been allocated. 1.4.3 Copy Constructor and Copy Assignment Copy constructors and copy assigment operators allow one object to be constructed or assigned a copy of another object directly: Foo a(10)3 Foo b(a);// (1): Copy via constructor Foo c 5 // (2): Copy via assignment operator This is accomplished by supplying a copy constructor and an assigment operator overload, both of which have a special syntax where they accept a const reference to an object of their same type. class Foo { private: int data; public: // Default (no argument) constructor Foo() : data(a) (} // Single argument constructor explicit Foo(const int v) : data(v) {) /1 Copy constructor Foo(const Foo & f) : data(f.data) {} // Copy assignment operator Foo & operator=(const Foo & f) { data = f.data; return *this; us Note that the compiler will always provide a default constructor, a default copy constructor, and a default copy assignment operator, so for simple cases (like this trivial example) you will not have to implement them yourself. More info on this can be found here. 1.4.4 Move Constructor and Move Assignment Sometimes instead of performing a copy you instead wish to completely move data from one object to another. This requires the use of a move constructor and move assignement operator. class Movable { private: Foo * data_ptr; public: Movable(Foo data) : data_ptr(new Foo(data)) {} // Move constructor Movable(Movable && m) { // Point to the other object's data data_ptr = m.data_ptr; // Remove the other object's data pointer by // setting it to nullptr m.data_ptr = nullptr; // Move assignment operator Movable & operator=(Movable && m) { data_ptr = m.data_ptr; m.data_ptr = nullptr; return *this; ~Movable() { delete data_ptr; u The move constructor and assignment operator can be used as follows: Movable Bar() { We > int main() ¢ Movable a(Bar()); // Using the move constructor Movable b = Bar(); // Using the move assignment operator Since Bar() creates an object that won't be used elsewhere and is deleted after the call, we can use the move constructor or move assignment operator to move the data to our object. A programming idiom called ‘copy and swap" makes use of the move constructor and can be a useful idiom. 1.5 Operator Overloading Operators such as +, -, *, etc. are familiar and ubiquitous when working with simple data types like integers and floating point numbers. These operators as well as others can also be overloaded to provide a clear syntactic meaning to your own classes. For example, when working with linear algebra you can overload the + operator to perform an element-wise addition of two vectors. Here's a brief example using complex numbers that allows you to use the + and - operators to easily add and subtract two complex numbers. There are two main ways to do operator overloading. The first is using normal member functions. The second uses the friend keyword and non-member methods that have access to the private member variables of the class. Using normal member functions (requires a getter method for the member variables) // File: conplex.h class Complex { private: double r = 0.0; // Real part, defaults to 0.0 double i = 0.0; // Imaginary part, defaults to 0.0 public: Complex(const double r, const double 4) : r(r), (4) {} JJ Accessor methods double GetReal(void) const { return rs } double GetImaginary(void) const { return 43 } // + Operator Complex operator+(const Complex & a, const Complex & b) { return Complex(a.GetReal() + b.GetReal(), a.GetImaginary() + b.GetImaginary! JI ~ Operator Complex operator=(const Complex& a, const Complex& b) return Complex(a.GetReal() - b.GetReal(), a.GetImaginary() - b.GetImaginary! h Using friend methods // File: conplex.h class Complex { private: double r = 0.0; // Real part, defaults to 0.0 double i = @.0; // Imaginary part, defaults to 0.0 publi Complex(const double r, const double i) : r(r), i(i) {} JJ + Operator (declaration only) friend Complex operator+(const Complex & a, const Complex & b); JJ ~ Operator (declaration only) friend Complex operator-(const Complex& a, const Complex& b); b // These are NOT member functions // They can also be defined inside the class body but leaving them outside // is a clearer reminder that they are not part of the class Complex operator+(const Complex & a, const Complex & b) return Complex(a.r + b.r, a.i + b.i); Complex operator-(const Complex& a, const Conplex® b) return Complex(a.r - ber, acd = bei); In either case, the new operators can be used as follows: int main() { Complex a(1, 2); // 1+ 24 Complex b(5, 3); // 5 +34 Complex « Complex a+b; // 645i a-bs // -4- ti It's also often useful to overload the output stream operator to provide a custom output string displaying the object's internal state in a human-readable format. This is done by overloading the << operator and requires using the functionality. include class Complex { private: 7 publi Move friend std::ostream & operator<<(std::ostream & os, const Complex & c); h // Definition // Again, this is NOT a menber function std: :ostream & operator<<(std::ostream & os, const Complex & c) { 0s Kn KK KK CLE KK return os; , int main() { Complex a {1, 2}; Complex b (5, 3}; std::cout << a; // Prints: 1+ 2i std::cout << a +b; // Prints: 6 + Si You can also similiarly overload the input stream operator ( >» ), and can read more about, the various operators here. 1.6 Templates Templates are a very powerful abstraction allowing you to generate compile-time methods/classes/etc. for any number of types while writing only one implementation, Say you have a method that adds two floating point number together, and another to add two integers together: double Add(const double a, const double b) { return a + b5 int Add(const int a, const int b) { return a +b That's great, but since both floating point numbers and integers implement the + operator you can use a template to instead write one generic implementation of a method that can operate on doubles, ints, floats, and (in this case) any other type that implements the + operator. A simple templatized version of add would look something like this: template // T becones whatever type is used at conpile-tine T Add(const T & a, const T & b) { return a +b; // The type T must support the + operator + // Usages int main() { Add(3, 5)5 // int version Add(3.2, 5.8); // double Add(3.45#, 5.0F); // implicit float version: we leave off the here Complex a (1, 2}; // Custom class Complex b (5, 3}; Add(a, b)3 // Works because we added support for the + operator! } < » In this simple example the compiler would generate four different methods, one for each type. Templating allows you to write more concise and modular code at the expense of generating a larger executable (code bloat) Templates are especially useful to create class templates. Class templates must be completely defined in a single header file // File: storage.h template // <--> ‘class’ is synonymous with ‘typename’ class Container { private: T data; public: explicit Container(const T @ d) : data(d) {} h // Usage int main() { Containercint> a(1); Container b(10.0F); Container> c(a); NOTE: More coming soon on templates. Read more about templates here and here, 2.0 General C++ Syntax 2.1 Namespaces Ina large production project you may have thousands of symbols for various types, variables, methods, and so on. To avoid symbol names conflicting with one another you can use namespaces to logically separate symbol names in to broad categories. Namespaces are an inherent feature of C++; when you create a class and refer to a method as ClassNane::Method() you are essentially using a namespace feature intrinsic to classes. For a brief namespace example, suppose that you have two data structures, both of which implement a Node class. In the following code, namespaces are used to allow the compiler (and the programmer) to distinguish between the two types // File: list-h namespace list { template struct Node { Node * next; Node = prev; T data; u 35 // namespace J/ File: bst-h namespace bst { template struct Node { Node * left; Node * right; T data; 35 // namespace // File: main.cpp include “list.h" #include “bst.h" int main() { List: :Nodecint> a; bst::Nodecint> bs h The standard C++ library uses the namespace std, e.g, std::cout, std::string, std::endl , etc. While you can use a using namespace foo; directive to address symbols directly in the foo namespace without prefixing the foo:: qualifier, this is generally considered bad practice as it pollutes the global namespace and sort of undermines the point of using namespaces in the first place. #include using namespace std; cout << "Hello, World” << endl; BAD: pollutes the global namespe #include std::cout << "Hello, World" << std::endl // <--~ GOOD: It's clear that you're usi < » 2.2 References and Pointers Those familiar with C will be very intimately acquainted with pointers. C++ adds the concept of references, which is a powerful way to have some of the features of pointers while avoiding some of the pitfalls. Later versions of C++ also add smart pointers, which allow for better memory management and scoping via st std::shared_ptr, and sti inique_ptr , weak_ptr , as compared to traditional raw pointers. Raw pointers in C++ behave exactly the same way as they do in C: a pointer variable stores the address of whatever it is pointing to. You can think of pointers as essentially storing a link to another piece of data. You can access the data that the pointer points to with the - > operator, or dereference it with the * operator. References are more akin to an alias, References cannot be NULL or nvliptr , and references cannot be reassigned to reference something else after they have been created. Additionally, references do not take up extra memory; they share the same address as whatever they reference to, References cannot have multiple levels of indirection (pointers can), and there is no reference arithmetic like there is for pointers. You can access the underlying data of a reference directly by using the reference itself: that is, ifit's a reference to an integer it can be used as an integer. Ifit's a reference to a class you can access the class members directly with the . operator. Although pointers are incredibly powerful, references are generally much safer, especially when passing objects to methods using pass-by-reference. It is very common in C++ code to pass an object as a const reference (if the data should be unmutable within the method) or a non-const reference rather than a raw pointer as is required in C. More on references vs pointers here. In the following code, assume a 32-bit system, in which case the size of a pointer variable is 4 bytes, and that the stack grows towards higher memory addresses. // Pointers int a = 10; // Ends up at memory address "@x2Ae@0084", for € int b = 203 // Ends up at memory address "@x2A000088" int * ptr = nullptr; // pte is a separate variable whose type is ‘poi printf ("ptr = Xp\n"); ptr << std::endl; *ptr << std::endl; ptr << std::endl; *ptr << std // References int a = 10; int b = 20; int & ref_a = a; int & ref_b = b; std::cout << ref_a << st std::cout << ref_b << std: endl; endl; std::cout << &ref_a << std::endl; std::cout << &ref_b << std::endl; ret std: std reout << ref_a << std::endl; cout << a << std::endl; int & ref_c; // Prints: exe // The value of ptr is now the address of the ve // Prints: ex2aeeee84 // Prints: 18 // The value of ptr is now the address of the ve // Prints: @x2aee0088 11 Prints: 28 // Ends up at memory address "@x2aeeees4", for € // Ends up at memory address "@x2Aeee88" // vef_a is an alias of (reference to) the varia // vef_b is an alias of (reference to) the varie // Prints: 18 Uf Prints: 28 // Prints: 1) Prints: ex2aeeee84 ex2a000088 // SETS THE VALUE OF ‘a’ TO THE VALUE OF ‘b'! I) Prints: 28 // ALSO PRINTS: 26 ! // ERROR! References must be initialized at thei Perhaps the most widely used aspect of references is to pass objects by reference (sometimes constant reference) to a method. To avoid hammering the stack with large objects when you pass them by value it is nearly always preferrable to pass by reference, which is the term used when using either a reference or a pointer. Using a reference allows you to pass any size object by reference, while still allowing you to access the object directly. // Pass by reference using a const reference void Foo(const Bar & bar) { int a = bar.GetValue(); if (bar.SomeMethod()) { Move ? bar.SetValue(10);// ERROR! Cannot modify a const reference! > // Pass by reference using a non-const reference void Foo(Bar & bar) { int a = bar.GetValue(); if (bar.SomeMethod()) { Wives , bar.SetValue(1@);/// Modifies ‘bar’ and thus whatever ‘bar’ references By passing an object by reference using a reference instead of a pointer you: ‘* Don't need to check for NULL or nuliptr since references cannot be null * Can access the referenced object's data directly instead of using the -> operator or dereferencing a pointer ‘+ Make it clearer which parameters are meant to be input parameters and which are meant to be output parameters by using const to denote strictly input parameters * Gain the benefits of both passing by value and passing by reference since you don't need to use a lot of memory on the stack for your object, Thus, passing by reference using a const. reference is essentially the same as passing by value, but you avoid copying the object onto the stack. Passing by reference using a non- const reference is essentially the same as passing by reference using a pointer, but you are guaranteed that it is not null and it's as if the pointer is effectively dereferenced 2.3 Keywords Reference 2.3.1 General Keywords asm auto const constexpr (since C++11) explicit export (until C++17) extern (language linkage) friend inline mutable noexcept (operator) noexcept (function specifier) nullptr override static (class member specifier) texplate this virtual (function specifier) virtual (base class specifier) volatile 2.3.2 Storage Class Specifiers Reference © auto (until C++17) © register (until C++17) © static © extern © thread_local (since C++11) 2.3.3 const and dynamic Cast Conversion © const_cast © dynamic_cast 2.4 Preprocessor Tokens © if : Preprocessor version of if(...) * HeLif : Preprocessor version of else if(. * Helse : Preprocessor version of else © #endif : Used to end an #if, #ifdef , or #ifndet © defined() : Returns true if the macro is defined © ifdef Same as #if defined(...) © Hifndet :Same as #if Idefined(...) ‘* #define : Defines a text macro. See here for full explanation, including macro functions and predefined macros. ‘© undef : Un-defines a text macro ‘© include : Includes a source file ‘* fine : Changes the current file name and line number in the preprocessor * #error : Prints an error message and stops compilation ‘© #pragna ; Non-standard, can be used instead of header guards ( #ifndef HEADER ...) 2.5 Strings (std tring) Reference 2.6 Iterators (std: :iterator<...>) Reference 2.7 Exceptions Reference 2.8 Lambdas Reference

You might also like