Introduction - krishnaramb/cplusplus GitHub Wiki
- The Basic
- Constants
- Pointers, Arrays, and Loops
- Important uses of reference
- exception and assertion
- Function
- structure copy constructor
- C++ offers variety of notations for expressing initialization, such as =
- Other universal form based on curly-brace-delimited initilier list:
#include <iostream>
using namespace std;
int main()
{
  int  d1{2.5};// gives out compilation error
  int d1 = {2.5}; // same as above, here '=' is redundant
  int d2 = 2.5; // d2 will hold 2
  cout << d1;
}- The =form is traditional and dates back in C, but if in doubt, use the general{}-list form. If nothing else, it saves you from the conversions that lose information( narrowing conversion).
  int i1 = 7.2; // i1 becomes 7 (surpr ise?)
  int i2 {7.2}; // error : floating-point to integer conversion
  int i3 = {7.2}; // error : floating-point to integer conversion (the = is redundant)- 
Note that a constant cannot be left uninitialized. A variable should only be left uninitialized in extremely rare condition. 
- 
user defind types (such as string, vector, Matrix, Motor controller) can be defined to be implicitly initialized? How?? 👊 ---> wait for constructor 
- 
When defining a variable, you don’t actually need to state its type explicitly when it can be deduced from the initializer: 
auto b = true; // a bool
auto ch = 'x'; // a char
auto i = 123; // an int
auto d = 1.2; // a double
auto z = sqrt(y); // z has the type of whatever sqr t(y) retur ns- 
With auto, we use the=because there is no potentially troublesome type conversion involved. 👍
- 
We use autowhere we don’t hav e a specific reason to mention the type explicitly.We want to be explicit about a variable’s range or precision (e.g., double rather than float). ‘‘Specific reasons’’ include:- The definition is in a large scope where we want to make the type clearly visible to readers of our code.
- We want to be explicit about a variable’s range or precision (e.g., double rather than float).
 
The important point is, Using auto, we avoid redundancy and writing long type names. This is especially important in generic programming where the exact type of an object can be hard for the programmer to know and the type names can be quite long. (more on iterators)
C++ support two notion of immutability.
- 
const: meaning roughly ‘‘I promise not to change this value.’’ This is used primarily to specify interfaces, so that data can be passed to functions without fear of it being modified. The compiler enforces the promise made byconst
- 
constexpr: meaning roughly ‘‘to be evaluated at compile time.’’ This is used primarily to specify constants, to allow placement of data in read-only memory (where it is unlikely to be corrupted) and for performance. const int dmv = 17; // dmv is a named constant
const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4∗square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§1.8)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expressionchar v[6];  // array of 6 characters
char∗p;    //pointer to character
char∗ p = &v[3]; // p points to v’s four th element
char x = ∗p; //*p is the object that p points toConsider copying ten elements from one array to another:
//program 1
void copy_fct()
{
int v1[10] = {0,1,2,3,4,5,6,7,8,9};
int v2[10]; // to become a copy of v1
for (auto i=0; i!=10; ++i) // copy elements
v2[i]=v1[i];
// ...
}//program 2
void print()
{
int v[] = {0,1,2,3,4,5,6,7,8,9};
for (auto x : v) // for each x in v
cout << x << '\n';
for (auto x : {10,21,32,43,54,65})
cout << x << '\n';
// ...
}Note that we don’t have to specify an array bound when we initialize it
with a list. The range-for-statement can be used for any sequence of elements
If we didn’t want to copy the values from v into the variable x, but rather just have x refer to an element, we could write:
void increment()
{
int v[] = {0,1,2,3,4,5,6,7,8,9};
for (auto& x : v)
    ++x;
// ...
}
Explain why the following code doesn't work?
// Example program
#include <iostream>
#include <string>
using namespace std;
void increment(int x[]);
int main()
{
    int a[] = {1, 2,3};
    increment(a);
}
void increment(int array[]){
    for (auto x:array)
        cout<<x;
}The solution is, when you pass an array to a function, it decays to a pointer. So in the increment function array is just a decayed pointer. It doesn't know the side of array. since the for statement for (auto x:array) requires undecayed array for finding the end of this array, it will not work if arary is a pointer.
In a declaration, the unary suffix & means ‘‘reference to.’’ A reference is similar to a pointer,
except that you don’t need to use a prefix ∗ to access the value referred to by the reference. Also, a reference cannot be made to refer to a different object after its initialization. More on References.
- References are particularly useful for specifying function arguments. For example
void sort(vector<double>& v); // sort vBy using a reference, we ensure that for a call sort(my_vec), we do not copy my_vec and that it
really is my_vec that is sorted and not a copy of it.
When we don’t want to modify an argument, but still don’t want the cost of copying, we use a
const reference. For example:
double sum(const vector<double>&)- Functions taking const references are very common.
- When used in declarations, operators (such as &, ∗, and [ ]) are called declarator operators:
T a[n]; // T[n]: array of n Ts
T∗ p; // T*: pointer to T
T& r; // T&: reference to T
T f(A); // T(A): function taking an argument of type A returning a result of type TException reports errors found at the run time. If an error can be found at compile time, it is usually prefable to do so. Assertion will help you catch the error at compile time thus saving time
- Many functions with the same name, but different arguments 🌿
- The function called is the one whose arguments match the invocation
void printOnNewLine(int x)
{
    cout << "Integer: " << x << endl;
}
void printOnNewLine(char *x)
{
    cout << "String: " << x << endl;
}- printOnNewLine(3) prints “Integer: 3”
- printOnNewLine(“hello”) prints “String: hello”
void printOnNewLine(int x)
{
    cout << "1 Integer: " << x << endl;
}
void printOnNewLine(int x, int y)
{
    cout << "2 Integers: " << x << " and " << y << endl;
}- printOnNewLine(3) prints “1 Integer: 3”
- printOnNewLine(2, 3) prints “2 Integers: 2 and 3”
- Note that function declaration need to occur before invocation
int foo()
{
      return bar()*2; // ERROR - bar hasn’t been declared yet
}
int bar()
{
      return 3;
}- Solution 1: reorder function declarations
int bar()
{
    return 3;
}
int foo()
{
    return bar()*2; // ok
}- Solution 2: use a function prototype; informs the compiler you’ll implement it later
int bar(); // function prototype
int foo()
{
    return bar()*2; // ok
}
int bar()
{
    return 3;
}- Function prototypes should match the signature of the method, though argument names don’t matter
int square(int); //function prototype
int cube(int x)
{
    return x*square(x);
}
int square(int x)
{
    return x*x;
}- 
Function prototypes are generally put into separate header files
- Separates specification of the function from its implementation
 
