Skip to content

Commit eee0ed4

Browse files
committed
Refactor references/pointers section and remove exceptions and lambdas
1 parent 9a4211d commit eee0ed4

File tree

1 file changed

+49
-158
lines changed

1 file changed

+49
-158
lines changed

C++ Syntax.md

Lines changed: 49 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ Since the C++ language varies so heavily between versions (e.g. C++0x, C++11, C+
4242
- [2.5 Strings ](#25-strings-stdstring)
4343
- [2.6 Iterators](#26-iterators-stditerator)
4444
- [2.7 Exceptions](#27-exceptions)
45-
- [2.7.1 Motivation](#271-motivation)
46-
- [2.7.2 Standard exception hierarchy](#272-stdexception)
4745
- [2.8 Lambdas](#28-lambdas)
4846

4947
<!-- /TOC -->
@@ -851,56 +849,67 @@ std::cout << "Hello, World" << std::endl; // <--- GOOD: It's clear that you're
851849
```
852850

853851
### 2.2 References and Pointers
854-
are used to store the address of the varibale/object in memory. So having the pointer or reference, you could do the same operations as that of object being pointed to.
855-
```c++
856-
int a = 3;
857-
int b = 5;
852+
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
853+
pointers while avoiding some of the pitfalls. Later versions of C++ also add [smart pointers](https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=vs-2019),
854+
which allow for better memory management and scoping via `std::unique_ptr`, `std::shared_ptr`, and `std::weak_ptr`, as compared to traditional raw pointers.
858855

859-
int* aptr = &a; // & operator gets the address of variable a
860-
int* bptr = &b; // int * is a type of pointer to int
856+
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
857+
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.
858+
859+
References are more akin to an alias. References cannot be `NULL` or `nullptr`, and references cannot be reassigned to reference something else after they have been created.
860+
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),
861+
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, if it's a reference
862+
to an integer it can be used as an integer. If it's a reference to a class you can access the class members directly with the `.` operator.
863+
864+
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
865+
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.
866+
867+
More on [references vs pointers here](https://stackoverflow.com/a/57492).
868+
869+
In the following code, assume a 32-bit system, in which case the size of a pointer variable is 4 bytes (32 bits), and that the stack grows towards higher memory addresses.
861870

862-
int c = *aptr + *bptr; // * opeartor used to get the value of the object that
863-
// is being pointed to
864-
int d = a + b; // c and d are equal at the end of the day
865-
```
866-
Think of pointer as the abstraction that knows where to find an object, but don't own it.
867-
The references are about the same, but they use more convenient syntax, but some [limitations](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in) apply. The main difference is that refernce can't be reassigned and must be assigned at initialization:
868871
```c++
869-
int a = 3;
870-
int b = 5;
872+
// Pointers
873+
int a = 10; // Ends up at memory address '0x2A000084', for example
874+
int b = 20; // Ends up at memory address '0x2A000088'
871875

872-
int& aref = a; // & means reference type
873-
int& bref = b;
876+
int * ptr = nullptr; // ptr is a separate variable whose type is 'pointer to int' and whose value has been initialized to '0x00000000'
877+
printf("ptr = %p\n"); // Prints: 0x0
874878

875-
int c = aref + bref; // simple addition syntax
879+
ptr = &a; // The value of ptr is now the address of the variable 'a'
880+
std::cout << p << std::endl; // Prints: 0x2a000084
881+
std::cout << *p << std::endl; // Prints: 10
876882

877-
int d = a + b; // c and d are equal at the end of the day
883+
ptr = &b; // The value of ptr is now the address of the variable 'b'
884+
std::cout << p << std::endl; // Prints: 0x2a000088
885+
std::cout << *p << std::endl; // Prints: 20
878886
```
879-
Also pointers are used in arrays like that:
887+
880888
```c++
881-
#include <iostream>
889+
// References
890+
int a = 10; // Ends up at memory address '0x2A000084', for example
891+
int b = 20; // Ends up at memory address '0x2A000088'
882892
883-
int arr[] = { 9, 5, 8, 2 }; // array storage is contiguous
884-
int size = 4;
893+
int & ref_a = a; // ref_a is an alias of (reference to) the variable a
894+
int & ref_b = b; // ref_b is an alias of (reference to) the variable b
885895
886-
for(int i = 0; i < size; i++) std::cout << arr[i] << ' '; // 9 5 8 2
887-
std::cout << '\n';
896+
std::cout << ref_a << std::endl; // Prints: 10
897+
std::cout << ref_b << std::endl; // Prints: 20
888898
889-
int* ptr = arr; // ptr points to first element of an array
890-
for(int i = 0; i < size; i++) std::cout << *(ptr + i) << ' '; // 9 5 8 2
891-
std::cout << '\n'
899+
std::cout << &ref_a << std::endl; // Prints: 0x2a000084
900+
std::cout << &ref_b << std::endl; // Prints: 0x2a000088
892901
893-
ptr = arr; // reassignment of pointer, can't be preformed with reference
894-
for(int i = 0; i < size; i++) std::cout << *(ptr++) << ' '; // 9 5 8 2
895-
std::cout << '\n'
896-
```
897-
This loops are equivalent, moreover first-second are implemented in the same way. First loop is just convenience syntax for second. Notice, that pointers can be incremented. Also there are a special pointer, called `nullptr`.
898-
```c++
899-
double* ptr = nullptr; // means it points to nowhere
902+
ref_a = b; // SETS THE VALUE OF 'a' TO THE VALUE OF 'b'!
900903
901-
double a = *ptr; // this is a huge error, DON'T DO THIS
904+
std::cout << ref_a << std::endl; // Prints: 20
905+
std::cout << a << std::endl; // ALSO PRINTS: 20 !
906+
907+
int & ref_c; // ERROR! References must be initialized at their declaration
902908
```
903909

910+
In many cases
911+
912+
904913
### 2.3 Keywords
905914
[Reference](http://en.cppreference.com/w/cpp/keyword)
906915

@@ -951,7 +960,7 @@ double a = *ptr; // this is a huge error, DON'T DO THIS
951960
* `#include`: Includes a source file
952961
* `#line`: Changes the current file name and line number in the preprocessor
953962
* `#error`: Prints an error message and stops compilation
954-
* `#pragma`: Non-standard, used instead of header guards (`#ifndef HEADER_H` ...)
963+
* `#pragma`: Non-standard, can be used instead of header guards (`#ifndef HEADER_H` ...)
955964

956965
### 2.5 Strings (`std::string`)
957966
[Reference](http://en.cppreference.com/w/cpp/string/basic_string)
@@ -960,125 +969,7 @@ double a = *ptr; // this is a huge error, DON'T DO THIS
960969
[Reference](http://en.cppreference.com/w/cpp/concept/Iterator)
961970

962971
### 2.7 Exceptions
963-
#### 2.7.1 Motivation
964-
What if you want to tell that this behaviour is unacceptable in current situation? Then you should use exceptions:
965-
```c++
966-
#include <iostream>
967-
968-
int divide(int a, int b)
969-
{
970-
if( b == 0 ) throw "Can't divide by zero"; // throwing an exception
971-
return a / b;
972-
}
973-
974-
// usage
975-
int main()
976-
{
977-
int a = 3, b = 0;
978-
979-
try { // try-catch close
980-
divide(a, b); // we are trying to do division
981-
} catch( const char* err_msg) { // if exception being thrown, we are trying to catch it
982-
std::cout << "Error is: " << err_msg << '\n'; // print error on the output
983-
}
984-
985-
return 0; // and finish work
986-
}
987-
```
988-
#### 2.7.2 `std::exception`
989-
[Here](http://en.cppreference.com/w/cpp/error/exception) are all standard exceptions, you may use them as follows:
990-
```c++
991-
#include <iostream>
992-
#include <exception> // for std::logic_error
972+
[Reference](http://en.cppreference.com/w/cpp/error/exception)
993973

994-
class Complex { // class from previous examples
995-
double re {};
996-
double im {};
997-
public:
998-
// ... constructor, plus/minus/multiply operators ...
999-
1000-
class ZeroDivisionError : public std::logic_error { // class for zero division error
1001-
const* char what() const { return "Can't divide by zero"; }
1002-
};
1003-
1004-
friend Complex operator/(const Complex& fst, const Complex& snd)
1005-
{
1006-
if(snd.re == 0 && snd.im == 0) throw ZeroDivisionError {}; // throwing by value
1007-
1008-
// ... other code for division ...
1009-
}
1010-
};
1011-
1012-
// usage
1013-
int main()
1014-
{
1015-
Complex a{1, 2};
1016-
Complex b{0, 0};
1017-
1018-
try {
1019-
a / b;
1020-
} catch( const Complex::ZeroDivisionError& e) { // catching by const reference
1021-
std::cout << e.what() << '\n';
1022-
}
1023-
1024-
return 0; // and happily finish program
1025-
}
1026-
```
1027-
`what` is a standard name for a function that tells what's happened in the exception class.
1028974
### 2.8 Lambdas
1029-
Very common pattern is to write such a code:
1030-
```c++
1031-
struct AddTo3 {
1032-
int operator()(int addto) const { return 3 + addto; }
1033-
};
1034-
1035-
int add_to_3(int addto) { return 3 + addto; }
1036-
// basically the same, but it is not enough in some cases
1037-
1038-
// usage
1039-
int main()
1040-
{
1041-
(AddTo3 {})(5); // 8
1042-
add_to_3(5); // 8
1043-
}
1044-
```
1045-
It is so common, that language designers decided to add it to the standard and call it `lambda function`:
1046-
```c++
1047-
int main()
1048-
{
1049-
auto add_to_3 { // much, much simpler, just declare variable add_to_3
1050-
[] (int addto) { return 3 + addto; }
1051-
};
1052-
1053-
add_to_3(5);
1054-
}
1055-
```
1056-
Now, what this syntax means:
1057-
```c++
1058-
int main()
1059-
{
1060-
auto add_to_3 {
1061-
[] // 1
1062-
/**
1063-
* Capture variables, from outer scope, by reference(&),
1064-
* or by value(=).
1065-
* [&] - captures all variables by reference(uses them inside lambda)
1066-
* [=] - captures all variables by value(copies them inside lambda)
1067-
* [&var] - capture only var by reference
1068-
* [=var] - capture only var by value
1069-
* [=, &var] capture var by ref, and others by value
1070-
*/
1071-
(int addto) // 2
1072-
/**
1073-
* Argument list, just like in ordinary functions
1074-
*/
1075-
{ return 3 + addto; } // 3
1076-
/**
1077-
* Lambda body, just like in ordinary functions
1078-
*/
1079-
};
1080-
1081-
add_to_3(5); // 8
1082-
}
1083-
```
1084-
So you could use it in advanced scenarios. Read more, like [this](https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11), and [that](https://en.cppreference.com/w/cpp/language/lambda).
975+
[Reference](https://en.cppreference.com/w/cpp/language/lambda)

0 commit comments

Comments
 (0)