// myLib.h - header
// contains prototypes
int square(int);
int cube (int);// myLib.cpp - implementation
#include "myLib.h"
int cube(int x)
{
    return x*square(x);
}
int square(int x)
{
    return x*x;
}- functiosn can call themselves
- fib(n) = fib(n-1) + fib(n-2) can be easily expressed via a recursive implementation
int fibonacci(int n) {
if (n == 0 || n == 1) {
    return 1;
} else {
    return fibonacci(n-2) + fibonacci(n-1);
    }
}- passing by value: makes a copy of the variable; changes to the variable within the function don’t occur outside the function
// pass-by-value
void increment(int a) {
    a = a + 1;
    cout << "a in increment " << a << endl;
}
int main() {
    int q = 3;
    increment(q); // does nothing
    cout << "q in main " << q << endl;
  }
  //a in increment 4
//q in main 3- If you want to modify the original variable as opposed to making a copy, pass the variable by reference (int &a instead of int a)
// pass-by-value
void increment(int &a) {
    a = a + 1;
    cout << "a in increment " << a << endl;
}
int main() {
    int q = 3;
    increment(q); // works
    cout << "q in main " << q << endl;
}
//output
//a in increment 4
//q in main 4void swap(int &a, int &b) {
    int t = a;
    a = b; // HERE
    b = t;
}
int main() {
    int q = 3;
    int r = 5;
    swap(q, r);
    cout << "q " << q << endl; // q 5
    cout << "r " << r << endl; // r 3
}
So far we have used variables to store values in memory for later reuse. We now explore a means to store multiple values together as one unit, the array.
An array is a fixed number of elements of the same type stored sequentially in memory. Therefore, an integer array holds some number of integers, a character array holds some number of characters, and so on. The size of the array is referred to as its dimension. To declare an array in C++, we write the following:
type arrayName[dimension];
int arr[4];
arr[0] = 6;
arr[1] = 0;
arr[2] = 9;
arr[3] = 6;int arr[4] = { 6, 0, 9, 6 };int arr[] = { 6, 0, 9, 6, 2, 0, 1, 1 };int arr[4] = { 6, 0, 9}; //remaing would be 0int arr[]{1,2,3};  //imp see above about this type of initialization- Arrays can also be passed as arguments to functions. When declaring the function, simply specify the array as a parameter, without a dimension. The array can then be used as normal within the function. For example:
#include <iostream>
using namespace std;
int sum(const int array[], const int length) {
 long sum = 0;
 for(int i = 0; i < length; sum += array[i++]);
 return sum;
 }
 int main() {
 int arr[] = {1, 2, 3, 4, 5, 6, 7};
 cout << "Sum: " << sum(arr, 7) << endl;
 return 0;
 }The function sum takes a constant integer array and a constant integer length as its arguments and adds up length elements in the array. It then returns the sum, and the program prints out Sum: 28.
It is important to note that arrays are passed by reference and so any changes made to the array within the function will be observed in the calling scope.
int twoDimArray[2][4] = { 6, 0, 9, 6, 2, 0, 1, 1 };
int twoDimArray[2][4] = { { 6, 0, 9, 6 } , { 2, 0, 1, 1 } };Note that dimensions must always be provided when initializing multidimensional arrays, as it is otherwise impossible for the compiler to determine what the intended element partitioning is. For the same reason, when multidimensional arrays are specified as arguments to functions, all dimensions but the first must be provided (the first dimension is optional), as in the following:
int aFunction(int arr[][4]) { … }Multidimensional arrays are merely an abstraction for programmers, as all of the elements in the array are sequential in memory. Declaring int arr[2][4]; is the same thing as declaring int arr[8];.
When you declare a variable, the computer associates the variable name with a particular location in memory and stores a value there.
When you refer to the variable by name in your code, the computer must take two steps:
- Look up the address that the variable name corresponds to
- Go to that location in memory and retrieve or set the value it contains
C++ allows us to perform either one of these steps independently on a variable with the & and * operators:
- 
&xevaluates to the address of x in memory.
- 
*( &x )takes the address of x and dereferences it – it retrieves the value at that location in memory.*( &x )thus evaluates to the same thing asx.
Memory addresses, or pointers, allow us to manipulate data much more flexibly; manipulating the memory addresses of data can be more efficient than manipulating the data itself. Just a taste of what we’ll be able to do with pointers:
- More flexible pass-by-reference
- Manipulate complex data structures efficiently, even if their data is scattered in different memory locations
- Use polymorphism – calling functions on data without knowing exactly what kind of data it is
Pointers are just variables storing integers – but those integers happen to be memory addresses, usually addresses of other variables. A pointer that stores the address of some variable x is said to point to x. We can access the value of x by dereferencing the pointer.
As with arrays, it is often helpful to visualize pointers by using a row of adjacent cells to represent memory locations, as below. Each cell represents 1 block of memory. The dot-arrow notation indicates that ptr “points to” x – that is, the value stored in ptr is 12314, x’s memory address.
- To declare a pointer variable named ptr that points to an integer variable named x:
int *ptr = &x;int *ptr declares the pointer to an integer value, which we are initializing to the address
of x.
We can have pointers to values of any type. The general scheme for declaring pointers is:
data_type *pointer_name; // Add "= initial_value " if applicablepointer_name is then a variable of type data_type * – a “pointer to a data_type value.”
Once a pointer is declared, we can dereference it with the * operator to access its value:
cout << *ptr; // Prints the value pointed to by ptr,
              // which in the above example would be x’s valueWe can use deferenced pointers as l-values:
*ptr = 5; // Sets the value of xNote that Without the * operator, the identifier x refers to the pointer itself, not the value it points to:
cout << ptr; // Outputs the memory address of x in base 16Just like any other data type, we can pass pointers as arguments to functions. The same way we’d say void func(int x) {...}, we can say void func(int *x){...}. Here is an example of using pointers to square a number in a similar fashion to pass-by-reference:
void squareByPtr ( int* numPtr)
{
  //sth
}There are two places the const keyword can be placed within a pointer variable declaration. This is because there are two different variables whose values you might want to forbid changing: the pointer itself and the value it points to.
const int *ptr;  //Note you can left it uninitialized becoz ptr is variable.
                //It can be changed. Here only location is read-only. When you say,
                // read-only location, it is with respect to pointer ptr. Content of
                //this location can be changed via other pointers- Remember Right-left Rule- :"it declares a changeable pointer to a constant integer".
- Note that the integer value can't be changed through this pointers but the pointer may be changed to point to a different integer.
- (const int )--> read-only location
int * const ptr; // Note you can't left this uninitialized
//because ptr is read-only varaible so you should assign it at
//the very first time. Here the location is not read-only.
//YOu can change the value in that location.- declares a constant pointer to changeable integer data. The integer value can be changed through this pointer, but the pointer may not be changed to point to a different constant integer.
- ptr is read-only varible
const int * const ptr;   //error, you can't left it uninitialized- forbids changing either the address ptr contains or the value it points to.
/* Find out what will happen in this code? */
#include<iostream>
using namespace std;
int main() {
  int x = 5;
  int y = 10;
const  int  *i; //Now location is const with respect to the pointer i
i = &x;
(*i)++;  //fails, becoz location is const wrt i
int *j = i; // what is wrong here?? Invalid conversion from const int to int
(*j)++; // if line 13 had been ok, this line will be ok,, since line 13 says location pointed by j is not const
const  int *j = i;  // this is ok,,
 *i=*i+1;  //ERROR: assignment of read-only location (const int)
 i = &y; //pointer variable i is not const. you can make it point to others
  x++;
 cout<<*i<<"\n";
 cout<<*j;
  return 0;
}
/* Find out what will happen in this code? */
#include<iostream>
using namespace std;
int main() {
  int x = 5;
  int y = 10;
const  int *const i = &x;  //you need to assign such statement. It will throw error if you don't assign
 *i=*i+1;  //ERROR: assignment of read-only location (const int)
  i = &y; // ERROR: assignment of read-only variable i in i=&y
  x++;    // nobody is preventing you to change the value of x; its valid
 cout<<*i;  // what will it print?? 5 or 6?
  return 0;
}#include<iostream>
using namespace std;
int main() {
  int x = 5;
  int y = 10;
  int *const i = &x;  //you need to assign such statement. It will throw error if you don't assign
  *i=*i+1;  //valid, because value pointing to can be changed but pointer itself can't be changed to point to other variable
  i = &y; // error, i is the read only variable. You can't reassign it
  x++;
 cout<<*i;  //what will it print?
  return 0;
}/* Find out what will happen in this code? */
#include<iostream>
using namespace std;
int main() {
  int x = 5;
  int y = 10;
  const int *i;   // this and the below are same
//  int  const *i ;  //this and the above one are same....Pls don't confuse on this one, remove one of them
  i = &x;     //i is non-const variable. you can assign it as many times as you want
 *i=*i+1;    //ERROR: you can't do this because location is const
  i = &y;     //Can you point to any other variable; Now qustion is can you change the new location value?
  (*i)++;    //ERROR: increment of read-only location i
  x++;
  cout<<*i;
  return 0;
}
I have tested most of the combinations. Pls see const_pointer for more details.
Some pointers do not point to valid data; dereferencing such a pointer is a runtime error. Any pointerset to 0 is called a null pointer, and since there is no memory location 0, it is an invalid pointer. One should generally check whether a pointer is null before dereferencing it. Pointers are often set to 0 to signal that they are not currently valid.
Dereferencing pointers to data that has been erased from memory also usually causes runtime errors. 💥
int *myFunc(){
  int phantom = 4;
  return &phantom;
}phantom is deallocated when myFunc exits, so the pointer the function returns is invalid.
Note: read/write pointer can't point to the read only location 💥 👊
int i = 2; //initialization is optional
const int ci = 3; // initialization required
++ci;// not ok
int *p = &i; //ok
++*p;
p = &ci; //not ok
++*p;
const int * pc; // ok; read-only view of the poiner I am pointing to
const int * pc = &ci;  //OK
++*pc; //not ok
++pc; // ok
pc = &i; //ok
int *imp = pc; //not ok becoz pc is read-only pointer(r-value), so its illegal
int * const cp; // not ok because poner can't move, it needs to be initialized
int *const cp = &ci; //not ok becoz ci is ref to const int. you can't assign read-only view pointer to pointer having read-write view
++*cp; //ok
++cp; //not okint * p = new int[1000];
...
...
++*p;   // but if you did ++p instead of ++*p by mistake, the program will crash
...
delete[] p;Instead, if we did,
int *const p = new int [1000];
...
...
++p; //if you did this my mistake -> compiler will throw error and you can catch it befoer program will crash
...
delete [] p;When we write void f(int &x) {...} and call f(y), the reference variable x becomes another name – an alias – for the value of y in memory. We can declare a reference variable locally, as well:
int y;
int &x = y; // Makes x a reference to, or alias of, yAfter these declarations,changing x will change y and vice versa,because they are two names for the same thing.
References are just pointers that are dereferenced every time they are used. Just like pointers, you can pass them around, return them, set other references to them, etc. The only differences between using pointers and using references are: 👊 🍁
- References are sort of pre-dereferenced – you do not dereference them explicitly.
- You cannot change the location to which a reference points, whereas you can change the location to which a pointer points. Because of this, references must always be initialized when they are declared.💖
- When writing the value that you want to make a reference to, you do not put an &before it to take its address, whereas you do need to do this for pointers.
- Reference should always be stuck to some target.
const int ci = 3;  //initialization required
int &c = ci;
int &ci; //ERROR:const int &rc = ci;Here reference rc is inherently constant.  and note that the const in const int &rc = ci; is for the int.
int a [] = {2,3,4};
//a is stuck to  pointer
//same as ref, so it is tuck to a location
// same as int * const a;The name of an array is actually a pointer to the first element in the array. Writing myArray[3] tells the compiler to return the element that is 3 away from the starting element of myArray.
This explains why arrays are always passed by reference: passing an array is really passing a pointer.
This also explains why array indices start at 0: the first element of an array is the element that is 0 away from the start of the array.
Pointer arithmetic is a way of using subtraction and addition of pointers to move around between locations in memory, typically between array elements. Adding an integer n to a pointer produces a new pointer pointing to n positions further down in memory.
myArray[3] is *(myArray + 3)
- a string is actually an array of characters.
- When you set achar *to a string, you are really setting a pointer to point to the first character in the array that holds the string
- You cannot modify string literals;, 👊 to do so is either a syntax error or a runtime error, depending on how you try to do it
- String literals are loaded into read-only program memory at program startup. 💥
- You can, however, modify the contents of an array of characters. Consider the following example
char courseName1[] = {’6’, ’.’, ’0’, ’9’, ’6’, ’\0 ’ };
char *courseName2 = "6.096 ";- Attempting to modify one of the elements courseName1 is permitted,
- attempting to modify one of the characters in courseName2 will generate a runtime error, causing the program to crash.
- Why same name is getting printed for nodes as "Wendy"?
  // Example program
  #include <iostream>
  #include <string.h>
  using namespace std;
  struct Node {
      char *name;
      int age;
      Node(char *n = "", int a = 0){
          name = strdup(n);
          age = a;
      }
  };
  int main(){
      Node node1("Roger", 20), node2(node1);
      cout<<node1.name<<" "<<node1.age<<"\n"<<endl;
      cout<<node2.name<<" "<<node2.age<<"\n"<<endl;
      strcpy(node2.name,"Wendy");
      node2.age = 30;
      cout<<node1.name<<" "<<node1.age<<"\n"<<endl;
      cout<<node2.name<<" "<<node2.age<<"\n"<<endl;
  }
  /*Outputs
  Roger 20
  Roger 20
  Wendy 20
  Wendy 30 */- 
The problem is that the definition of Node does not provide a copy constructor Node(const Node&); which is necessary to execute the declaration node2(node1)to initializenode1.
- 
If a user copy constructor is missing, the constructor is generated automatically by the compiler. But the compiler-generated copy constructor performs member-by-member copying. 🌹 🌺 
- 
Because nameis a pointer, the copy constructor copies the string addressnode1.nametonode2.name, not the string content.
- 
To prevent this from happening, the user must define a proper copy constructor, as in struct Node { char *name; int age; Node(char *n = 0, int a = 0) { name = strdup(n); age = a; } Node(const Node& n) { // copy constructor; name = strdup(n.name); age = n.age; } }; 
- 
Note that a similar problem is raised by the assignment operator. If a definition of the assignment operator is not provided by the user, an assignment. node1 = node2; 
performs member-by-member copying, which leads to the same problem
- To avoid the problem, the assignment operator has to be overloaded by
the user. For Node, the overloading is accomplished byNode& operator=(const Node& n) { if (this != &n) { // no assignment to itself; if (name != 0) free(name); name = strdup(n.name); age = n.age; } return *this; } 
In this code, a special pointer this is used. Each object can access its own address
through the pointer this so that *this is the object itself.