├── Chapter 1 ├── Function-Call-Stack-Frame.md ├── Program-Compiling-and-Linking.md └── Virtual-Address-Space-of-Process-Memory-Partition-and-Layout.md ├── Chapter 10 ├── Custom-Deleters.md ├── Smart-Pointers-with-Reference-Counting.md ├── Smart-Pointers-without-Reference-Counting.md └── Smart-Pointers.md ├── Chapter 11 ├── Introduction-to-std-function.md ├── More-about-Binders.md ├── More-about-Lambda-Expressions.md ├── More-about-std-function.md ├── Template-Specialization-and-Argument-Deduction.md └── std-bind()-A-Simple-Thread-Pool.md ├── Chapter 12 ├── Atomic-Operations.md ├── Important-Features-in-C++11.md ├── Multithreaded-Programming-with-std-thread.md ├── Mutual-Exclusion.md ├── Producer-Consumer-Problem.md └── Thread-Visibility-and-volatile.md ├── Chapter 13 ├── Adapter-Pattern.md ├── Decorator-Pattern.md ├── Factory-Pattern.md ├── Observer-Pattern.md ├── Proxy-Pattern.md └── Singleton-Pattern.md ├── Chapter 2 ├── Const-and-Pointers.md ├── Default-Parameters.md ├── Function-Overloading.md ├── Inline-Function.md ├── New-and-Delete.md └── References-in-Detail.md ├── Chapter 3 ├── Class-and-Object.md ├── Constructor-and-Destructor.md ├── Initializer-List.md ├── Pointer-to-Class-Members.md ├── Shallow-Copy-and-Deep-Copy.md └── Various-Member-Functions.md ├── Chapter 4 ├── Class-Templates.md ├── Function-Templates.md └── Memory-Allocators.md ├── Chapter 5 ├── Introduction-to-Iterators.md ├── Issues-of-Iterator-Invalidation.md ├── More-about-new-and-delete.md ├── Operator-Overloading.md └── Overloading-of-new-and-delete-Object-Pool.md ├── Chapter 6 ├── Abstract-Classes.md ├── Frequently-Asked-Interview-Questions-Polymorphism.md ├── Look-inside-Inheritance.md ├── More-about-Inheritance.md ├── More-about-Virtual-Functions.md ├── Understanding-Polymorphism.md └── Virtual-Functions-Static-Binding-and-Dynamic-Binding.md ├── Chapter 7 ├── Diamond-Problem.md ├── Four-Kinds-of-Type-Conversions.md └── Virtual-Inheritance-and-Virtual-Base-Classes.md ├── Chapter 8 ├── Associative-Containers.md ├── Container-Adaptors.md ├── Function-Objects.md ├── Generic-Algorithms-Binders-and-Lambda-Expressions.md ├── More-about-Iterators.md └── Sequence-Containers.md ├── Chapter 9 ├── Behind-the-Object.md ├── Member-Functions-with-Rvalue-References.md ├── Move-Semantics-and-Perfect-Forwarding.md └── Optimizing-Objects-in-Functions.md ├── README.md ├── assets ├── compiling-and-linking.png ├── diamond-problem-2.png ├── diamond-problem-3.png ├── diamond-problem.png ├── function-call-1.png ├── function-call-2.png ├── function-call-3.png ├── function-call-4.png ├── function-call-5.png ├── function-call-6.png ├── linking.png ├── shallow-copy.png ├── shared_ptr.png ├── virtual-address-space.png ├── virtual-base-class-2.png ├── virtual-base-class.png ├── virtual-function-1.png └── virtual-function-2.png └── codes ├── Chapter 10 └── SmartPtr.cpp ├── Chapter 11 └── ThreadPool.cpp ├── Chapter 12 ├── Atomic-Operations.cpp ├── Mutex.cpp └── Producer-Consumer.cpp ├── Chapter 13 ├── Abstract-Factory.cpp ├── Adapter.cpp ├── Decorator.cpp ├── Factory-Method.cpp ├── Observer.cpp ├── Proxy.cpp ├── Simple-Factory.cpp └── Singleton.cpp ├── Chapter 3 ├── Goods.cpp ├── MyQueue.cpp ├── MyStack.cpp └── MyString.cpp ├── Chapter 4 ├── MyStack.cpp └── MyVector.cpp ├── Chapter 5 ├── MyComplex.cpp ├── MyQueue.cpp ├── MyString.cpp └── MyVector.cpp └── Chapter 9 ├── MyString.cpp └── MyVector.cpp /Chapter 1/Function-Call-Stack-Frame.md: -------------------------------------------------------------------------------- 1 | # Function Call: Stack Frame 2 | 3 | Let's look at the following code: 4 | 5 | ``` c++ 6 | int sum(int a, int b) { 7 | int temp = 0; 8 | temp = a + b; 9 | return temp; 10 | } 11 | 12 | int main() { 13 | int a = 10; 14 | int b = 20; 15 | int ret = sum(a, b); 16 | cout << "result: " << res << endl; 17 | return 0; 18 | } 19 | ``` 20 | 21 | It is a simple program, but what exactly happened when the program is running? You may have two questions: 22 | 23 | 1. After main() called sum(), how does the program know which function to return back to? 24 | 25 | 2. After sum() returns to main(), how does the program know which line to continue? 26 | 27 | Now let's start from assembly language to see what happens in the black box. 28 | 29 | ## Stack Frame 30 | 31 | Every time a function is called, an independent stack frame is created on the stack. We use two registers, ***ebp*** and ***esp*** to determine the scope. ebp points to the bottom of the stack, while esp always points to the top of the stack. 32 | 33 | In the program above, we first assign values to integers *a* and *b*. Each integer is 4 bytes in size, so we push them into the stack with 34 | 35 | ```assembly 36 | mov dword ptr[ebp-4], 0Ah 37 | mov dword ptr[ebp-8], 14h 38 | ``` 39 | 40 | Then we meet the next commands. We assign the return value of sum() to variable *ret*. How does that happen? 41 | 42 | ![function-call-1](../assets/function-call-1.png) 43 | 44 | ## Function Call 45 | 46 | After a function is called, we first push all the arguments into the stack. In C and C++, the push order is from right to left, so we push *b* and *a* in order. 47 | 48 | ```assembly 49 | mov eax, dword ptr[ebp-8] 50 | push eax 51 | mov eax, dword ptr[ebp-4] 52 | push eax 53 | ``` 54 | 55 | ![function-call-2](../assets/function-call-2.png) 56 | 57 | Then we need push the address of the next command into stack, the call command does so. This ensures that we can find our way to continue after we return from the function. 58 | 59 | ```assembly 60 | call sum 61 | add esp, 8 # Next command 0x08124458 62 | ``` 63 | 64 | ![function-call-3](../assets/function-call-3.png) 65 | 66 | Now we enter sum(). Remember that stack frame is independent for each function, so we need to adjust it. First, we push the address of caller's *ebp* into the stack, so that we can restore it after. Then, we set *ebp* to *esp*, which means that the new stack frame is on the top of the older one. Finally, we need to assign memory space for the new stack frame. In Visual Studio, the compiler will automatically assign an value *0xCCCCCCCC* to it, while not in g++ or gcc. 67 | 68 | ```assembly 69 | push ebp # 0x0018ff40 70 | mov ebp, esp 71 | sub esp, 4Ch 72 | ``` 73 | 74 | ![function-call-4](../assets/function-call-4.png) 75 | 76 | Then inside sum(), we calculate the sum of *a* and *b*, and store the result into *temp*. 77 | 78 | ```assembly 79 | mov dword ptr[ebp-4], 0 80 | mov eax, dword ptr[ebp+0Ch] 81 | add eax, dword ptr[ebp+8] 82 | mov dword ptr[ebp-4], eax 83 | ``` 84 | 85 | ![function-call-5](../assets/function-call-5.png) 86 | 87 | ## Function Return 88 | 89 | When we return from a function, we also need to return its stack frame back to the system. If a function has a return value, it first store the value inside register *eax*. Then it recycle the local variable space by move *esp* to the bottom. By popping the old address out, we restore *ebp* to the bottom of the caller function. 90 | 91 | ```assembly 92 | mov eax, dword ptr[ebp-4] 93 | mov esp, ebp 94 | pop ebp 95 | ``` 96 | 97 | ![function-call-3](../assets/function-call-3.png) 98 | 99 | Now we want to go back to where we called the function. *ret* command pop the current element on top of the stack, and put it inside a PC register. PC register always store the address of the next command for the CPU to execute, so we are able to jump to the position we store before. 100 | 101 | ```assembly 102 | ret 103 | ``` 104 | 105 | ![function-call-2](../assets/function-call-2.png) 106 | 107 | After that, the function parameters are no longer useful, we can simply pop them out. Then we assign the return value from *eax* to *ret*. 108 | 109 | ```assembly 110 | add esp, 8 111 | mov dword ptr[ebp-0Ch], eax # assign return value 112 | ``` 113 | 114 | Now the stack frame remains the same as initially. All memories related to the called function has been eliminated. 115 | 116 | ![function-call-6](../assets/function-call-6.png) 117 | 118 | ## At Last 119 | 120 | This is all about the underlying principle of function call and return. Now we are able to answer these two questions at the beginning. You may also figure out what's the problem in the following code, and how to avoid this in your own codes. 121 | 122 | ```c++ 123 | int* foo() { 124 | int data = 10; 125 | return &data; 126 | } 127 | 128 | int bar() { 129 | int *p = foo(); 130 | cout << *p << endl; 131 | } 132 | ``` 133 | 134 | -------------------------------------------------------------------------------- /Chapter 1/Virtual-Address-Space-of-Process-Memory-Partition-and-Layout.md: -------------------------------------------------------------------------------- 1 | # Virtual Address Space of Process: Memory Partition and Layout 2 | 3 | Any programming language is compiled into commands and data. When a program start, it will be loaded from the disk into the memory. However, it won't be loaded into the physical memory directly. Under a x86-32 Linux operating system, current process will be allocated a space of 2^32 bits (4 GB), which is called the virtual address space. 4 | 5 | ![virtual-address-space](../assets/virtual-address-space.png) 6 | 7 | Virtual means that this space does not exists physically, it is just a data structure provided by the system. This virtual space is divided into two parts: user space and kernel space. In default, the user space occupies 3 GB and the kernel space occupies 1 GB. 8 | 9 | The memory space from 0x00000000 to 0x0804800 is not allowed to access. A nullptr has a memory address of 0x00000000, so any access of what a nullptr points to will cause a segmentation fault. 10 | 11 | ```C++ 12 | *p = nullptr; 13 | p = p->next; // ERROR 14 | ``` 15 | 16 | On the bottom of the user space it stores **.text** and **.rodata**. .text stores commands, and .rodata stores read-only data, such us constant string. An error occurs when trying to modify the data stores in this area, for example: 17 | 18 | ```C++ 19 | char *p = "hello world"; 20 | *p = 'a'; // ERROR 21 | ``` 22 | 23 | Next address space stores **.bss**, which represents variables without initialization. The system will automatically assign 0 two these variables. It also stores variables which is assigned 0 by programmers. 24 | 25 | ```C++ 26 | int g_data; 27 | cout << g_data << endl; // 0 28 | ``` 29 | 30 | Variables declared out side functions are stored as data type. So they are stored inside *.data* or .bss depending on whether they are initialized or not. Notice that all local variables declared inside function scope are all compiled into assembly commands instead of data. So they all stored in .text. After the function is called, the system will load these commands, and create corresponding spaces on the function stack for these variables. 31 | 32 | ```c++ 33 | int g_data1 = 10; // .data 34 | int g_data2 = 0; // .bss 35 | int g_data3; // .bss 36 | 37 | static int g_data4 = 11; // .data 38 | static int g_data5 = 0; // .bss 39 | static int g_data6; // .bss 40 | 41 | int main() { 42 | int a = 12; // .text 43 | int b = 0; // .text 44 | int c; // .text 45 | 46 | static int e = 13; // .data 47 | static int f = 0; // .bss 48 | static int g; // .bss 49 | 50 | return 0; 51 | } 52 | ``` 53 | 54 | Then there is **.heap**, which stores data created by programmers with new or malloc. Currently it is empty because we haven't create any new memory yet. 55 | 56 | Above .heap is the area storing shared libraries (**.dll** in Windows and **.so** in Linux). 57 | 58 | Then there is the space called **.stack**. It is the most important part for function calls. Unlike heap, stack grows downward from a larger address to a smaller address. 59 | 60 | At the top of the user space it stores terminal parameters and environment variables. For example, in this case 61 | 62 | ```shell 63 | ./a.out 192.168.1.000 8888 64 | ``` 65 | 66 | the two parameters IP and port will be stored here. 67 | 68 | Inside kernel space there are three areas: **ZONE_DMA**, **ZONE_NORMAL** and **ZONE_HIGHMEM**. Kernel space stores data which is essential for operating system. 69 | 70 | The user space for each process is private, but the kernel space is shared. That is, if we want to communicate among processes, we have to go through the kernel space. Anonymous channel is a popular way that creates an area inside kernel space for processes to communicate with each others. -------------------------------------------------------------------------------- /Chapter 10/Custom-Deleters.md: -------------------------------------------------------------------------------- 1 | # Custom Deleters 2 | 3 | Smart pointers ensure the automatic release of resources on the heap. In the previous example, our custom *SmartPtr* release the resources by calling `delete` on the pointer. However, `delete` is not always the correct way to release resources. For example, array objects need to be destroyed with `delete[]`, and files need to be closed with *fclose()*. Therefore, under some circumstances we need to customize the deleter. 4 | 5 | *unique_ptr* 和 *shared_ptr* both support custom deleters. Inside their destructors a function object of the deleter is called. The default deleter simply uses `delete`: 6 | 7 | ```cpp 8 | template 9 | class Deleter { 10 | public: 11 | void operator() (T *ptr) { 12 | delete ptr; 13 | } 14 | }; 15 | ``` 16 | 17 | Now if we use a *unique_ptr* to point to an array, we can defined our own deleter which uses `delete[]` to free the memory. Then we need to use *MyDeleter* as a template parameter. 18 | 19 | ```cpp 20 | template 21 | class MyDeleter { 22 | public: 23 | void operator() (T *ptr) const { 24 | delete[] ptr; 25 | } 26 | }; 27 | 28 | int main() { 29 | unique_ptr> p = new int[100]; 30 | return 0; 31 | } 32 | ``` 33 | 34 | Similarly, we can also customize a deleter to release a file resource with *fclose()*: 35 | 36 | ```cpp 37 | template 38 | class MyDeleter { 39 | public: 40 | void operator() (T *ptr) const { 41 | fclose(ptr); 42 | } 43 | }; 44 | 45 | int main() { 46 | unique_ptr> p = fopen("data.txt", "w"); 47 | return 0; 48 | } 49 | ``` 50 | 51 | In modern C++, a more convenient way to use custom deleters is to use the lambda expressions. The above examples can also be written as follows, without additional definitions of function objects. 52 | 53 | ```cpp 54 | #include 55 | int main() { 56 | unique_ptr> p1(new int[100], 57 | [](int *p) -> void { 58 | delete[] p; 59 | } 60 | ); 61 | unique_ptr> p2(fopen("data.txt", "w"), 62 | [](FILE *p) -> void { 63 | fclose(p); 64 | } 65 | ); 66 | } 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /Chapter 10/Smart-Pointers-without-Reference-Counting.md: -------------------------------------------------------------------------------- 1 | # Smart Pointers without Reference Counting 2 | 3 | ```cpp 4 | #include 5 | ``` 6 | 7 | C++ utilities library provides us three kinds of smart pointers without reference counting: *auto_ptr*, *scoped_ptr* and *unique_ptr*. These pointers have a common feature, that at the same time there can only be one pointer that manages the object. In other words, every time a new copy of a pointer transfer the ownership of objects from the previous one. 8 | 9 | ## *auto_ptr* 10 | 11 | *auto_ptr* rewrites the copy constructor so that every time a new copy of the pointer is created, it takes handle of its object, and the original pointer is set to NULL. In this way, there can only be one pointer pointing to the object. In this way, the destructor only need to deal with the only pointer pointing to the object. Therefore, we can not use the original pointer any more if its ownership has been transferred: 12 | 13 | ```cpp 14 | int main() { 15 | auto_ptr p1(new int); 16 | auto_ptr p2(p1); 17 | *p2 = 20; 18 | cout << *p1 << endl; // ERROR 19 | return 0; 20 | } 21 | ``` 22 | 23 | *auto_ptr* has to be used carefully, for programmers sometimes may not realize the transfer of ownership and invalidate the pointer unintentionally,. 24 | 25 | ## *scoped_ptr* 26 | 27 | *scoped_ptr* strictly regulates that there is always one pointer pointing to an object. It achieves this by disabling the copy constructor of operator`=`: 28 | 29 | ```cpp 30 | scoped_ptr(const scoped_ptr &) = delete; 31 | scpoed_ptr &operator=(const scoped_ptr &) = delete; 32 | ``` 33 | 34 | In this case, any copy of a *scoped_ptr* is not allowed: 35 | 36 | ```cpp 37 | int main() { 38 | scoped_ptr p1(new int); 39 | scoped_ptr p2(p1); // ERROR 40 | return 0; 41 | } 42 | ``` 43 | 44 | Since *scoped_ptr* is to strict towards the ownership, it is not widely used.使用的比较少 45 | 46 | ## *unique_ptr* 47 | 48 | *unique_ptr* disables the copy constructor and operator`=` as well. But instead, it provides a copy constructor and operator`=` with Rvalue references: 49 | 50 | ```cpp 51 | unique_ptr(const unique_ptr &) = delete; 52 | unique_ptr &operator=(const unique_ptr &) = delete; 53 | unique_ptr(unique_ptr &&); 54 | unique_ptr &operator=(unique_ptr &&); 55 | ``` 56 | 57 | In this case, we still cannot copy a pointer directly, but we can use the move semantics here: 58 | 59 | ```cpp 60 | int main() { 61 | unique_ptr p1(new int); 62 | unique_ptr p2(std::move(p1)); 63 | return 0; 64 | } 65 | ``` 66 | 67 | Now *p1* transfer the ownership of the object to *p2*, and then be set to NULL. Again, the destructor only needs to deal with *p2*. 68 | 69 | *unique_ptr* has almost the same functionality as *auto_ptr*, but is more recommended because its semantic is clearer. Under normal circumstances, copying a *unique_ptr* is not allowed, and we must explicitly use *std::move*. Hence, it will not happen that the programmer unintentionally transfer the ownership of a pointer, which is much safer. -------------------------------------------------------------------------------- /Chapter 10/Smart-Pointers.md: -------------------------------------------------------------------------------- 1 | # Smart Pointers 2 | 3 | Pointers are the most powerful tools in C++, but programmers have to be responsible for the management of memory allocated on the heap. Any objects created with `new` has to be destroyed with `delete`, otherwise a memory leak will happen. Is there a way that the pointer can be automatically deleted when the memory is no longer used? The answer is yes. We can use object-oriented programming to implement a simplest smart pointer. 4 | 5 | Here we defined a template class *SmartPtr* which keeps a normal pointer as its member variable. The constructor simply initializes the pointer, while the destructor deletes it. 6 | 7 | ```cpp 8 | template 9 | class SmartPtr { 10 | public: 11 | SmartPtr(T *ptr = nullptr) : _ptr(ptr) {} 12 | ~SmartPtr() {delete _ptr;} 13 | private: 14 | T *_ptr; 15 | }; 16 | ``` 17 | 18 | Now our smart pointer can be automatically deleted, taking use of the feature that objects on the function stack are automatically destroyed when they get out of the scope. 19 | 20 | ```cpp 21 | int main() { 22 | SmartPtr p(new int); 23 | return 0; 24 | } 25 | ``` 26 | 27 | To achieve full functionality of a pointer, we need to provide the dereference and the arrow as well. 28 | 29 | ```cpp 30 | template 31 | class SmartPtr { 32 | ... 33 | T& operator*() {return *_ptr;} 34 | T* operator->() {return _ptr;} 35 | }; 36 | ``` 37 | 38 | So far, out smart pointer works perfectly, but a problem occurs with the copy constructor: 39 | 40 | ```cpp 41 | int main() { 42 | SmartPtr p1(new int); 43 | SmartPtr p2(p1); 44 | return 0; // ERROR 45 | } 46 | ``` 47 | 48 | The program goes wrong when it returns. Here our copy constructor is a shallow copy, which only copies the pointer. When *p1* and *p2* is destroyed, the memory is deallocated repeatedly. Of course, we can rewrite the copy constructor to implement a deep copy, but in most circumstances we only want *p1* and *p2* pointing to a same object. 49 | 50 | There are two ways to solve the shallow copy problem, with or without the reference counting. In next articles, we will introduce different kinds of smart pointers in C++ utilities library. -------------------------------------------------------------------------------- /Chapter 11/Introduction-to-std-function.md: -------------------------------------------------------------------------------- 1 | # Introduction to *std::function* 2 | 3 | *std::function* is a powerful class in C++ 11 which is able to bind a function with a function object. To use it we need to include the functional library: 4 | 5 | ```cpp 6 | #include 7 | ``` 8 | 9 | *std::function* takes a function, and binds it into a function objects. Types of function parameters and return value need to be provided as the template parameter. Then the function can be called with operator`()` of the corresponding function object. The following example shows the usage of *std::function*: 10 | 11 | ```cpp 12 | void foo() { 13 | cout << "foo()" << endl; 14 | } 15 | void bar(string str) { 16 | cout << str << endl; 17 | } 18 | int sum(int a, int b) { 19 | return a + b; 20 | } 21 | int main() { 22 | function f1 = foo; 23 | f1(); // foo() 24 | function f2 = bar; 25 | f2("bar()"); // bar() 26 | function f3 = sum; 27 | cout << f3(20, 30) << endl; // 50 28 | return 0; 29 | } 30 | ``` 31 | 32 | Besides normal functions, *std::function* can be used to bind other function objects as well. Notice the underlying implementation of the lambda expression is also a function object. In the following example, we use *std::function* to bind a lambda expression which returns the sum of two values. 33 | 34 | ```cpp 35 | int main() { 36 | function f4 = [](int a, int b)->int {return a + b;}; 37 | cout << f4(100, 200) << endl; // 300 38 | return 0; 39 | } 40 | ``` 41 | 42 | Member functions can also be bound with *std::function*. Notice that member functions must rely on an object, and take a **this* pointer to the object as their first parameter. Therefore when using *std::function* we need to pass the type of the object pointer inside. And when calling the member function with operator`()`, we also need to pass in the address of the object. 43 | 44 | ```cpp 45 | class Test { 46 | public: 47 | void foo(string str) { 48 | cout << str << endl; 49 | } 50 | } 51 | 52 | int main() { 53 | function f5 = &Test::foo; 54 | f5(&Test(), "Test::foo()"); // Test::foo() 55 | return 0; 56 | } 57 | ``` 58 | 59 | *std::function* has a similar functionality to function pointers in C, but is much more powerful. A typical application of *std::function* is the callback function. Suppose we have a program which monitors users' input commands. Then different functions are called with different commands. Here we can create a map with commands as keys, and different function objects as values. We can write the functions elsewhere, and use *std::function* to bind them into function objects. This approach is a good design pattern in actual software development. -------------------------------------------------------------------------------- /Chapter 11/More-about-Binders.md: -------------------------------------------------------------------------------- 1 | # More about Binders 2 | 3 | In previous chapter we briefly introduced binders and how they can be used in generic algorithms. With binders, we can convert a binary function object into a unitary function object. Among them, *bind1st()* binds the first parameter of operator`()` into a fixed value, while *bind2st()* binds the second parameter into a fixed value. 4 | 5 | The following example sorts an array from largest to smallest. Then we want to insert 48 inside the array and keep the array sorted. *bind1st()* is used here to bind *greater* with 48 in order to find the first element smaller than 48. 6 | 7 | ```cpp 8 | #include 9 | int main() { 10 | int arr[] = {12, 4, 78, 9, 21, 43, 56, 52, 42, 31}; 11 | vector vec(arr, arr+sizeof(arr)/sizeof(arr[0])); 12 | sort(vec.begin(), vec.end(), greater()); 13 | for (int v : vec) { 14 | cout << v << " "; // 78 56 52 43 42 31 21 12 9 4 15 | } 16 | auto it = find_if(vec.begin(), vec.end(), bind1st(greater(), 48)); 17 | vec.insert(it, 48); 18 | for (int v : vec) { 19 | cout << v << " "; // 78 56 52 48 43 42 31 21 12 9 4 20 | } 21 | ... 22 | return 0; 23 | } 24 | ``` 25 | 26 | What is the underlying principle of a binder? Here we try to implement *bind1st()* ourselves. First we write our own *my_find_if()* function, which takes two iterators and a function object for the criterion. Then in the function, we call operator`()` of the function object on every elements within the range until we find the proper one. Apparently, the function object here should be unitary since it only takes one parameter. 27 | 28 | ```cpp 29 | template 30 | Iterator my_find_if(Iterator first, Iterator last, Compare comp) { 31 | for (; first != last; ++first) { 32 | if (comp(*first)) { 33 | return first; 34 | } 35 | } 36 | return last; 37 | } 38 | ``` 39 | 40 | Now let's implement *mybind1st()*. In general, this function takes a binary function object and returns a unitary one. Let's name the returned unitary function object as *_mybind1st*. Here we don't implement it directly, but encapsulate once with template. This enables the type deduction of template function so that we don't need to pass in template parameters explicitly. 41 | 42 | ```cpp 43 | template 44 | _mybind1st mybind1st(Compare comp, const T &val) { 45 | return _mybind1st(comp, val); 46 | } 47 | ``` 48 | 49 | Next is the *_mybind1st* class, which takes a binary function object and a value to be fixed as member variables. Then the operator`()` takes one parameter, and simple returns the binary function object with the first parameter fixed to the value. 50 | 51 | ```cpp 52 | template 53 | class _mybind1st { 54 | public: 55 | _mybind1st(Compare comp, T val) : _comp(comp), _val(val) {} 56 | bool operator()(const T &second) { 57 | return _comp(_val, second); 58 | } 59 | private: 60 | Compare _comp; 61 | T _val; 62 | } 63 | ``` 64 | 65 | As we can see, the underlying implementation of *bind1st()* and *bind2nd()* is simply a multi-layer encapsulation of function objects. However, their limitation is obvious: they are only for binary objects. What if the function object is more complicated, for example with multiple parameters? Fortunately, C++ 11 introduces two classes *std::function* and *std::bind*, which is more convenient and powerful. 66 | 67 | -------------------------------------------------------------------------------- /Chapter 11/More-about-Lambda-Expressions.md: -------------------------------------------------------------------------------- 1 | # More about Lambda Expressions 2 | 3 | Function objects are powerful, but every time we use a function object we need to define a class for it, which has a poor flexibility. In many cases we don't want to explicitly define a class, then we can use lambda expressions instead. 4 | 5 | In previous chapters we have already used lambda expressions a lot. Lambda expression is also known as an anonymous function since it can serve as a function without being given a name. A lambda function has the following syntax: 6 | 7 | ``` 8 | [External variables](Parameters)->Retuen value {Function Content} 9 | ``` 10 | 11 | The following example is a lambda expression which prints "Hello World" to the console. We use a function object to store it and call the function with operator `=`. 12 | 13 | ```cpp 14 | int main() { 15 | auto f1 = []()->void {cout << "Hello World!" << endl;}; 16 | f1(); // Hello World! 17 | return 0; 18 | } 19 | ``` 20 | 21 | With lambda expression, the compiler will automatically generate a function object class for as, something like this: 22 | 23 | ```cpp 24 | template 25 | class Lambda1 { 26 | public: 27 | Lambda1() {} 28 | void operator() const { 29 | cout << "Hello World!" << endl; 30 | } 31 | }; 32 | ``` 33 | 34 | Another example use lambda expression to calculate the sum of two integers: 35 | 36 | ```cpp 37 | int main() { 38 | auto f2 = [](int a, int b)->void {return a}; 39 | cout << f2(10,20) << endl; // 30 40 | return 0; 41 | } 42 | ``` 43 | 44 | This expression is equivalent to using a function object looks like: 45 | 46 | ```cpp 47 | template 48 | class Lambda2 { 49 | public: 50 | Lambda2() {} 51 | int operator(int a, int b) const { 52 | return a + b; 53 | } 54 | }; 55 | ``` 56 | 57 | Inside the square brackets `[]` is the external variables we want to lambda expression to capture. Its usage is as follows: 58 | 59 | - `[]`: No external variable is captured. 60 | 61 | - `[=]`: All external variables are captured and passed by value. 62 | 63 | - `[&]`: All external variables are captured and passed by reference. 64 | 65 | - `[this]`: `*this` pointer of an object is captured. 66 | 67 | - `[=, &a]`: All external variables are captured and passed by value, but *a* is passed by reference. 68 | 69 | - `[a, b]`: *a* and *b* are captured and passed by value. 70 | 71 | Now if we have lambda expression which takes two value and swap their values. Obviously, we should pass these two values by reference here: 72 | 73 | ```cpp 74 | int main() { 75 | int a = 10; 76 | int b = 20; 77 | auto f3 = [&a, &b]()->void { 78 | int tmp = a; 79 | a = b; 80 | b = tmp; 81 | }; 82 | f3(); 83 | cout << a << endl; // 20 84 | cout << b << endl; // 10 85 | return 0; 86 | } 87 | ``` 88 | 89 | It is equal to have a function object with two member variables which are initialized by reference, and operator `()` swap these two variables. 90 | 91 | ```cpp 92 | template 93 | class Lambda3 { 94 | public: 95 | Lambda3(int &a, int &b) : ma(a), mb(b) {} 96 | int operator(int a, int b) const { 97 | int tmp = ma; 98 | ma = mb; 99 | mb = tmp; 100 | } 101 | private: 102 | int &ma; 103 | int &mb; 104 | }; 105 | ``` 106 | 107 | Since lambda expression is actually a function object, we can use *std::function* to store it as well. The following example is a map whose key is an integer and value is a *std::function* object with two *int* parameters and return type *int*. Then we can use this map to store lambda expressions of arithmetic operations. 108 | 109 | ```cpp 110 | int main() { 111 | map> m; 112 | map[1] = [](int a, int b)->int{ return a + b;}; 113 | map[2] = [](int a, int b)->int{ return a - b;}; 114 | map[3] = [](int a, int b)->int{ return a * b;}; 115 | map[4] = [](int a, int b)->int{ return a / b;}; 116 | cout << m[1](10, 20) << endl; // 30 117 | cout << m[2](10, 20) << endl; // -10 118 | return 0; 119 | } 120 | ``` 121 | 122 | -------------------------------------------------------------------------------- /Chapter 11/More-about-std-function.md: -------------------------------------------------------------------------------- 1 | # More about *std::function* 2 | 3 | *std::function* can be used to bind a function into a function object. Then corresponding functions can be called through function objects. 4 | 5 | ```cpp 6 | void foo(string str) { 7 | cout << str << endl; 8 | } 9 | 10 | int main() { 11 | function f = foo; 12 | foo("foo()"); // foo() 13 | return 0; 14 | } 15 | ``` 16 | 17 | Now let's implement our own *myfunction* class for *foo()*. Since *foo()* takes one parameter and a return value, we have two parameters for the template as well. First we need to have a basic template class, and then write the specialized one. The class stores a function pointer of type *R(\*)(A1)*, and operator`()` simply calls the function with the pointer, and returns its return value. 18 | 19 | ```cpp 20 | template 21 | class myfunction {}; 22 | 23 | template 24 | class myfunction { 25 | public: 26 | using PFUNC = R(*)(A1); 27 | myfunction(PFUNC pfunc) : _pfunc(pfunc) {} 28 | R operator()(A1 a1) { 29 | return _pfunc(a1); 30 | } 31 | private: 32 | PFUNC _pfunc; 33 | }; 34 | 35 | int main() { 36 | myfunction f = foo; 37 | f("foo()"); // foo() 38 | return 0; 39 | } 40 | ``` 41 | 42 | Similarly, if we have a function *sum()* which takes two arguments, we need to write another specialized template class as well. This time the template has three parameters. 43 | 44 | ```cpp 45 | template 46 | class myfunction { 47 | public: 48 | using PFUNC = R(*)(A1, A2); 49 | myfunction(PFUNC pfunc) : _pfunc(pfunc) {} 50 | R operator()(A1 a1, A2 a2) { 51 | return _pfunc(a1, a1); 52 | } 53 | private: 54 | PFUNC _pfunc; 55 | }; 56 | 57 | int sum(int a, int b) { 58 | return a + b; 59 | } 60 | 61 | int main() { 62 | myfunction f = sum; 63 | cout << f(10, 20) << endl; // 30 64 | return 0; 65 | } 66 | ``` 67 | 68 | There is a problem here: functions with different number of parameters should have different version of *myfunction*. Should we write all of them? It seems impossible. Fortunately, templates are so powerful in C++ that they support variable number of parameters with operator `...`. We need to add `...` wherever the parameters are uncertain: 69 | 70 | ```cpp 71 | template 72 | class myfunction { 73 | public: 74 | using PFUNC = R(*)(A...); 75 | myfunction(PFUNC pfunc) : _pfunc(pfunc) {} 76 | R operator()(A... a) { 77 | return _pfunc(a...); 78 | } 79 | private: 80 | PFUNC _pfunc; 81 | }; 82 | ``` 83 | 84 | Now the compiler will automatically generate the correct version of *myfunction* when instantiating objects. -------------------------------------------------------------------------------- /Chapter 11/std-bind()-A-Simple-Thread-Pool.md: -------------------------------------------------------------------------------- 1 | # *std::bind()*: A Simple Thread Pool 2 | 3 | In previous chapter, we have introduced *bind1st* and *bind2nd*. These binders are used to bind function parameters with fixed value. However, they only apply to functions with two parameters. Now let's introduce a more powerful binder in C++ 11: *std::bind()*. 4 | 5 | *std::bind()* binds the parameters in a function, and returns a function object. *std::bind()* is implemented with function templates, so it supports type deduction of parameter types, and various number of parameters. *std::bind()* can be used to bind functions with different number of parameters, and member functions as well: 6 | 7 | ```cpp 8 | void foo(string str) { 9 | cout << str << endl; 10 | } 11 | 12 | int sum(int a, int b) { 13 | return a + b; 14 | } 15 | 16 | class Test { 17 | public: 18 | int sum(int a, int b) { 19 | return a + b; 20 | } 21 | }; 22 | 23 | int main() { 24 | bind(foo, "foo")(); // foo 25 | cout << bind(sum, 10, 20)() << endl; // 30 26 | cout << bind(&Test::sum, Test(), 20, 30)() << endl; // 50 27 | return 0; 28 | } 29 | ``` 30 | 31 | In the previous example, all parameters are bound with *std::bind()*. We can also bind a part of them with **parameter placeholders**. Parameter placeholders are within the namespace of *placeholders*, and consist of a underline and a number, which indicates the order of parameters. In the following example, we put a placeholder at the first parameter of *sum()*, and bind the other one with 10. It is the same as using *bind1st(sum, 10)*, but is much more flexible. 32 | 33 | ```cpp 34 | using namespace placeholders 35 | int main() { 36 | cout << bind(sum, _1, _2)(20, 30) << endl; // 50 37 | cout << bind(sum, _1, 10)(20) << endl; // 30 38 | return 0; 39 | } 40 | ``` 41 | 42 | Since *std::bind()* returns a function object, we can use a *std::function* object with it. The next example use an object of type *function* to receive the returned function object, where the binder can be reused later. 43 | 44 | ```cpp 45 | int main() { 46 | function f = bind(foo, _1); 47 | f("foo"); // foo 48 | f("bar"); // bar 49 | return 0; 50 | } 51 | ``` 52 | 53 | *std::bind()* are widely used in large-scale C++ projects. We can implement a simple thread pool with the feature of *std::bind()*. More about multithreading will be introduced later. Now let's just look at this example. 54 | 55 | ```cpp 56 | #include 57 | #include 58 | #include 59 | #include 60 | using namespace std; 61 | 62 | class Thread { 63 | public: 64 | Thread(function func) : _func(func) {} 65 | 66 | thread start() { 67 | thread t(_func); 68 | return t; 69 | } 70 | 71 | private: 72 | function _func; 73 | }; 74 | 75 | class ThreadPool { 76 | public: 77 | ThreadPool() {} 78 | 79 | ~ThreadPool() { 80 | for (int i = 0; i < _pool.size(); i++) { 81 | delete _pool[i]; 82 | } 83 | } 84 | void startPool(int size) { 85 | for (int i = 0; i < size; i++) { 86 | _pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, i))); 87 | } 88 | 89 | for (int i = 0; i < size; i++) { 90 | _handler.push_back(_pool[i]->start()); 91 | } 92 | 93 | for (thread &t : _handler) { 94 | t.join(); 95 | } 96 | } 97 | 98 | private: 99 | vector _pool; 100 | vector _handler; 101 | void runInThread(int id) { cout << "Thread " << id << endl; } 102 | }; 103 | ``` 104 | 105 | The above example has a *Thread* class which basically takes a function object and *start()* executes that function in the thread. Then inside *ThreadPool*, we create a certain number of *Thread* objects, and call their *start()* method respectively. Here we also have a function *runInThread()* which is exactly the function we want to execute in each thread. Since this function is a member function, we can not pass it as a function pointer or else to the constructor of *Thread*. Instead, we use *std::bind()* to bind its parameter, and convert it into a function object. In this way, *Thread::start()* can call the function directly with operator `()`. 106 | 107 | Now in the main function we create a thread pool, and start with 10 threads. 108 | 109 | ```cpp 110 | int main() { 111 | ThreadPool pool; 112 | pool.startPool(10); 113 | return 0; 114 | } 115 | ``` 116 | 117 | The program has the following output. Since each print statement is executed in each thread disorderly, the output is disordered as well. 118 | 119 | ``` 120 | Thread Thread 10 121 | 122 | Thread 2 123 | Thread 3 124 | Thread 4 125 | Thread 5 126 | Thread 6 127 | Thread 7 128 | Thread 8 129 | Thread 9 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /Chapter 12/Atomic-Operations.md: -------------------------------------------------------------------------------- 1 | # Atomic Operations 2 | 3 | In previous chapter, we wrote a multithreaded program that sells bus tickets. We used mutual exclusion for thread synchronization. Inside the critical section, only one thread can execute at the same time. Though mutex is the most common tool for synchronization, it is somehow heavy since it block other threads to keep waiting. For some simple operations, for example adding and subtracting, we can keep thread safety without using any locks. These operations are known as **atomic operations**. 4 | 5 | The word "atomic" means uncuttable. A task performed by a computer can't be broken into smaller steps if it is atomic. In other words, there is no way for a thread to slip through an atomic operation concurrently performed by another one. Remember in traditional `++` operation, it takes three steps to add a variable by 1: load the value of the variable into a register, add the register by 1, and store the value back to the variable. With atomic operations, the load and store operations of the variable are uncuttable, therefore there's no possibility of race condition. 6 | 7 | ```assembly 8 | mov eax, a 9 | add eax, 1 10 | mov a, eax 11 | ``` 12 | 13 | Atomic operations are low-level, which need the support of hardware. The most common strategy to achieve atomicity is called **compare-and-swap (CAS)**. It compares the contents of a memory location with a given value and, only if they are the same, modifies the contents of that memory location to a new given value. 14 | 15 | C++ 11 provides an atomic library which supports atomic operations: 16 | 17 | ```cpp 18 | #include 19 | ``` 20 | 21 | Here we use a simple example to illustrate it. We defined two variables, *isReady* and *myCount* which is of type *atomic_bool* and *atomic_int* respectively. They are just aliases for *atomic\* and *atomic\*. Then we create 10 threads, and each of them call `myCount++` for a hundred times. *this_thread::yield()* is called to skip the current CPU time slice and wait for the next scheduling, if the thread creation has not finished yet. If we are not using atomic operations here, there would be races between threads, and the final result is uncertain. But with atomic operations, we can get a correct result of 1000. 22 | 23 | ```cpp 24 | atomic_bool isReady; 25 | atomic_int myCount; 26 | 27 | void task() { 28 | while (!isReady) { 29 | this_thread::yield(); 30 | } 31 | for (int i = 0; i < 100; i++) { 32 | myCount++; 33 | } 34 | } 35 | 36 | int main() { 37 | list tlist; 38 | for (int i = 0; i < 10; i++) { 39 | tlist.push_back(thread(task)); 40 | } 41 | isReady = true; 42 | for (thread &t : tlist) { 43 | t.join(); 44 | } 45 | cout << myCount << endl; // 1000 46 | return 0; 47 | } 48 | ``` 49 | 50 | However, the above program still has potential problems. We will discuss more in the next article. -------------------------------------------------------------------------------- /Chapter 12/Important-Features-in-C++11.md: -------------------------------------------------------------------------------- 1 | # Important Features in C++11 2 | 3 | So far, we have already learnt most of the commonly used C++ features. Many of them are introduced in C++11 standard. Here we sum up a list of some important features in C++11. 4 | 5 | ## Keywords and Syntax 6 | 7 | - `auto`: A type identifier that can automatically derives the type from the right side of operator `=`. 8 | 9 | - `nullptr`: A specialized keyword that refers to a NULL pointer. 10 | 11 | - For each: Traverse the container. The syntax is `for(element: containter)`. Underlying implementation is iterators. 12 | 13 | - Rvalue reference `&&`: A reference to a Rvalue. It supports move semantics *std::move()* and perfect forwarding *std::forwar()*. 14 | - `...`: Templates with variable number of paramters. 15 | 16 | ## Smart Pointers 17 | 18 | *shared_ptr*: A pointer with reference counting. Multiple pointers can point to a same object. 19 | 20 | *weak_ptr*: A observer pointer which will not change the reference count of the recourse, but can only examine if the object exists or not. 21 | 22 | ## Function Objects and Binders 23 | 24 | *std::function*: A powerful class able to bind a function with a function object. 25 | 26 | *std::bind()*: Binds the parameters in a function, and returns a function object. 27 | 28 | Lambda expressions: Anonymous functions that can serve as a function object without being given a name. 29 | 30 | ## Containers 31 | 32 | *unordered_set*: A associative container storing keys. Its underlying implementation is a hash map. 33 | 34 | *unordered_map*: A associative container storing key-value pairs. Its underlying implementation is a hash map. 35 | 36 | *array*: An array container. Unlike *vector*, the size of the array is immutable. 37 | 38 | *forward_list*: A single linked list. *list* is a double linked list. 39 | 40 | ## Multithreading 41 | 42 | Previous C++ standard doesn't support language-level thread library. Since different operating system has different thread API, there are troubles when writing cross-platform codes. Now C++11 has its own thread support library which is very easy to use. In this chapter we will introduce threads, locks and atomic operations in C++. -------------------------------------------------------------------------------- /Chapter 12/Multithreaded-Programming-with-std-thread.md: -------------------------------------------------------------------------------- 1 | # Multithreaded Programming with *std::thread* 2 | 3 | *std::thread* is a class that encapsulates multithreading APIs of the operating system. Different operating systems has different API functions. For example, *createThread()* is used to create a thread in Windows, while *pthread_create()* is used instead in Linux. With *std::thread*, programmers can write multithreaded programs more easily. To use *std::thread*, we should include the thread library: 4 | 5 | ```cpp 6 | #include 7 | ``` 8 | 9 | We can start a thread by creating a *thread* object with the thread function. In the following example, we create a thread *t* which executes function *threadHander()*. The thread starts to execute after it is created. 10 | 11 | ***join()*** blocks the current thread, and wait for the child thread to terminate. The current thread continues to run only after the child thread has been terminated. 12 | 13 | ```cpp 14 | void threadHandler() { 15 | cout << "Hello World!" << endl; 16 | } 17 | 18 | int main() { 19 | thread t(threadHandler); 20 | t.join(); 21 | cout << "Thread Done!" << endl; 22 | return 0; 23 | } 24 | ``` 25 | 26 | The above example has the following output: 27 | 28 | ``` 29 | Hello World! 30 | Thread Done! 31 | ``` 32 | 33 | We can also pass arguments into the thread function. In the following example, we have a thread function which sleeps the thread for several seconds. We use *this_thread::sleep_for()* for sleeping and *chrono::seconds()* for the number of seconds. 34 | 35 | ```cpp 36 | void threadHandler(int sec) { 37 | this_thread::sleep_for(chrono::seconds(sec)); 38 | cout << "Hello World!" << endl; 39 | } 40 | 41 | int main() { 42 | thread t(threadHandler, 2); 43 | t.join(); 44 | cout << "Thread Done!" << endl; 45 | return 0; 46 | } 47 | ``` 48 | 49 | So far we use *join()* to make the main thread wait for the child thread to terminate. But what if we don't do this? When the main thread finishes its running, it will check if there are child threads which have not finished yet. If so, the process will terminate abnormally. 50 | 51 | ```cpp 52 | int main() { 53 | thread t(threadHandler, 2); 54 | // t.join(); 55 | cout << "Thread Done!" << endl; 56 | return 0; // ERROR 57 | } 58 | ``` 59 | 60 | If we don't want the main thread to wait for the child thread, we can use ***detach()***. *detach()* will separate the child thread with the main thread. After the process terminates, all the unfinished threads will be terminated as well. 61 | 62 | ```cpp 63 | int main() { 64 | thread t(threadHandler, 2); 65 | t.detach(); 66 | cout << "Thread Done!" << endl; 67 | return 0; 68 | } 69 | ``` 70 | 71 | In all, *join()* ensures that the main thread can not terminate before the child thread is terminated, while *detach()* does not. We should always remember to use either *join()* or *detach()* after a thread is created. -------------------------------------------------------------------------------- /Chapter 12/Thread-Visibility-and-volatile.md: -------------------------------------------------------------------------------- 1 | # Thread Visibility and `volatile` 2 | 3 | In the previous chapter we see an example of atomic operations. However, the program has a potential problem which is common in multithreaded programming which is called **thread visibility**. In multithreaded programming, shared variables are often used to notify other threads that a certain state has changed, and these threads are supposed to sense the change in time. In the previous example, we use a global atomic variable *isReady* to tell the threads whether they are ready to work or not. 4 | 5 | In a symmetric multiprocessor architecture (SMP), multiple processors and cores share a same global memory through the bus, but each processor has its own cache as well. During thread executions, the data is usually loaded into caches or registers instead of reading from the global memory directly to improve access efficiency. However, this optimization may bring problems in multithreaded programs. When a thread makes a modification to a shared variable, it may just modify the temporary value in the register or cache, and may not update the global memory immediately. Even if the memory is updated in time, other threads on other processors may not read from it immediately, and can not perceive the changes of the shared data. 6 | 7 | To fix this problem, we can use keyword `volatile` to force each thread to always access the original data in the global memory: 8 | 9 | ```cpp 10 | volatile atomic_bool isReady; 11 | volatile atomic_int myCount; 12 | ``` 13 | 14 | The word "volatile" means liable to change rapidly and unpredictably. It is a type modifier which tells the compiler that the variable may be changed by some factors unknown to the compiler, for example operating system, hardware or multi-threads. When encountering variables declared by `volatile`, the compiler will no longer optimize the code, and provides stable access to the original memory address. This non-optimizable feature is reflected in two places: 15 | 16 | First, do not make any optimization involving memory access. When accessing a memory object multiple times, the compiler tends to put the data in the cache or in the register, and access the object from them. With `volatile`, the data is always read from the memory, even if its previous instruction just read the data. Similarly, the data is written into the global memory immediately after being modified. 17 | 18 | Secondly, keeps the order of instructions unchanged. For multiple read and write operations on variables with `volatile`, the compiler cannot eliminate some of the read and write operations based on its prediction. Moreover, it ensures that the previous read and write operations must be completed before the subsequent read and write operations. 19 | 20 | A good approach in multithreaded programming is to always declare shared variables as `volatile`. Notice that `volatile` only ensures that the compiler won't do any optimization on the variable, but there is not any guarantee on thread safety. Please don't confuse this concept with atomicity, as some people may do. -------------------------------------------------------------------------------- /Chapter 13/Adapter-Pattern.md: -------------------------------------------------------------------------------- 1 | # Adapter Pattern 2 | 3 | The adapter pattern is able to convert an interface into another, and let incompatible interfaces work together. It is also a structural pattern. 4 | 5 | There are two popular interface in multimedia devices, HDMI and VGA. Suppose we have a *VGA* class, and a display device *TV1* which uses the interface. 6 | 7 | ```cpp 8 | class VGA { 9 | public: 10 | virtual void play() = 0; 11 | }; 12 | 13 | class TV1 : public VGA { 14 | public: 15 | void play() { 16 | cout << "TV1: VGA" << endl; 17 | } 18 | }; 19 | ``` 20 | 21 | What's more, we have a computer which supports VGA interface: 22 | 23 | ```cpp 24 | class Computer { 25 | public: 26 | void playVideo(VGA *pVGA) { 27 | pVGA->play(); 28 | } 29 | }; 30 | ``` 31 | 32 | Since the interfaces of the computer and the screen are consistent, we can directly connect them together by passing a *VGA* pointer into the *playVideo()* method. 33 | 34 | ```cpp 35 | int main() { 36 | Computer computer; 37 | computer.playVideo(new TV1()); // TV1: VGA 38 | return 0; 39 | } 40 | ``` 41 | 42 | Now suppose we have another monitor which supports HDMI interface: 43 | 44 | ```cpp 45 | class HDMI { 46 | public: 47 | virtual void play() = 0; 48 | }; 49 | 50 | class TV2 : public HDMI { 51 | public: 52 | void play() { 53 | cout << "TV2: HDMI" << endl; 54 | } 55 | }; 56 | ``` 57 | 58 | Since our computer only takes a *VGA* pointer, we can not use this monitor directly. There are two ways to do so. We can simply change a computer which supports HDMI, which is equivalent to code refactoring. This method is once and for all, but would be a painful thing if there are a large amount of code. Or we can use an adapter which convert VGA into HDMI. 59 | 60 | Here we add an *Adapter* class which inherits from *VGA*. The class keeps a *HDMI* pointer as its member variable, and calls the *play()* method of *HDMI* from its own *play()* method. 61 | 62 | ```cpp 63 | class Adapter : public VGA { 64 | public: 65 | Adapter(HDMI *p) : pHDMI(p) {} 66 | void play() { 67 | pHDMI->play(); 68 | } 69 | private: 70 | HDMI *pHDMI; 71 | }; 72 | ``` 73 | 74 | Then we can use our adapter to connect the computer to *TV2*. The *playVideo()* method takes the *VGA* adapter, which is constructed by the *HDMI* object. 75 | 76 | ```cpp 77 | int main() { 78 | Computer computer; 79 | computer.playVideo(new Adapter(new TV2())); // TV2: HDMI 80 | return 0; 81 | } 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /Chapter 13/Decorator-Pattern.md: -------------------------------------------------------------------------------- 1 | # Decorator Pattern 2 | 3 | The decorator pattern enables us to add new functionality to an existing class, without changing its structure. Similar to proxy pattern, the decorator pattern is also a structural pattern, where the decorator class acts as a wrapper for the existing class. 4 | 5 | Generally, inheritance is also used to extend the functionality of a class. But as the extended functionality increases, the number of derived classes will expand as well. Therefore, the decorator pattern is more flexible. 6 | 7 | Let's look at the following example, where we have an abstract class *Car* and three brands of cars that implement it and override *show()* method. 8 | 9 | ```cpp 10 | class Car { 11 | public: 12 | virtual void show() = 0; 13 | }; 14 | 15 | class Bmw : public Car { 16 | public: 17 | void show() { 18 | cout << "This is a BMW" << endl; 19 | } 20 | }; 21 | 22 | class Audi : public Car { 23 | public: 24 | void show() { 25 | cout << "This is an Audi" << endl; 26 | } 27 | }; 28 | 29 | class Benz : public Car { 30 | public: 31 | void show() { 32 | cout << "This is a Benz" << endl; 33 | } 34 | }; 35 | ``` 36 | 37 | Now suppose we want to add new features to these cars. Instead of using inheritance, we can define a decorator class instead. Here we have a class *CarDecorator1* which inherits *Car*. The class has a *Car* object as its member. The class overrides *show()*, and besides calling the *show()* method of the *Car* object, it implements its own functionality here. This pattern is similar to the proxy pattern, but it actively takes a *Car* object as a parameter in its constructor. We can also write *CarDecorator2* and *CarDecorator3* which implements other features in a similar way. 38 | 39 | ```cpp 40 | class CarDecorator1 : public Car { 41 | public: 42 | CarDecorator1(Car *p) : pCar(p) {} 43 | ~CarDecorator1() { 44 | delete pCar; 45 | } 46 | void show() { 47 | pCar->show(); 48 | cout << "Feature 1" << endl; 49 | } 50 | private: 51 | Car *pCar; 52 | }; 53 | ``` 54 | 55 | To use the decorator, we create a decorator object, and pass in the *Car* object to be decorated. In this way, different brand of cars can have different features without modifying the original class. 56 | 57 | ```cpp 58 | int main() { 59 | Car *p1 = new CarDecorator1(new Bmw()); 60 | Car *p2 = new CarDecorator2(new Audi()); 61 | Car *p3 = new CarDecorator3(new Benz()); 62 | p1->show(); 63 | p2->show(); 64 | p3->show(); 65 | delete p1; 66 | delete p2; 67 | delete p3; 68 | return 0; 69 | } 70 | ``` 71 | 72 | The above code has the following output: 73 | 74 | ``` 75 | This is a Bwm 76 | Feature 1 77 | This is an Audi 78 | Feature 2 79 | This is a Benz 80 | Feature 3 81 | ``` 82 | 83 | Moreover, a single object can also have multiple features: just use the decorator in a nested way by keep passing the same pointer inside: 84 | 85 | ```cpp 86 | int main() { 87 | Car *p1 = new CarDecorator1(new Bmw()); 88 | p1 = new CarDecorator2(p1); 89 | p1 = new CarDecorator3(p1); 90 | p1->show(); 91 | delete p1; 92 | return 0; 93 | } 94 | ``` 95 | 96 | The above code has the following output: 97 | 98 | ```cpp 99 | This is a Bwm 100 | Feature 1 101 | Feature 2 102 | Feature 3 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /Chapter 13/Observer-Pattern.md: -------------------------------------------------------------------------------- 1 | # Observer Pattern 2 | 3 | The observer pattern belongs to the behavior pattern, which focuses on the communication between objects. The observer pattern applies to a one-to-many relationship of objects, where multiple objects rely on a single object. When the state of the object changes, other objects can receive corresponding notifications. 4 | 5 | A common observer pattern is the publish-subscribe model, where the observers subscribe to a subject, and the subject publishes notification to the observers when its status changes. 6 | 7 | Here we have an abstract class *Observer*, and three classes that inherit from it. These classes override the *handle()* method which handles messages according to their IDs. *Observer1* handles message 1 and 2, while *Observer2* handles message 2 and 3, and *Observer3* handles message *1* and *3*. 8 | 9 | ```cpp 10 | class Observer { 11 | public: 12 | virtual void handle(int id) = 0; 13 | }; 14 | 15 | class Observer1 : public Observer { 16 | public: 17 | void handle(int id) { 18 | switch (id) { 19 | case 1: 20 | cout << "Message 1" << endl; 21 | break; 22 | case 2: 23 | cout << "Message 2" << endl; 24 | break; 25 | default: 26 | cout << "Unknown message" << endl; 27 | break; 28 | } 29 | } 30 | }; 31 | 32 | class Observer2 : public Observer { 33 | public: 34 | void handle(int id) { 35 | switch (id) { 36 | case 2: 37 | cout << "Message 2" << endl; 38 | break; 39 | case 3: 40 | cout << "Message 3" << endl; 41 | break; 42 | default: 43 | cout << "Unknown message" << endl; 44 | break; 45 | } 46 | } 47 | }; 48 | 49 | class Observer3 : public Observer { 50 | public: 51 | void handle(int id) { 52 | switch (id) { 53 | case 1: 54 | cout << "Message 1" << endl; 55 | break; 56 | case 3: 57 | cout << "Message 3" << endl; 58 | break; 59 | default: 60 | cout << "Unknown message" << endl; 61 | break; 62 | } 63 | } 64 | }; 65 | ``` 66 | 67 | Then we have the *Subject* class which keeps an unordered map. The map keys are message IDs, and the values are lists of observers. The class has two methods *subscribe()* and *publish()*. The former takes an *Observer* and the message ID to be subscribed, and add the observer to the map with message ID as the key. *publish()* takes the message ID to be published, takes out the observers from the map who have subscribed to this message, and call their *handle()* method one by one. 68 | 69 | ```cpp 70 | class Subject { 71 | public: 72 | void subscribe(Observer *observer, int id) { 73 | _map[id].push_back(observer); 74 | } 75 | void publish(int id) { 76 | auto it = _map.find(id); 77 | if (it != _map.end()) { 78 | for (Observer *p : it->second) { 79 | p->handle(id); 80 | } 81 | } 82 | } 83 | private: 84 | unordered_map> _map; 85 | }; 86 | ``` 87 | 88 | Now in the *main()* function, we subscribes the observers to the messages they would like to handle. When the status of the subject changes, it notify the observers by publishing the corresponding messages. 89 | 90 | ```cpp 91 | int main() { 92 | Subject subject; 93 | Observer *p1 = new Observer1(); 94 | Observer *p2 = new Observer2(); 95 | Observer *p3 = new Observer3(); 96 | 97 | subject.subscribe(p1, 1); 98 | subject.subscribe(p1, 2); 99 | subject.subscribe(p2, 2); 100 | subject.subscribe(p2, 3); 101 | subject.subscribe(p3, 1); 102 | subject.subscribe(p3, 3); 103 | 104 | subject.publish(1); 105 | cout << "--------------------" << endl; 106 | subject.publish(2); 107 | cout << "--------------------" << endl; 108 | subject.publish(3); 109 | cout << "--------------------" << endl; 110 | subject.publish(4); 111 | delete p1, p2, p3; 112 | } 113 | ``` 114 | 115 | The above code has the following outputs: 116 | 117 | ``` 118 | Observer1: Message 1 119 | Observer3: Message 1 120 | -------------------- 121 | Observer1: Message 2 122 | Observer2: Message 2 123 | -------------------- 124 | Observer2: Message 3 125 | Observer3: Message 3 126 | -------------------- 127 | ``` 128 | 129 | -------------------------------------------------------------------------------- /Chapter 13/Proxy-Pattern.md: -------------------------------------------------------------------------------- 1 | # Proxy Pattern 2 | 3 | The proxy pattern provides a proxy for other objects which controls the access permission of objects. Let's look at this example, where we have a abstract class *Movie* and a derived class *MovieSite* which provides free movies and VIP movies. Free movies are for general users, while VIP movies are for members. 4 | 5 | ```cpp 6 | class Movie { 7 | public: 8 | virtual void freeMovie() = 0; 9 | virtual void vipMovie() = 0; 10 | }; 11 | 12 | class MovieSite : public Movie { 13 | public: 14 | virtual void freeMovie() { 15 | cout << "Free Movie" << endl; 16 | } 17 | virtual void vipMovie() { 18 | cout << "VIP Movie" << endl; 19 | } 20 | }; 21 | ``` 22 | 23 | However, if we write the class like this, its objects can access both *freeMovie()* and *vipMovie()*, which means we have to judge the authority of users in the service layer. It is apparently not a good OOP design. 24 | 25 | ```cpp 26 | int main() { 27 | MovieSite *p = new MovieSite(); 28 | p->freeMovie(); // Free Movie 29 | p->vipMovie(); // VIP Movie 30 | delete p; 31 | return 0; 32 | } 33 | ``` 34 | 35 | A better approach is to provide a proxy class which controls the access permission of users. Here we have a *FreeMovieProxy* which acts as a proxy of free movies. The class combines a *MovieSite* object as its member. In this way, the *freeMovie()* method of the delegate class *MovieSite* is called through the *freeMovie()* method of its proxy class. Moreover, since general users can not watch VIP movies, the *vipMovie()* method of *MovieSite* can not be called through the proxy. 36 | 37 | ```cpp 38 | class FreeMovieProxy : public Movie { 39 | public: 40 | FreeMovieProxy() { 41 | pMovie = new MovieSite(); 42 | } 43 | ~FreeMovieProxy() { 44 | delete pMovie; 45 | } 46 | virtual void freeMovie() { 47 | pMovie->freeMovie(); 48 | } 49 | virtual void vipMovie() { 50 | cout << "Permission denied!" << endl; 51 | } 52 | private: 53 | MovieSite *pMovie; 54 | }; 55 | ``` 56 | 57 | Similarly, we have a *VipMovieProxy* class which acts as a proxy of VIP movies. In this class, users have the full permissions to *freeMovie()* and *vipMovie()* of *MovieSite*. 58 | 59 | ```cpp 60 | class VipMovieProxy : public Movie { 61 | public: 62 | VipMovieProxy() { 63 | pMovie = new MovieSite(); 64 | } 65 | ~VipMovieProxy() { 66 | delete pMovie; 67 | } 68 | virtual void freeMovie() { 69 | pMovie->freeMovie(); 70 | } 71 | virtual void vipMovie() { 72 | pMovie->vipMovie(); 73 | } 74 | private: 75 | MovieSite *pMovie; 76 | }; 77 | ``` 78 | 79 | Then in the service module, the client accesses the proxy object instead of the original object, where the permissions of users can be managed. 80 | 81 | ```cpp 82 | int main() { 83 | MovieSite *p = new FreeMovieProxy(); 84 | p->freeMovie(); // Free Movie 85 | p->vipMovie(); // Permission denied! 86 | delete p; 87 | return 0; 88 | } 89 | ``` 90 | 91 | -------------------------------------------------------------------------------- /Chapter 2/Default-Parameters.md: -------------------------------------------------------------------------------- 1 | 2 | # Default Parameters 3 | 4 | When passing arguments to a funtion, we can do this in a naive way: 5 | 6 | ```cpp 7 | int sum(int a, int b) { 8 | return a + b; 9 | } 10 | 11 | int main() { 12 | int a = 10; 13 | int b = 20; 14 | int ret = sum(a, b); 15 | cout << "return: " << ret << endl; 16 | return 0; 17 | } 18 | ``` 19 | 20 | Alternatively, we can assign values to parameters inside the funtion brackets. These parameters with initialize values are called **default parameters**. Then in our main() function, we can pass inside a single parameter *a*. Then the second argument is automatically assigned to 20 since we do not pass it inside. 21 | 22 | ```cpp 23 | int sum(int a, int b = 20) { 24 | return a + b; 25 | } 26 | 27 | int main() { 28 | int a = 10; 29 | int ret = sum(a); 30 | cout << "return: " << ret << endl; 31 | return 0; 32 | } 33 | ``` 34 | 35 | It is worth noting that the defualt parameters have to be assigned from right to left. For example, the following code is illegal. Recall that the function parameters are pushed into stack frame from right to left, and it will cause ambiguity if we don't follow this pattern. (In the following case, how does the compiler know variable *b* inside main() is passing to argument *a* or *b* ?) 36 | 37 | ```cpp 38 | int sum(int a = 10, int b) { 39 | return a + b; 40 | } 41 | 42 | int main() { 43 | int b = 20; 44 | int ret = sum(b); // ERROR 45 | cout << "return: " << ret << endl; 46 | return 0; 47 | } 48 | ``` 49 | 50 | Moreover, we can also give default parameters in declaration besides definition: 51 | 52 | ```cpp 53 | int sum(int a = 10, int b = 20); 54 | 55 | int main() { 56 | int ret = sum(); 57 | cout << "return: " << ret << endl; 58 | return 0; 59 | } 60 | 61 | int sum(int a, int b) { 62 | return a + b; 63 | } 64 | ``` 65 | 66 | But we must pay attention that the same default argument can only occurs once, no matter in declaration or definition. For example, the following program is invaild, because *a* and *b* are assigned twice in declaration and definition. 67 | 68 | ```cpp 69 | int sum(int a = 10, int b = 20); 70 | 71 | int main() { 72 | int ret = sum(); 73 | cout << "return: " << ret << endl; 74 | return 0; 75 | } 76 | 77 | int sum(int a = 10, int b = 20) { // ERROR 78 | return a + b; 79 | } 80 | ``` 81 | 82 | Then let's look at the following code. Although the default parameters shows up from left to right in line 2, this program is actually valid. When the compiler parsing the code, it first reaches line 1, and record the value of *b*. When it reaches line 2, the compiler already knows that *b* equals 20, so it only needs the value of *a*. 83 | 84 | ```cpp 85 | int sum(int a, int b = 20); 86 | int sum(int a = 10, int b); // Valid 87 | 88 | int main() { 89 | int ret = sum(); 90 | cout << "return: " << ret << endl; 91 | return 0; 92 | } 93 | 94 | int sum(int a, int b) { 95 | return a + b; 96 | } 97 | ``` 98 | 99 | Sometimes function with default parameters can enhance the performance. In the naive approach we pass two variables inside the function, and the assembly code of pushing parameters into stack looks like: 100 | 101 | ```assembly 102 | mov eax, dword ptr[ebp-8] 103 | push eax 104 | mov ecx, dword ptr[ebp-4] 105 | push ecx 106 | ``` 107 | 108 | Then if only one variable is passed, there is no need for moving the value of variable into register *eax*. We can directly push the constant into the stack, thus eliminating one *mov* command. Of course, if we manually pass constants inside the function like ```int ret = sum(10, 20)```, there's no such improvement since we also push the constant into stack directly. 109 | 110 | -------------------------------------------------------------------------------- /Chapter 2/Function-Overloading.md: -------------------------------------------------------------------------------- 1 | # Function Overloading 2 | 3 | In the following code, we have three compare functions that compare the value of two arguments. Notice that their function names are the same, but the arguments types are different. This pattern is called **function overloading**. 4 | 5 | ```cpp 6 | bool compare(int a, int b) { 7 | cout << "compare_int_int" << endl; 8 | return a > b; 9 | } 10 | 11 | bool compare(double a, double b) { 12 | cout << "compare_double_double" << endl; 13 | return a > b; 14 | } 15 | 16 | bool compare(const char *a, const char *b) { 17 | cout << "compare_char*_char*" << endl; 18 | return strcmp(a, b); 19 | } 20 | 21 | int main() { 22 | compare(10, 20); 23 | compare(10.0, 20.0); 24 | compare("aaa", "bbb"); 25 | return 0; 26 | } 27 | ``` 28 | 29 | Function overloading refers to a set of functions with the same function name, but different arguments list, either in number or type. Moreover, these functions have to be in the same scope. For example, the following code won't compile, because all three statements call the same function which is declared in the first line of main(). 30 | 31 | ```cpp 32 | int main() { 33 | bool compare(int a, int b); 34 | 35 | compare(10, 20); 36 | compare(10.0, 20.0); 37 | compare("aaa", "bbb"); // ERROR 38 | return 0; 39 | } 40 | ``` 41 | 42 | Arguments modified with *const* or *volatile* need special attention. For example, in the following codes, the former will cause an compile error, but the latter is valid. We will take more about these two keywords in the future. 43 | 44 | ```cpp 45 | void func(int a) {} 46 | void func(const int a) {} // ERROR 47 | ``` 48 | 49 | ```cpp 50 | void func(int *a) {} 51 | void func(const int *a) {} // Valid 52 | ``` 53 | 54 | In addition, if a set of functions have the same function name and the same arguments list, but their return types are different, this is not regarded as a function overloading, and will raise compile error. 55 | 56 | Function overloading is considered as a behavior of polymorphism. There are two types of polymorphism, static and dynamic. The former is compile-time, and the latter is runtime. Function overloading is a static polymorphism, because function call is proceeded in compiling. 57 | 58 | It is worth mentioning that function overloading is only supported in C++ but not in C. This is due to the different strategy in generating function symbols. We can compile the code above and take a look at the symbol table with ```objdump -t main.o | grep compare```. In C++, function symbols are named by both the function name and the arguments list, while function symbols are only named by function name in C. Thus, functions with the same name but different arguments are considered the same in C. 59 | 60 | ```shell 61 | 0000000000000000 g F .text 0000000000000041 _Z7compareii 62 | 0000000000000041 g F .text 0000000000000049 _Z7comparedd 63 | 000000000000008a g F .text 0000000000000052 _Z7comparePKcS0_ 64 | ``` 65 | 66 | What if we have the object file compiled in C and we want to use it in C++? Assume we have two file as follow: 67 | 68 | **sum.c** 69 | 70 | ```c 71 | int sum(int a, int b) { 72 | return a + b; 73 | } 74 | ``` 75 | 76 | **main.cpp** 77 | 78 | ```cpp 79 | int sum(int a, int b); 80 | 81 | int main() { 82 | int ret = sum(10, 20); 83 | cout << "return: " << ret << endl; 84 | return 0; 85 | } 86 | ``` 87 | 88 | An error has occurred! There is an undefined reference to `sum(int, int)` inside main.cpp. This is because the function sum is named *sum* in the symbol table of sum.o, but is *_Z3sumii* in main.o. To fix this, we can rewrite main.cpp like this. 89 | 90 | ```cpp 91 | extern "C" { 92 | int sum(int a, int b); 93 | } 94 | 95 | int main() { 96 | int ret = sum(10, 20); 97 | cout << "return: " << ret << endl; 98 | return 0; 99 | } 100 | ``` 101 | 102 | ```extern "C"``` tells the compiler to generate the function symbol as the way C does. In this way sum() is named *sum* now and the linker can find it's definition. Similarly, if we want to use C++ code in C, we also need to put function declarations inside ```extern "C"``` brackets in our C++ file (There's no such things like ```extern "C++"``` in C). 103 | 104 | In actual project codes, we often do this in a cleverer way: 105 | 106 | ```cpp 107 | #ifdef __cplusplus 108 | extern "C" { 109 | #endif 110 | int sum(int a, int b) { 111 | return a + b; 112 | } 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | ``` 117 | 118 | `__cplusplus` is a built-in macro in all C++ compilers. By using compiler macros, the code above can be compiled with either C or C++ compilers. -------------------------------------------------------------------------------- /Chapter 2/Inline-Function.md: -------------------------------------------------------------------------------- 1 | # Inline Function 2 | 3 | Let's look at the following program 4 | 5 | ```cpp 6 | int sum(int x, int y) { 7 | return x + y; 8 | } 9 | 10 | int main() { 11 | int a = 10; 12 | int b = 20; 13 | int ret = sum(a, b); 14 | return 0; 15 | } 16 | ``` 17 | 18 | When main() calls sum(), it needs to push all parameters into stack frame, create a new stack frame for sum(), and return the stack frame back to system after it quits. But what if we call sum() thousands of times? Notice that sum() function only involves three operations (*mov*, *add*, and *mov*), then it is a huge amount of overhead in calling the function comparing to the calculation itself. Under this circumstances, we had better mark function sum() as **inline**. 19 | 20 | An function with inline simply does one work: it expand all the commands inside the function, and insert them into where the function is called, during the compiling process. In this way, the function calling process is eliminated. 21 | 22 | ``` cpp 23 | inline int sum(int x, int y) { 24 | return x + y; 25 | } 26 | 27 | int main() { 28 | int a = 10; 29 | int b = 20; 30 | int ret = sum(a, b); // Convert into: int ret = a + b; 31 | return 0; 32 | } 33 | ``` 34 | 35 | Since there's no function call, there's no need to generate function symbols inside the symbol table. To verify this, we can compile this code with O2 compiler optimization as ```g++ -c main.cpp -O2``` and dump the object file with ```objdump -t main.o```. 36 | 37 | ```bash 38 | main.o: file format elf64-x86-64 39 | 40 | SYMBOL TABLE: 41 | 0000000000000000 l df *ABS* 0000000000000000 main.cpp 42 | 0000000000000000 l d .text 0000000000000000 .text 43 | 0000000000000000 l d .data 0000000000000000 .data 44 | 0000000000000000 l d .bss 0000000000000000 .bss 45 | 0000000000000000 l d .text.startup 0000000000000000 .text.startup 46 | 0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 47 | 0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 48 | 0000000000000000 l d .comment 0000000000000000 .comment 49 | 0000000000000000 g F .text.startup 0000000000000003 main 50 | ``` 51 | 52 | However, not every function marked with `inline` will be converted into inline function. A recursive function, for example, which calls itself constantly, can not be inlined because the times it is called is only known at runtime. Moreover, if the function has too many lines, it may also not be inlined. 53 | 54 | Therefore, the keyword `inline` is just a suggestion for the compiler to make it inlined. It is up to the compiler to decide whether make it inlined or not. 55 | 56 | In VS Code, `inline` only works in the release version. The debug version makes it unavailable in order to facilitate the debugging of the program. 57 | 58 | -------------------------------------------------------------------------------- /Chapter 2/New-and-Delete.md: -------------------------------------------------------------------------------- 1 | # `new` and `delete` 2 | 3 | There are two ways to create memory on heap. One original approach in C style is to use the library functions `malloc` and `free`: 4 | 5 | ```cpp 6 | int main() { 7 | int *p = (int*)malloc(sizeof(int)); 8 | if (p == nullptr) { 9 | return -1; 10 | } 11 | *p = 20; 12 | free(p); 13 | return 0; 14 | } 15 | ``` 16 | 17 | Notice that `malloc` returns a type of *void \**, so we always need to cast it into the correct pointer type. What's more, we need to pass the size of allocated memory as the function argument. If the allocation fails, the function will return a NULL pointer, so we also need to check the validity of our pointer after the function call. 18 | 19 | In C++, the memory allocation on heap can be done in a cleverer way: using operator **`new`** and **`delete`**. 20 | 21 | ```cpp 22 | int *p = new int(20); 23 | delete p; 24 | ``` 25 | 26 | When using `new`, we no longer need to calculate the memory size nor cast the pointer type, thus things get much easier. Notice that `new` can also initialize the memory bucket after allocating it, just like the brackets we use in the code above. If the memory allocation fails, a *bad_alloc* exception is raised, we can then handle it with a try-catch structure. 27 | 28 | When it's up to arrays, we can also allocate memory for it in a easy way. For `new`, we need to use square brackets, just like the way we declare an array on the stack. And for `delete`, we also need to add a pair of square brackets after it. `delete[]` will then loop through the array and operate `delete` to every element in the array. 29 | 30 | Besides the conventional way to use `new`, there is also several other ways to use `new`. We can tell the program not to throw an exception if the allocation fails by stating *(nothrow)* after `new`. 31 | 32 | ```cpp 33 | int *p2 = new (nothrow) int; // No exception 34 | ``` 35 | 36 | We can also assign constant memory on the heap with `const` keyword. In this way, the return pointer should be modified with `const` as well. 37 | 38 | ```cpp 39 | const int *p3 = new const int(40); // Constant memory 40 | ``` 41 | 42 | Now all the memory address on the heap is allocated by the system. What if we want to allocate the memory on a specific location? We need to state the address of the destination after `new`, with brackets. 43 | 44 | ```cpp 45 | int data = 0; 46 | int *p4 = new (&data) int(50); // Memory allocated on &data 47 | cout << data << endl; // 50 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /Chapter 2/References-in-Detail.md: -------------------------------------------------------------------------------- 1 | # References in Detail 2 | 3 | ## References and Pointers 4 | 5 | A variable can be declared as reference with an *&* in the declaration. When a variable is declared as reference, it is an alias of an existing variable. Alias means that a variable and its reference can be regarded as the same thing. 6 | 7 | ```cpp 8 | int main() { 9 | int a = 10; 10 | int *p = &a; 11 | int &b = a; 12 | cout << b << endl; // 10 13 | return 0; 14 | } 15 | ``` 16 | 17 | Declaring a pointer variable and a reference variable has the same underlying instructions. Both two approaches get the memory address of the existing variable and stores into the declared variable. Similarly, the assembly commands for modifying the value of a memory address through reference or through pointer dereference, are also the same. 18 | 19 | ```cpp 20 | *p = 20; 21 | cout << a << " " << *p << " " << b << endl; // 20 20 20 22 | b = 30; 23 | cout << a << " " << *p << " " << b << endl; // 30 30 30 24 | ``` 25 | 26 | Unlike pointer which can be declared without initialization or as a *nullptr*, a reference variable must be initialized with an existing variable when it is declared. In this way, a reference is safer than a pointer since it can't be NULL. 27 | 28 | ```cpp 29 | int &q; // ERROR 30 | ``` 31 | 32 | Besides, these is no multi-level references as pointer does. 33 | 34 | Reference plays an important role, especially as function parameters. In the following case, we write a *swap* function which takes two arguments and swap them. We can make this in using pointers as parameters and pass addresses of arguments inside, but reference makes it in a easier way. 35 | 36 | ```cpp 37 | int swap(int &x, int &y) { 38 | int temp = x; 39 | x = y; 40 | y = temp; 41 | } 42 | 43 | int main() { 44 | int a = 10; 45 | int b = 20; 46 | swap(a, b); 47 | cout << "a: " << a << "b: " << b << endl; 48 | } 49 | ``` 50 | 51 | The way we declare reference to an array in kind tricky. We need to add brackets outside the reference variable, which states the priority, and then states the array size with square brackets. It is similar to declaring an array instead of declaring a pointer, since reference is the alias of another variable. 52 | 53 | ```cpp 54 | int main() { 55 | int array[5] = {}; 56 | int *p = array; 57 | int (&q)[5] = array; 58 | 59 | cout << sizeof(array) << endl; // 20 60 | cout << sizeof(p) << endl; // 4 61 | cout << sizeof(q) << endl; // 20 62 | return 0; 63 | } 64 | ``` 65 | 66 | In the above case, the pointer has a size of 4 bytes, and reference has a size of 20 bytes, which is the same as the original array. 67 | 68 | ## Rvalue References 69 | 70 | All the cases we've seen now is references of Lvalues. A Lvalue is a type of value that can appears at the left of operator `=`. It has a variable name and a memory address that can be modified. On the contrary, a Rvalue doesn't have a variable name nor a memory address. It can only appears at the right of operator `=`. We cannot declare a normal reference of a Rvalue. 71 | 72 | ```cpp 73 | int main() { 74 | int a = 10; // a is a Lvalue 75 | int &b = a; 76 | int &c = 20; // ERROR: 20 a Rvalue 77 | return 0; 78 | } 79 | ``` 80 | 81 | C++11 introduced many features, one of them is Rvalue references. With two *&* symbols, we can declare a reference of a constant. 82 | 83 | ```cpp 84 | int &&c = 20; 85 | ``` 86 | 87 | But how can we make a reference to a Rvalue since it doesn't have a memory address? The assembly commands shows the magic. When declaring a Rvalue reference, the compiler creates a temporary variable to store the value of the constant, and then assign the address of that temporary variable to the reference variable. 88 | 89 | ```assembly 90 | mov dword ptr [ebp-30h],14h 91 | lea eax,[ebp-30h] 92 | mov dword ptr [c],eax 93 | ``` 94 | 95 | Alternatively, we can use `const` to declare a Rvalue reference as well. 96 | 97 | ```cpp 98 | const int &c = 20; 99 | ``` 100 | 101 | Notice that a Rvalue reference is a Lvalue itself, which means that only Lvalue references can be used to refer to it. 102 | 103 | ```cpp 104 | int &d = c; 105 | int &&e = c; // ERROR 106 | ``` 107 | 108 | ## `const`, Pointers and References 109 | 110 | Now let's take a look at the following code. Is this code able to be complied or not? 111 | 112 | ```cpp 113 | int main() { 114 | int a = 10; 115 | int *p = &a; 116 | const int *&q = p; 117 | return 0; 118 | } 119 | ``` 120 | 121 | It seems confusing, but we can convert the reference to a pointer to make it clearer. Notice that `int &a = b` has the same function as `int *p = &b`, so the code above can be converted into follows: 122 | 123 | ```cpp 124 | int a = 10; 125 | int *p = &a; 126 | const int **q = &p; // ERROR 127 | ``` 128 | 129 | Now the problem is clear since we already discuss about `const` and double pointers. The code above actually convert a type of *int \*\** into *const int \*\**, which is invalid. 130 | 131 | > See the post "`const` and Pointers" if you forget it. 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /Chapter 3/Class-and-Object.md: -------------------------------------------------------------------------------- 1 | # Class and Object 2 | 3 | OOP (Object-Oriented Programming) is almost the most important programming paradigm nowadays. The following code is a class *Goods* which represents goods from a grocery. 4 | 5 | ```cpp 6 | class Goods { 7 | public: // Methods 8 | // Initialize goods information 9 | void init(const char *name, double price, int amount); 10 | // Print goods information 11 | void show(); 12 | // Getters 13 | void setName(const char *name) {strcpy(_name, name);} 14 | void setPrice(double price) {_price = price;}; 15 | void setAmount(int amount) {_amount = amount;}; 16 | // Setters 17 | const char* getName() {return _name;}; 18 | double getPrice() {return _price;}; 19 | int getAmount() {return _amount;}; 20 | private: // Variables 21 | char _name[20]; 22 | double _price; 23 | int _amount; 24 | }; 25 | ``` 26 | 27 | Class is an abstract blueprint of an entity. An entity has its attributes and behaviors, and a class is a composition of member variables and member methods. With a class as an abstract, we can instantiate objects. These objects all follows a similar pattern, with same types of attributes and behaviors. 28 | 29 | Object-oriented programming has four basic features: abstract, encapsulation, inherence and Polymorphism. Access qualifiers enables the encapsulation of a class. There are three types of access qualifiers: `public`, `private` and `protected`. Generally, member variables are declared a private, and public methods a provided to access them, known as Getters and Setters. These methods are usually defined inside the class, and is compiled as inline functions. 30 | 31 | When declaring methods outside the class, we need to add scope before the function name. 32 | 33 | ```cpp 34 | void Goods::init(const char *name, double price, int amount) { 35 | strcpy(_name, name); 36 | _price = price; 37 | _amount = amount; 38 | } 39 | 40 | void Goods::show() { 41 | cout << "name: " << _name << endl; 42 | cout << "price: " << _price << endl; 43 | cout << "amount: " << _amount << endl; 44 | } 45 | ``` 46 | 47 | A number of objects can be defined with a class. Each object has their own member variables, but the member methods are shared among them. Then how does the methods know which one should be dealt with? In fact while compiling, a pointer **this* which refers to the object itself is added into the parameter list of member methods, and when the methods are called, the memory address of the object is passed as argument. 48 | 49 | ```cpp 50 | int main() { 51 | Goods good; 52 | good.init("Bread", 10.0, 200); // init(&good, "Bread", 10.0, 200); 53 | good.show(); // good.show(&good); 54 | } 55 | ``` 56 | 57 | What is the size of our Goods class? Notice that the memory size of an object is only related to its member variables. The class above has an *char* array with length 20, a *double* and an *int*, so the size of this class is 32. 58 | 59 | But that's not correct! The actual size of Goods is 40. Why's that? It is because of the memory alignment mechanism in memory allocation. The size of each variable should be aligned to the largest type size in all variables. In this case the largest type is *double*, which occupies 8 bytes. So all variables should be a multiple of 8. Then the *char* array is aligned to 24 bytes, and the *int* variable is aligned to 8 bytes. 60 | 61 | > There are other factors that change the size of a class, we will cover those later. -------------------------------------------------------------------------------- /Chapter 3/Constructor-and-Destructor.md: -------------------------------------------------------------------------------- 1 | # Constructor and Destructor 2 | 3 | Let's look at the following code. Here we use object-oriented programming to implement a data structure *stack*. We use a dynamic array to store elements, and use member methods to imitate stack push and pop. The stack is also able to resize if it is full. 4 | 5 | ```cpp 6 | class MyStack { 7 | public: 8 | void init() { 9 | _pstack = new int[size]; 10 | _top = -1; 11 | _size = size; 12 | } 13 | void push(int val) { 14 | if (full()) 15 | resize(); 16 | _pstack[++_top] = val; 17 | } 18 | void pop() { 19 | if (empty()) 20 | return; 21 | --_top; 22 | } 23 | int top() { 24 | return _pstack[_top]; 25 | } 26 | bool empty() { return _top == -1; } 27 | bool full() { return _top == _size - 1; } 28 | void release() 29 | { 30 | delete[]_pstack; 31 | _pstack = nullptr; 32 | } 33 | private: 34 | int *_pstack; 35 | int _top; 36 | int _size; 37 | 38 | void resize() { 39 | int *ptmp = new int[_size * 2]; 40 | for (int i = 0; i < _size; ++i) { 41 | ptmp[i] = _pstack[i]; 42 | } 43 | delete[]_pstack; 44 | _pstack = ptmp; 45 | _size *= 2; 46 | } 47 | }; 48 | ``` 49 | 50 | If we want to use our own stack, we need to initialize it with *init()* and release the memory with *release()* explicitly. If the user forget to do so, it will either fail to allocate memory or cause a memory leak. 51 | 52 | ```cpp 53 | int main() { 54 | MyStack s; 55 | s.init(5); // Initialize memory 56 | for (int i =0; i < 15; i++) { 57 | s.push(rand() % 100); 58 | } 59 | while(!s.empty()) { 60 | cout << s.top() << endl; 61 | s.pop(); 62 | } 63 | s.release(); // Release memory 64 | } 65 | ``` 66 | 67 | Fortunately, the class provides us a **constructor** and **destructor**. The former is called once an object is created, and the latter is called once the object is destroyed. The constructor and destructor has the same name as the class (destructor has a tilde at the front), and there's no return value. Thus, we can rewrite *init()* and *release()* as 68 | 69 | ```cpp 70 | MyStack(int size = 10) { 71 | _pstack = new int[size]; 72 | _top = -1; 73 | _size = size; 74 | } 75 | ~MyStack() { 76 | delete[]_pstack; 77 | _pstack = nullptr; 78 | } 79 | ``` 80 | 81 | Every time an object is declared, the memory is allocated first, and then the constructor is called. When to call the destructor depends on the lifecycle of the object. A global object is destructed when the program exits. The object on function stack is destructed when the function returns. It follows FILO: Objects which construct first destruct last. The object on the heap has too be released with `delete` manually. When an object is deleted, it first calls its destructor, and then free the memory with `free`. 82 | 83 | Notice that the constructor can be overloaded. Several constructors can be defined, and the compiler will automatically calls the right one when the object is created. If no constructors are defined, the compiler will generate a empty constructor with no parameters. But destructors do not have parameters, so there is only one destructor in each object. 84 | 85 | -------------------------------------------------------------------------------- /Chapter 3/Initializer-List.md: -------------------------------------------------------------------------------- 1 | # Initializer List 2 | 3 | Now let's go back to the *Goods* class we wrote before. Suppose we want to add a production date to the goods, with a *Data* class: 4 | 5 | ```cpp 6 | class Date { 7 | public: 8 | Date(int y, int m, int d) { 9 | _year = y; 10 | _month = m; 11 | _date = d; 12 | } 13 | 14 | void show() { 15 | cout << _year << "/" << _month << "/" << _day << endl; 16 | } 17 | 18 | private: 19 | int _yead; 20 | int _month; 21 | int _date; 22 | } 23 | ``` 24 | 25 | Then we can make the data as one of the member attributes: 26 | 27 | ```cpp 28 | class Goods { 29 | ... 30 | private: 31 | char _name[20]; 32 | double _price; 33 | int _amount; 34 | Date _date; 35 | } 36 | ``` 37 | 38 | But an error raises: *Date* does not have a default constructor. Remember that the declaration of an object includes both memory allocation and construction. We have already defined a customized constructor in *Date*, so there is no default constructor. But the constructor requires three parameters: year, month and date, which is provided when a *Goods* object is created. How can we pass them inside? A **initializer list** is used in initializing data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon: 39 | 40 | ```cpp 41 | Goods::Goods(const char *name, double price, int amount, int y, int m, int d) 42 | : _date(y, m, d) { 43 | strcpy(_name, name); 44 | _price = price; 45 | _amount = amount; 46 | } 47 | ``` 48 | 49 | In this way, *y*, *m* and *d* can be passed inside the constructor of *Date*. Not only can objects be initialized in this way, we can also use initializer list to initialize a normal member variable: 50 | 51 | ```cpp 52 | Goods::Goods(const char *name, double price, int amount, int y, int m, int d) 53 | : _date(y, m, d), _price(price), _amount(amount) { 54 | strcpy(_name, name); 55 | } 56 | ``` 57 | 58 | Notice that this initialization simply assigns values to variables, so *_name* has to be initialized inside the constructor with *strcpy()*. 59 | 60 | What's the initialization sequence of the initializer list? Does it follows the order from left to right? Let's look at this case. 61 | 62 | ```cpp 63 | class Test { 64 | public: 65 | Test(int data = 10) : mb(_data), ma(mb) {} 66 | void show() {cout << "ma: " << ma << " mb " << mb << endl;} 67 | private: 68 | int ma; 69 | int mb; 70 | }; 71 | 72 | int main() { 73 | Test t; 74 | t.show(); // ma: -858993460 mb: 10 75 | return 0; 76 | } 77 | ``` 78 | 79 | When *t.show()* is called, we find that *mb* is 10, but *ma* is a weird number. This is actually the default initial value in Visual Studio, which means that *ma* is not initialized correctly with *mb*. In fact, the initialization order only depends on the order we declared the variables, and has nothing to do with the initializer list. In the above case, we declared *ma* before *mb*, so *ma* is initialized first. 80 | 81 | -------------------------------------------------------------------------------- /Chapter 3/Pointer-to-Class-Members.md: -------------------------------------------------------------------------------- 1 | # Pointer to Class Members 2 | 3 | ## Pointer to Member Variables 4 | 5 | We all know that a pointer can used to represent the memory address of a variable. But what if the variable is inside a class? Let' look at this case. 6 | 7 | ```cpp 8 | class Test { 9 | public: 10 | int ma; 11 | static int mb; 12 | }; 13 | 14 | int Test::mb; 15 | ``` 16 | 17 | Now if we want to access the memory of *Test::ma*, we will get an error: cannot convert ‘int Test::\*’ to ‘int\*’ in initialization. 18 | 19 | ```cpp 20 | int main() { 21 | int *p = &Test::ma; // ERROR 22 | *p = 20; 23 | return 0; 24 | } 25 | ``` 26 | 27 | This is because *ma* is within the scope of class *Test*, so the type of our pointer should also be modified with the qualifier. But another error occurs: invalid use of unary ‘*’ on pointer to member. 28 | 29 | ```cpp 30 | int main() { 31 | int Test::*p = &Test::ma; 32 | *p = 20; // ERROR 33 | return 0; 34 | } 35 | ``` 36 | 37 | Remember that class is an abstract of instances, so the member variable is only valid within an object. And the following code is valid when we bound the pointer with an object. 38 | 39 | ```cpp 40 | int main() { 41 | Test *t = new Test(); 42 | int Test::*p = &Test::ma; 43 | t->*p = 20; 44 | cout << t->ma << endl; // 20 45 | delete t; 46 | return 0; 47 | } 48 | ``` 49 | 50 | Notice that static member variables belong to the class instead of objects. So it is correct to access them with pointers, just like normal variables. 51 | 52 | ```cpp 53 | int main() { 54 | int *p = &Test::mb; 55 | *p = 30; 56 | cout << t->Test::mb << endl; // 30 57 | return 0; 58 | } 59 | ``` 60 | 61 | ## Pointer to Member Functions 62 | 63 | In C and C++, we can have pointers to functions. This feature enables programmers to avoid code redundancy. Similarly, we can use pointers to access public member functions. 64 | 65 | ```cpp 66 | class Test { 67 | public: 68 | void func() { 69 | cout << "call Test::func" << endl; 70 | } 71 | static void static_func() { 72 | cout << "call Test::static_func" << endl; 73 | } 74 | }; 75 | ``` 76 | 77 | Like member variables, the pointer is within the scope of the class, and has to be bounded with an object. 78 | 79 | ```cpp 80 | int main() { 81 | Test *t = new Test(); 82 | void (Test::*pfunc)() = &Test::func; 83 | (t->*pfunc)(); // call Test::func 84 | delete t; 85 | return 0; 86 | } 87 | ``` 88 | 89 | Like static variables, static member functions can be pointed use normal function pointers. 90 | 91 | ```cpp 92 | int main() { 93 | void (*pfunc)() = &Test::static_func; 94 | (*pfunc)(); // call Test::static_func 95 | return 0; 96 | } 97 | ``` 98 | 99 | ## More About Function Pointers 100 | 101 | There may be ambiguity in using function pointers. In the following case all three statements can be compiled. 102 | 103 | ```cpp 104 | void foo() {...} 105 | void bar() { 106 | void (*p1)() = foo; // 1 107 | void (*p2)() = &foo; // 2 108 | void (*p3)() = *foo; // 3 109 | } 110 | ``` 111 | 112 | But when it comes to member functions, only the second one can be compiled correctly. 113 | 114 | ```cpp 115 | class Test { 116 | public: 117 | void foo() {...} 118 | }; 119 | void bar() { 120 | void (Test::*p1)() = Test::foo; // 1 121 | void (Test::*p2)() = &Test::foo; // 2 122 | void (Test::*p3)() = *Test::foo; // 3 123 | } 124 | ``` 125 | 126 | The underlying principle is complicated, including implicit type conversion and polymorphism of member functions. Notice that C++ is a language with long history, and there are many historical issues. We don't have to, and can not understand all of them. In the above case, we only need to remember that *&* with function name is always a correct way in using function pointers. 127 | 128 | > For more explanations, please refer to C++ standard n3376 - 4.3 - 1 and n3337 - 5.3.1 - 4. -------------------------------------------------------------------------------- /Chapter 3/Shallow-Copy-and-Deep-Copy.md: -------------------------------------------------------------------------------- 1 | # Shallow Copy and Deep Copy 2 | 3 | Now let's continue with our MyStack class. Suppose we create a MyStack object s1, and use it to create two other stacks s2 and s3, with either operator `=` or brackets. 4 | 5 | ```cpp 6 | int main() { 7 | MyStack s1(10); 8 | MyStack s2 = s1; 9 | MyStack s3(s1); 10 | return 0; 11 | } 12 | ``` 13 | 14 | By doing this, the compiler will create a **copy constructor** for us, which simply copy the memory space of s1 to s2 and s3. But the program crashes while running, the following image explains why. 15 | 16 | ![Shallow Copy](../assets/shallow-copy.png) 17 | 18 | 19 | 20 | Here in s1 we have a member variable **_pstack* which points to an array we created on the heap. By copying the memory space of s1, s2 has an identical pointer *\*_pstack* which points to the same array. When the function returns, it first calls s2's destructor, which destroys the array object. But when the destructor of s1 is called, the memory is already destroyed, so a illegal memory access happened. 21 | 22 | The behavior of copying the memory space is called a **shallow copy**. In some cases a shallow copy is safe and sufficient, but when the object occupies external resources, the shallow copy will cause problems. 23 | 24 | To fix this, we may use a **deep copy** instead. To achieve this, we can define the copy constructor by our own: 25 | 26 | ```cpp 27 | MyStack(const MyStack &other) { 28 | _pstack = new int[other._size]; 29 | for (int i = 0; i < _size; i++) { 30 | _pstack[i] = other._pstack[i]; 31 | } 32 | _top = other._top; 33 | _size = other._size; 34 | } 35 | ``` 36 | 37 | The copy constructor has the same name as the class and no return value. It takes a reference to an object of this class as parameter. In the code above, we allocate new memory for the array, and manually copy all the element into it. 38 | 39 | Now suppose we create two stacks, s1 and s2 with the constructor. And we want to override s2 with s1, with the operator `=`. 40 | 41 | ```cpp 42 | int main() { 43 | MyStack s1(10); 44 | MyStack s2(10); 45 | s2 = s1; 46 | return 0; 47 | } 48 | ``` 49 | 50 | The default behavior of such assignment is also a shallow copy, which causes problem in releasing memory. And in addition, it will also cause a memory leak, because s2's array pointer loses its control of the array on the heap. 51 | 52 | We can also define the operator `=` in the way we want, which is called an **operator override**. 53 | 54 | ```cpp 55 | MyStack& operator=(const MyStack &other) { 56 | if (this == &other) 57 | return *this; 58 | // 1. Release current memory 59 | delete[] _pstack; 60 | // 2. Allocate new memory 61 | _pstack = new int[other._size]; 62 | for (int i = 0; i < _size; i++) { 63 | _pstack[i] = other._pstack[i]; 64 | } 65 | _top = other._top; 66 | _size = other._size; 67 | 68 | return *this; 69 | } 70 | ``` 71 | 72 | The function name of an operator override is the keyword `operator` plus the symbol. It also takes a reference to an object of this class as parameter. The return value can be *void*, but in most cases we return a reference to the current object **this*. In this way we can achieve the relay of assignments, for example `s1 = s2 = s3`. 73 | 74 | We need to do two things in the override of operator `=`. First, we need to release the memory of the current object. But what if we assign an object to itself? Then its memory is released and can not go on. In preventing this, we need to check if the object on the right side is the current object. Then we can do the deep copy as before. 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Chapter 3/Various-Member-Functions.md: -------------------------------------------------------------------------------- 1 | # Various Member Functions 2 | 3 | We already know the properties of member functions. Member functions belong to the object of a class, and can only be called by that object. 4 | 5 | Now back to our *Goods* class, we want to count the total amount of goods in a grocery. How can we do that? We need a variable which belongs to the class instead of its objects. This kind of variable is called a **static member variable**. 6 | 7 | ```cpp 8 | class Goods { 9 | ... 10 | private: 11 | char _name[20]; 12 | double _price; 13 | int _amount; 14 | Date _date; 15 | static int _count; 16 | } 17 | 18 | int Goods::_count = 0; 19 | 20 | Goods::Goods(const char *name, double price, int amount, int y, int m, int d) 21 | : _date(y, m, d), _price(price), _amount(amount) { 22 | strcpy(_name, name); 23 | _count++; 24 | } 25 | ``` 26 | 27 | A static member variable is declared inside the class with keyword `static`, and has to be defined outside the scope of the class, which is similar to a static variable. A static member variable doesn't belong to any objects. Instead, it belongs to the class. Since the variable is private, we need another public method to access it. Also, we want this function to be managed by the class itself instead of any instances. This kind of function is a **static member function**. 28 | 29 | ## Static Member Function 30 | 31 | We can define our static member function *showCounts()* like this. 32 | 33 | ```cpp 34 | static void Goods::showCounts() { 35 | cout << "Counts: " << _count << endl; 36 | } 37 | ``` 38 | 39 | Then we can call this function directly inside our main function, without instantiating any objects. We need to add a scope qualifier before it. 40 | 41 | ```cpp 42 | int main() { 43 | ... 44 | Goods::showCounts(); 45 | return 0; 46 | } 47 | ``` 48 | 49 | The main difference between a normal member function and a static one is that the normal method has a pointer **this* as the parameter (which is added by the compiler), and a static method doesn't. Therefore, we can not access any normal member variables because there's no actual object. We can only access static variables. 50 | 51 | ## Constant Member Function 52 | 53 | Now look at this case. We create a constant object of class *Goods*, and call its *show()* method. But this raises an error. 54 | 55 | ```cpp 56 | int main() { 57 | const Goods goods(...); 58 | goods.show() // ERROR 59 | } 60 | ``` 61 | 62 | It is because **this* pointer of *goods* has type *const Goods \**, but the parameters in *show()* has type *Goods \**, so it is invalid to convert a `const` pointer to a normal one. 63 | 64 | To fix this, we can have a **constant member function** with keyword `const` after the function name. 65 | 66 | ```cpp 67 | void Goods::show() const { 68 | cout << "name: " << _name << endl; 69 | cout << "price: " << _price << endl; 70 | cout << "amount: " << _amount << endl; 71 | _date.show(); 72 | }; 73 | ``` 74 | 75 | Notice that *Goods::show()* calls *Date::show()*, sow *Date::show()* has to be modified with `const` as well. 76 | 77 | ```cpp 78 | void show() const { 79 | cout << _year << "/" << _month << "/" << _day << endl; 80 | } 81 | ``` 82 | 83 | Generally speaking, as long as the member function is read-only, we need to add the `const` qualifier, so that it can be called by both normal objects and constant objects. 84 | 85 | ## At Last 86 | 87 | Now we understand three types of member functions. 88 | 89 | **Member Function** 90 | 91 | - Within the scope of class 92 | - Called by an object 93 | 94 | **Static Member Function** 95 | 96 | - Within the scope of class 97 | - Called with scope qualifier 98 | - Can only access static member variables 99 | 100 | **Constant Member Function** 101 | 102 | - Within the scope of class 103 | - Called by an object 104 | - Read-only 105 | 106 | Remember that the essential difference between them is the type of **this* pointer. -------------------------------------------------------------------------------- /Chapter 4/Class-Templates.md: -------------------------------------------------------------------------------- 1 | # Class Templates 2 | 3 | Templates are also widely used for classes. For example, the Standard Template Library (STL) is a set of *C++* template classes to provide common programming data structures and functions. 4 | 5 | The syntax of template classes is similar to template functions, with keyword `template` and the template parameters before the class name. Since that the class name of a template class is composed of the template name and the template parameter, wherever a template class appears should the parameters be added as well. For convenience, constructors and destructors can omit template parameters. 6 | 7 | A template version of our *MyStack* class is shown as follow: 8 | 9 | ```cpp 10 | template 11 | class MyStack { 12 | public: 13 | MyStack(int size = 10); 14 | ~MyStack(); 15 | MyStack(const MyStack &other); 16 | MyStack& operator=(const MyStack &other); 17 | void push(const T &val); 18 | void pop(); 19 | T top() const; 20 | bool full() const; 21 | bool empty() const; 22 | private: 23 | T *_pstack; 24 | int _top; 25 | int _size; 26 | void resize(); 27 | }; 28 | ``` 29 | 30 | The keyword `template` only play a role within the scope of the class. So if we want to implement member functions outside the class, keyword `template` should be reused again. 31 | 32 | ```cpp 33 | template 34 | void MyStack::push(const T &val) { 35 | if (full()) resize(); 36 | _pstack[_top++] = val; 37 | } 38 | ``` 39 | 40 | Then we can use our self-defined fancy stack in the main function. When the template class is used, the compiler instantiate a copy of it with the type we choose. But unlike template functions, this instantiation is selective, which means only those methods being called are instantiated. This approach decreases the amount of the code text. 41 | 42 | ```cpp 43 | int main() { 44 | MyStack s; 45 | s.push(20); 46 | s.push(30); 47 | s.push(40); 48 | s.pop(); 49 | cout << s.top() << endl; // 30 50 | return 0; 51 | } 52 | ``` 53 | 54 | Templates can also have default parameters as well. In the following case, if we don't specify the type when using *MyStack*, the type name is *int* by default. 55 | 56 | ```cpp 57 | template 58 | class MyStack { 59 | ... 60 | }; 61 | 62 | int main() { 63 | MyStack<> s; 64 | return 0; 65 | } 66 | ``` 67 | 68 | The complete code of *MyStack* is as follows. 69 | 70 | ```cpp 71 | template 72 | class MyStack { 73 | public: 74 | MyStack(int size = 10) : _pstack(new T[size]), _top(0), _size(size) {} 75 | 76 | ~MyStack() { 77 | delete[] _pstack; 78 | _pstack = nullptr; 79 | } 80 | 81 | MyStack(const MyStack &other) : _top(other.top), _size(other._size) { 82 | _pstack = new T[_size]; 83 | for (int i = 0; i < _top; i++) { 84 | _pstack[i] = other._pstack[i]; 85 | } 86 | } 87 | 88 | MyStack &operator=(const MyStack &other) { 89 | if (*this == other) return *this; 90 | delete[] _pstack; 91 | _top = other.top; 92 | _size = other._size; 93 | _pstack = new T[_size]; 94 | for (int i = 0; i < _top; i++) { 95 | _pstack[i] = other._pstack[i]; 96 | } 97 | return *this; 98 | } 99 | 100 | void push(const T &val) { 101 | if (full()) resize(); 102 | _pstack[_top++] = val; 103 | } 104 | 105 | void pop() { 106 | if (!empty()) --_top; 107 | } 108 | 109 | T top() const { return _pstack[_top - 1]; } 110 | 111 | bool full() const { return _top == _size; } 112 | 113 | bool empty() const { return _top == 0; } 114 | 115 | private: 116 | T *_pstack; 117 | int _top; 118 | int _size; 119 | 120 | void resize() { 121 | T *tmp = new T[_size * 2]; 122 | for (int i = 0; i < _top; i++) { 123 | tmp[i] = _pstack[i]; 124 | } 125 | delete[] _pstack; 126 | _pstack = tmp; 127 | _size *= 2; 128 | } 129 | }; 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /Chapter 4/Function-Templates.md: -------------------------------------------------------------------------------- 1 | # Function Templates 2 | 3 | **Function templates** are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type without repeating the entire code for each type. An basic example shows as follow: 4 | 5 | ```cpp 6 | template 7 | bool compare(T a, T b) { 8 | return a > b; 9 | } 10 | ``` 11 | 12 | Now in we can call this function by specifying type name *T* within angle brackets. 13 | 14 | ```cpp 15 | int main() { 16 | compare(10, 12); 17 | compare(10.5, 20.5); 18 | return 0; 19 | } 20 | ``` 21 | 22 | When a template function is called, the compiler instantiates the code of the function by replacing type names with the types specified by programmers. Function template itself can not be compiled. It is only compiled after being instantiated at function call points. 23 | 24 | Sometimes we don't need to explicitly state the types with brackets, the compiler can deduce the template parameter types from the argument types. This is called the **template argument deduction**. But sometimes there are problems. In the following case, the two arguments are *int* and *double* respectively. But there is only one type for the template parameter. In this case a compile error arises. 25 | 26 | ```cpp 27 | int main() { 28 | compare(10, 12); 29 | compare(10, 20.5); // ERROR 30 | return 0; 31 | } 32 | ``` 33 | 34 | In some cases, the default instantiation cause problems. The code below compares two strings. Remember that string type is converted to *const char \** implicitly. So our *compare()* function simply compares the address of these two strings, which is obviously incorrect. 35 | 36 | ```cpp 37 | int main() { 38 | compare("aaa", "bbb"); 39 | return 0; 40 | } 41 | ``` 42 | 43 | **Template specialization** provides methods for programmers to handle special cases. By explicitly indicates the parameter types, we can use *strcmp()* to compare two strings. 44 | 45 | ```cpp 46 | template<> 47 | bool compare(const char *a, const char *b) { 48 | return strcmp(a, b) > 0; 49 | } 50 | ``` 51 | 52 | Another way to do that is to defined a non-template function, then call the function as normal. 53 | 54 | ```cpp 55 | bool compare(const char *a, const char *b) { 56 | return strcmp(a, b) > 0; 57 | } 58 | ``` 59 | 60 | Then if we call `compare("aaa", "bbb")`, the compiler will not use the template function any more. That is because the argument deduction has priority. The compiler first searches for normal functions, and then the specialized template function. If nothing suitable is found, a template function is instantiated. 61 | 62 | Moreover, template functions can have non-type parameters as will. In the following case, we specify the second parameter of our *sort()* function as an *int* variable. Then in the main function, we can use the template function by passing the size inside, along with the type name. 63 | 64 | ```cpp 65 | template 66 | void sort(T *arr) { 67 | for (int i = 0; i < SIZE - 1; i++) { 68 | for (int j = 0; i < SIZE - i - 1; j++) { 69 | if (arr[j] > arr[j+1]) { 70 | int tmp = arr[j]; 71 | arr[j] = arr[j+1]; 72 | arr[j+1] = tmp; 73 | } 74 | } 75 | } 76 | } 77 | 78 | int main() { 79 | int arr[] = {12, 5, 7, 89, 32, 21, 35}; 80 | const int size = sizeof(arr) / sizeof(arr[0]); 81 | sort(arr); 82 | for (int val : arr) { 83 | cout << val << " "; // 5 7 12 21 32 35 89 84 | } 85 | return 0; 86 | } 87 | ``` 88 | 89 | Non-type parameters of templates have to be a constant, which means it is either an immediate number or an address. 90 | 91 | Since a template function can only be compiled with instantiation at the call point, the compiler needs to see the specific implementation of the template. That is, we cannot put the declaration and definition into two separate files. In practice, it is common to put template codes in a single *.hpp* header file, and include the file where we use it. -------------------------------------------------------------------------------- /Chapter 5/Introduction-to-Iterators.md: -------------------------------------------------------------------------------- 1 | # Introduction to Iterators 2 | 3 | Using what we have learnt about operator overloading, we write a *MyString* class. This class resembles *std::string*, which maintains a *char \** type pointer, and implement basic string indexing, comparison, and concatenation. 4 | 5 | ```cpp 6 | #include 7 | #include 8 | 9 | class MyString { 10 | public: 11 | MyString(const char *p = nullptr) { 12 | if (p != nullptr) { 13 | _pstr = new char[strlen(p) + 1]; 14 | strcpy(_pstr, p); 15 | } else { 16 | _pstr = new char[1]; 17 | *_pstr = '\0'; 18 | } 19 | } 20 | 21 | ~MyString() { 22 | delete _pstr; 23 | _pstr = nullptr; 24 | } 25 | 26 | MyString(const MyString &other) { 27 | _pstr = new char[strlen(other._pstr) + 1]; 28 | strcpy(_pstr, other._pstr); 29 | } 30 | 31 | MyString &operator=(const MyString &other) { 32 | if (this == &other) return *this; 33 | delete[] _pstr; 34 | _pstr = new char[strlen(other._pstr) + 1]; 35 | strcpy(_pstr, other._pstr); 36 | return *this; 37 | } 38 | 39 | bool operator>(const MyString &other) const { 40 | return strcmp(_pstr, other._pstr) > 0; 41 | } 42 | 43 | bool operator<(const MyString &other) const { 44 | return strcmp(_pstr, other._pstr) < 0; 45 | } 46 | 47 | bool operator==(const MyString &other) const { 48 | return strcmp(_pstr, other._pstr) == 0; 49 | } 50 | 51 | int length() const { return strlen(_pstr); } 52 | 53 | char &operator[](int index) { return _pstr[index]; } 54 | 55 | const char &operator[](int index) const { return _pstr[index]; } 56 | 57 | const char *c_str() const { return _pstr; } 58 | 59 | private: 60 | char *_pstr; 61 | friend std::ostream &operator<<(std::ostream &out, const MyString s); 62 | friend MyString operator+(const MyString &s1, const MyString &s2); 63 | }; 64 | 65 | MyString operator+(const MyString &s1, const MyString &s2) { 66 | MyString tmp; 67 | tmp._pstr = new char[strlen(s1._pstr) + strlen(s2._pstr) + 1]; 68 | strcpy(tmp._pstr, s1._pstr); 69 | strcat(tmp._pstr, s2._pstr); 70 | return s; 71 | } 72 | 73 | std::ostream &operator<<(std::ostream &out, const MyString s) { 74 | return out << s._pstr; 75 | } 76 | ``` 77 | 78 | We can also regard our string class as a container, which contains a series of characters. However, there is another important feature not realized yet, which is the iteration of containers. 79 | 80 | There are many types of containers in C++ Standard Template Libraries whose underlying data structures are different. These data structures are private members and are not exposed to users directly. **Iterators** are pointers pointing to elements inside a container. We can use iterators to move through the contents of the container, without knowing the specific ways of memory storage. In other word, iterators are abstract interfaces that give users transparent access to elements inside the container. Iterators are widely used in C++ generic algorithms. These algorithms need to traverse the container in a uniform way, therefore taking container iterators as parameters. 81 | 82 | Iterator is implemented as a nested class of the container, and four methods need to be defined. *begin()* returns the iterator pointing to the first element inside the container. *end()* returns the iterator pointing to the latter of the last element inside the container. *iterator::operator++()* moves to the next element. *iterator::operator!=()* determines whether two iterators are equal. And *iterator::operator*()* returns the dereference of an iterator, which is the element itself. 83 | 84 | ```cpp 85 | class MyString { 86 | ... 87 | class iterator { 88 | public: 89 | iterator(char *p = nullptr) : _p(p) {} 90 | 91 | bool operator!=(const iterator &other) { return _p != other._p; } 92 | 93 | void operator++() { ++_p; } 94 | 95 | char &operator*() { return *_p; } 96 | 97 | private: 98 | char *_p; 99 | }; 100 | 101 | iterator begin() { 102 | return iterator(_pstr); 103 | } 104 | 105 | iterator end() { 106 | return iterator(_pstr + length()); 107 | } 108 | } 109 | ``` 110 | 111 | With iterators, we can traverse *MyString* in such way: 112 | 113 | ```cpp 114 | int main() { 115 | MyString s = "1234567890"; 116 | for (MyString::iterator it = s.begin(); it != s.end(); ++it) { 117 | cout << *it << " "; // 1 2 3 4 5 6 7 8 9 0 118 | } 119 | return 0; 120 | } 121 | ``` 122 | 123 | C++ 11 provides a convenient feature "for each", which can directly loop the elements without the process of dereference. 124 | 125 | ```cpp 126 | int main() { 127 | MyString s = "1234567890"; 128 | for (char a : s) { 129 | cout << a << " "; // 1 2 3 4 5 6 7 8 9 0 130 | } 131 | return 0; 132 | } 133 | ``` 134 | 135 | The underlying implementation of this approach is still an iterator. -------------------------------------------------------------------------------- /Chapter 5/More-about-new-and-delete.md: -------------------------------------------------------------------------------- 1 | # More about `new` and `delete` 2 | 3 | `new` and `delete` are a couple of keywords in C++ for memory arrangement. Meanwhile, they are operators that can be overloaded as well. When `new` is used, the compiler will first calls function *operator new()* to allocate memory on heap, and then calls the corresponding constructor of the object. When `delete` is used, the compiler first calls the destructor, and then calls function *operator delete()* to deallocate the memory. Those are same for `new[]` and `delete[]`, which is used for array elements. 4 | 5 | The underlying implementations of operators `new` and `delete` are standard C functions *malloc()* and *free()*, which shows as follow. The former takes size of the object as parameter, and the latter takes the pointer of the object to be deleted as parameter. 6 | 7 | ```cpp 8 | void *operator new(size_t size) { 9 | void *p = malloc(size); 10 | if (p == nullptr) 11 | throw bad_alloc(); 12 | return p; 13 | } 14 | 15 | void operator delete(void *p) { 16 | free(p); 17 | } 18 | ``` 19 | 20 | Knowing this, we can overload them using our own-defined memory management functions. With overloading, we may track the allocated memory, for more precise and detailed memory management. It is widely used in a memory pool, or in checking memory leaks. 21 | 22 | We already know that `delete` is used to free a single object, and `delete[]` is used to free an array object. But can these two be mixed used? Well, it depends. 23 | 24 | In the following example, it is okay to use either of them. *int* is a built-in type instead of an object, so there's no constructor to be called. In this case, `delete` and `delete[]` are the same since their underlying implementations are the same. 25 | 26 | ```cpp 27 | int main() { 28 | int *p = new int; 29 | delete[] p; 30 | int *q = new int[10]; 31 | delete q; 32 | return 0; 33 | } 34 | ``` 35 | 36 | However, in the next example, incorrect use of these two will make the program crash: 37 | 38 | ```cpp 39 | class Test { 40 | public: 41 | Test() {...} 42 | ~Test() {...} 43 | private: 44 | int a; 45 | }; 46 | 47 | int main() { 48 | Test *t1 = new Test(); 49 | delete[] t1; // ERROR 50 | Test *t2 = new Test[5]; 51 | delete t2; // ERROR 52 | return 0; 53 | } 54 | ``` 55 | 56 | It is because that objects need to be destroyed before deallocation. When `new[]` is used to create array objects, not only the memory space for these objects is allocated, an extra 4-byte space for recording the counts of objects is allocated as well. When `delete[]` is used, it first reads the counts *n* from it, then divides the memory space into *n* portions. In this way, the destructor of each array object can be called correctly. Unlike `delete[]`, `delete` simply treats the whole memory space as a single object and frees it. 57 | 58 | Therefore, if `delete[]` is used for a single object, it cannot find the count information. On the other hand, if `delete` is used for an array object, the freed memory has a 4-byte offset, and the destructors are not called properly as well. -------------------------------------------------------------------------------- /Chapter 5/Operator-Overloading.md: -------------------------------------------------------------------------------- 1 | # Operator Overloading 2 | 3 | Operator overloading is one of the most fancy feature in C++. It is is a specific case of polymorphism, where different operators can be overloaded so that object operations are the same as the built-in types of the compiler, which greatly facilitate programmers. 4 | 5 | Here we defined a class *MyComplex* which represents a complex number. The complex number has a real part *mreal* and an image part *mimage*. 6 | 7 | ```cpp 8 | class MyComplex { 9 | public: 10 | MyComplex(int r = 0, int i = 0) : mreal(r), mimage(i) {} 11 | 12 | private: 13 | int mreal; 14 | int mimage; 15 | }; 16 | 17 | int main() { 18 | MyComplex c1(10, 10); 19 | MyComplex c2(20, 20); 20 | MyComplex c3 = c1 + c2; // c1.operator+(c2) 21 | return 0; 22 | } 23 | ``` 24 | 25 | Now in the main function we would like to add two *MyComplex* objects. For object types, `a + b` is just `a.operator+(b)`, so we need to overload operator `+`, which simply add the real part and image part of two complex numbers separately. 26 | 27 | ```cpp 28 | MyComplex MyComplex::operator+(const MyComplex &other) { 29 | return MyComplex(this->mreal + other.mreal, this->mimage + other.mimage); 30 | } 31 | ``` 32 | 33 | It is also valid to add *MyComplex* with a number. In the following case, number 20 is converted into a temporary object *c(20, 0)* when passed inside. 34 | 35 | ```cpp 36 | int main() { 37 | MyComplex c1(10, 10); 38 | MyComplex c2 = c1 + 20; // c1.operator+(20) 39 | return 0; 40 | } 41 | ``` 42 | 43 | But it won't work if the constant is before the operator `+`. Since 20 is a constant, there's not any evidence for the compiler to call the overloaded function of *MyComplex*. In this case, the compiler will call the global operator `+`, so we also need to overload it. 44 | 45 | ```cpp 46 | int main() { 47 | MyComplex c1(10, 10); 48 | MyComplex c2 = 20 + c1; // ::operator+(20, c1) 49 | return 0; 50 | } 51 | ``` 52 | 53 | The global operator takes the object before it and after it as parameters. When the compiler does object operations, it will first call the overloaded function of the member method. If it is not found, the compiler will go on finding the appropriate overloaded function in the global scope. 54 | 55 | ```cpp 56 | MyComplex operator+(const MyComplex &c1, const MyComplex &c2) { 57 | return MyComplex(c1.mreal + c2.mreal, c1.mimage + c2.mimage); 58 | } 59 | ``` 60 | 61 | Here inside this function we use the private member variables of *MyComplex*, which is apparently not allowed. To fix this, we can use keyword `friend` inside *MyComplex*, to give the function access to private members. 62 | 63 | ```cpp 64 | class MyComplex { 65 | ... 66 | private: 67 | int mreal; 68 | int mimage; 69 | friend MyComplex operator+(const MyComplex &c1, const MyComplex &c2); 70 | } 71 | ``` 72 | 73 | Now our global overloaded function can also handle the sum of two *MyComplex* objects, so the overloaded member function is no longer needed. 74 | 75 | Now let's deal with the self growth operator `++`. There are two types of them, one is before the object and the other is after the object. The former adds one to the object, and returns its value, while the latter returns the original value, and then adds one. 76 | 77 | ```cpp 78 | int main() { 79 | MyComplex c1(10, 10); 80 | MyComplex c2 = c1++; // c1.operator++(int) 81 | MyComplex c3 = ++c1; // c1.operator++() 82 | return 0; 83 | } 84 | ``` 85 | 86 | The one before the object is represented as `a.operator++()`, and the one after the object is represented as `a.operator++(int)`. The *int* parameter is not used. It is solely for the compiler to distinguish them. 87 | 88 | ```cpp 89 | MyComplex MyComplex::operator++(int) { 90 | return MyComplex(mreal++, mimage++); 91 | } 92 | 93 | MyComplex &MyComplex::operator++() { 94 | mreal += 1; 95 | mimage += 1; 96 | return *this; 97 | } 98 | ``` 99 | 100 | We can also overload operator `+=`, which is simple in this case: 101 | 102 | ```cpp 103 | void MyComplex::operator+=(const MyComplex &other) { 104 | mreal += other.mreal; 105 | mimage += other.mimage; 106 | } 107 | ``` 108 | 109 | We can also overload the output stream operator `<<`. It is also a global function, which takes the *ostream* object as the first parameter, and our *MyComplex* object as the second. Similarly, it should also be defined as friend inside *MyComplex*. 110 | 111 | ```cpp 112 | std::ostream &operator<<(std::ostream &out, const MyComplex &c) { 113 | return out << c.mreal << "+" << c.mimage << "i"; 114 | } 115 | ``` 116 | 117 | Now we can use output stream to print our self-defined complex class. 118 | 119 | ```cpp 120 | int main() { 121 | MyComplex c1(10, 10); 122 | MyComplex c2(20, 20); 123 | std::cout << c1 << " " << c2 << std::endl; // 10+10i 20+20i 124 | return 0; 125 | } 126 | ``` 127 | 128 | The input stream is similar: 129 | 130 | ```cpp 131 | std::istream &operator>>(std::istream &in, MyComplex &c) { 132 | return in >> c.mreal >> c.mimage; 133 | } 134 | ``` 135 | 136 | -------------------------------------------------------------------------------- /Chapter 5/Overloading-of-new-and-delete-Object-Pool.md: -------------------------------------------------------------------------------- 1 | # Overloading of `new` and `delete`: Object Pool 2 | 3 | We have already written an own-defined queue data structure *MyQueue* before, which maintains a dynamic array as its private member. This time, we are going to rewrite *MyQueue*, using linked list as its underlying implementation. 4 | 5 | ```cpp 6 | template 7 | class MyQueue { 8 | public: 9 | MyQueue() { _front = _rear = new QueueItem(); } 10 | 11 | ~MyQueue() { 12 | QueueItem *cur = _front; 13 | while (cur != nullptr) { 14 | _front = _front->_next; 15 | delete cur; 16 | cur = _front; 17 | } 18 | } 19 | 20 | void push(const T &val) { 21 | QueueItem *item = new QueueItem(val); 22 | _rear->_next = item; 23 | _rear = item; 24 | } 25 | 26 | void pop() { 27 | if (empty()) return; 28 | QueueItem *first = _front->_next; 29 | _front->_next = first->_next; 30 | if (_front->_next == nullptr) { 31 | _rear = _front; 32 | } 33 | delete first; 34 | } 35 | 36 | T front() const { return _front->_next->_data; } 37 | 38 | bool empty() const { return _front == _rear; } 39 | 40 | private: 41 | struct QueueItem { 42 | QueueItem(T data = T()) : _data(data), _next(nullptr) {} 43 | T _data; 44 | QueueItem *_next; 45 | }; 46 | 47 | QueueItem *_front; 48 | QueueItem *_rear; 49 | }; 50 | ``` 51 | 52 | Here we use a nested struct *QueueItem* as the list node, and keep tracking the front and rear of our linked list. Now suppose we are using our new queue in this way: 53 | 54 | ```cpp 55 | int main() { 56 | MyQueue q; 57 | for (int i = 0; i < 10000; i++) { 58 | q.push(i); 59 | q.pop(); 60 | } 61 | return 0; 62 | } 63 | ``` 64 | 65 | Here we keep pushing and poping items from the queue for ten thousand times. In our current implementation, each time an element is pushed or popped, a new memory space for *QueueItem* is allocated and deallocated on the heap. When these operations are very frequent, the memory allocation and deallocation is consuming. To improve this, we can reuse the list nodes we'v already created. 66 | 67 | An **object pool** is a good approach in cases that a large number of objecs are constructed and destroyed in a short time. There are other kinds of pools like thread pool or connection pool, which share the same idea of resource reuse. 68 | 69 | The main idea of object pool is to create a pool of objects at once. When an object is created, we get an empty object from the pool. When an object is destroyed, we return it back to the pool. Here we use a static linked list of 10000 *QueueItem*s whose memory space is continuous. A pointer *_pool* points to the first empty element in the pool. Then we can overload the member function *operator new()* and *operator delete()* of *QueueItem*. 70 | 71 | When a new *QueueItem* is created, we simply return *_pool* to it, and move *_pool* to the next empty element. When a *QueueItem* is deleted, we do not deallocate the memory. Instead, we change its next element into *_pool*, and move *_pool* onto it. In this way, the object is returned back to the pool. If the pool is full, another pool is opened up. 72 | 73 | ```cpp 74 | struct QueueItem { 75 | QueueItem(T data = T()) : _data(data), _next(nullptr) {} 76 | 77 | void *operator new(size_t size) { 78 | if (_pool == nullptr) { 79 | _pool = (QueueItem *)new char[POOL_ITEM_SIZE * sizeof(QueueItem)]; 80 | QueueItem *p = _pool; 81 | for (; p < _pool + POOL_ITEM_SIZE; ++p) { 82 | p->_next = p + 1; 83 | } 84 | p->_next = nullptr; 85 | } 86 | QueueItem *p = _pool; 87 | _pool = _pool->_next; 88 | return p; 89 | } 90 | 91 | void operator delete(void *ptr) { 92 | QueueItem *p = (QueueItem *)ptr; 93 | p->_next = _pool; 94 | _pool = p; 95 | } 96 | 97 | T _data; 98 | QueueItem *_next; 99 | static const int POOL_ITEM_SIZE = 10000; 100 | static QueueItem *_pool; 101 | }; 102 | 103 | template 104 | typename MyQueue::QueueItem *MyQueue::QueueItem::_pool = nullptr; 105 | ``` 106 | 107 | Here we haven't implemented the memory deallocation of the object pool, since it's not straightforward here. Using smart pointers is a clever way to do so, which we will introduce in the future. -------------------------------------------------------------------------------- /Chapter 6/Abstract-Classes.md: -------------------------------------------------------------------------------- 1 | # Abstract Classes 2 | 3 | Let's take another look at what we wrote earlier: 4 | 5 | ```cpp 6 | class Animal { 7 | public: 8 | Animal(string name) : _name(name) {} 9 | virtual void bark() {} 10 | protected: 11 | string _name; 12 | }; 13 | 14 | class Cat : public Animal { 15 | public: 16 | Cat(string name) : Animal(name) {} 17 | void bark() { 18 | cout << _name << "Meow!" << endl; 19 | } 20 | }; 21 | 22 | class Dog : public Animal { 23 | public: 24 | Dog(string name) : Animal(name) {} 25 | void bark() { 26 | cout << _name << "Woof!" << endl; 27 | } 28 | }; 29 | 30 | class Bear : public Animal { 31 | public: 32 | Bear(string name) : Animal(name) {} 33 | void bark() { 34 | cout << _name << "Rua!" << endl; 35 | } 36 | }; 37 | ``` 38 | 39 | Remember that the original intention of defining class *Animal* is not to make *Animal* an abstract of some entities, but to: 40 | 41 | 1. Let all derived classes inherit the member variables of *Animal* to reuse its properties. 42 | 2. Keep a unified interface for all derived classes to override it to achieve polymorphism. 43 | 44 | Hence, we can defined *Animal* as an **abstract class**: 45 | 46 | ```cpp 47 | class Animal { 48 | public: 49 | Animal(string name) : _name(name) {} 50 | virtual void bark() = 0; 51 | protected: 52 | string _name; 53 | }; 54 | ``` 55 | 56 | Here we declared a virtual function *bark()*, but assign its address to 0. In this case, *bark()* is a **pure virtual function**. A class with pure virtual functions is called an abstract class. An abstract class can not be instantiated, but can have pointers and references: 57 | 58 | ``` cpp 59 | int main() { 60 | Animal a; // No 61 | Animal *pa; // Yes 62 | return 0; 63 | } 64 | ``` 65 | 66 | Let's look at another example. Here we defined an abstract class *Car*, and has a method which returns remaining miles under the current fuel level. 67 | 68 | ```cpp 69 | class Car { 70 | public: 71 | Car(string name, double oil) : _name(name), _oil(oil) {} 72 | double getLeftMiles(double oil) { 73 | return oil * getMilesPerGallon(); 74 | } 75 | protected: 76 | string _name; 77 | double _oil; 78 | virtual double getMilesPerGallon() = 0; 79 | }; 80 | ``` 81 | 82 | Then we defined three cars of different brands, with different mileages per gallon. 83 | 84 | ```cpp 85 | class Benz : public Car { 86 | public: 87 | Benz(string name, double oil) : Car(name, oil) {} 88 | double getMilesPerGallon() {return 20.0;} 89 | }; 90 | 91 | class Audi : public Car { 92 | public: 93 | Audi(string name, double oil) : Car(name, oil) {} 94 | double getMilesPerGallon() {return 18.0;} 95 | }; 96 | 97 | class BMW : public Car { 98 | public: 99 | BMW(string name, double oil) : Car(name, oil) {} 100 | double getMilesPerGallon() {return 19.0;} 101 | }; 102 | ``` 103 | 104 | Moreover, we provide an interface to get the remaining miles of the car, and passes in different objects: 105 | 106 | ```cpp 107 | double showCarLeftMiles(Car &car) { 108 | cout << car.getName() << " " << car.getLeftMiles() << endl; 109 | } 110 | 111 | int main() { 112 | Benz a("Benz", 20.0); 113 | Audi b("Audi", 20.0); 114 | BMW c("BWM", 20.0); 115 | showCarLeftMiles(a); // Benz 400 116 | showCarLeftMiles(b); // Audi 360 117 | showCarLeftMiles(c); // BMW 380 118 | return 0; 119 | } 120 | ``` 121 | 122 | Now in *showCarLeftMiles()*, the parameter is a *Car* object instead of a pointer, isn't it a static binding? How can we achieve polymorphism? Of course it is a static binding here, but notice that in *Car::getLeftMiles()*, the virtual function *getMilesPerGallon()* is called with *this->getMilesPerGallon()*. Since **this* is a *Car* pointer, it is still a dynamic binding, so the corresponding method of different objects is called. 123 | 124 | -------------------------------------------------------------------------------- /Chapter 6/Look-inside-Inheritance.md: -------------------------------------------------------------------------------- 1 | # Look inside Inheritance 2 | 3 | **Inheritance** is the core of object-oriented programming. The essence of inheritance is code reuse. Let's take a look at the following example: 4 | 5 | ```cpp 6 | class A { 7 | public: 8 | int ma; 9 | protected: 10 | int mb; 11 | private: 12 | int mc; 13 | }; 14 | 15 | class B : public A { 16 | public: 17 | int md; 18 | protected: 19 | int me; 20 | private: 21 | int mf; 22 | } 23 | ``` 24 | 25 | *A* is called a **base class**, and *B* is a **derived class**. Besides *B*'s own member variables *md*, *me* and *mf*, it also inherit *ma* and *mb* from *A*. Now if *B* has its own member variable *ma*, it does not conflict with *ma* from *A*, since their scopes are different. 26 | 27 | We should be able to tell the differences between combination and inheritance. The former can be described as class *B* is **a past of** class *A*, while the latter is described as class *B* is **a kind of** class *A*. For example, *vehicle* and *wheels* belongs to combination relationship, while *vehicle* and *truck* is an inheritance relationship. 28 | 29 | ### Access Limitations of Different Inheritance Types 30 | 31 | There are three types of inheritance: public, protected and private. There differences is the access authority of base class's members, which is listed as follow: 32 | 33 | #### Public Inheritance 34 | 35 | | Base Class | Derived Class | External Access | 36 | | :--------: | :-----------: | :-------------: | 37 | | public | public | Yes | 38 | | protected | protected | No | 39 | | private | Inaccessible | No | 40 | 41 | #### Protected Inheritance 42 | 43 | | Base Class | Derived Class | External Access | 44 | | :--------: | :-----------: | :-------------: | 45 | | public | public | No | 46 | | protected | protected | No | 47 | | private | Inaccessible | No | 48 | 49 | #### Private Inheritance 50 | 51 | | Base Class | Derived Class | External Access | 52 | | :--------: | :-----------: | :-------------: | 53 | | public | private | No | 54 | | protected | private | No | 55 | | private | Inaccessible | No | 56 | 57 | From this, we can conclude the law of access limitations with different inheritance types: 58 | 59 | 1. The access limit of the derived class cannot exceed the access limit of the base class. 60 | 2. Only public members can be accessed externally. 61 | 3. Derived class can inherent base class's private members, but can not access it directly. 62 | 4. Members defined as protected in the base class can be access by its derived classes, but cannot be accessed externally. 63 | 64 | The default inheritance type is same as the default access limitation of members. In classes, the default inheritance is private, while in structs it is public. 65 | 66 | ### Construction and Destruction of the Derived Class 67 | 68 | In the following example, we create a base class and another class inherited from it. 69 | 70 | ```cpp 71 | class Base{ 72 | public: 73 | Base(int data) : ma(data) { 74 | cout << "Base()" << endl; 75 | } 76 | ~Base() { 77 | cout << "~Base()" << endl; 78 | } 79 | protected: 80 | int ma; 81 | }; 82 | 83 | class Derived : public Base { 84 | public: 85 | Derived(int data) : Base(data), mb(data) { 86 | cout << "Derived()" << endl; 87 | } 88 | ~Derived() { 89 | cout << "~Derived()" << endl; 90 | } 91 | private: 92 | int mb; 93 | }; 94 | 95 | int main() { 96 | Derived a(10); 97 | return 0; 98 | } 99 | ``` 100 | 101 | Notice that a derived classes cannot directly initialize member variables inherited from the base class. In this case we need to call the corresponding constructor of the base class. The constructor and destructor of the derived class is only responsible for the members of the derived class, and the members of the base class is handled by the constructor and destructor of the base class. The program above has the following output: 102 | 103 | ``` 104 | Base() 105 | Derived() 106 | ~Derived() 107 | Base() 108 | ``` 109 | 110 | That is, the construction order and destruction order of a derived class is: 111 | 112 | 1. The derived class calls the constructor of the base class to initialize the members inherited from the base class 113 | 2. Then the derived class's own constructor is called to initialize its own members 114 | 3. The destructor of the derived class is called to release the external resources of the derived class members 115 | 4. Then the destructor of the base class is called to release the external resources of the members inherited from the base class 116 | 117 | -------------------------------------------------------------------------------- /Chapter 6/More-about-Inheritance.md: -------------------------------------------------------------------------------- 1 | # More about Inheritance 2 | 3 | ## Function Hiding 4 | 5 | In the following example, class *Base* has two overloaded member functions *show()* and *show(int)*. Class *Derived* inherit from *Base*, and has a member function *show()*. 6 | 7 | ```cpp 8 | class Base { 9 | public: 10 | Base(int data = 10) : ma(data) {} 11 | void show() { 12 | cout << "Base::show()" << endl; 13 | } 14 | void show(int) { 15 | cout << "Base::show(int)" << endl; 16 | } 17 | protected: 18 | int ma; 19 | }; 20 | 21 | class Derived : public Base { 22 | public: 23 | Derived(int data = 20) : Base(data), mb(data) {} 24 | void show() { 25 | cout << "Derived::show()" << endl; 26 | } 27 | private: 28 | int mb; 29 | }; 30 | ``` 31 | 32 | Now in the main function we defined an object of class *Derived*. But when the function *show(int)* is called, an error occurs. Since *Derived* inherits *show(int)* from *Base*, why can't the compiler find it? 33 | 34 | ```cpp 35 | int main() { 36 | Derived d; 37 | d.show(); // Derived::show() 38 | d.show(10); // ERROR 39 | return 0; 40 | } 41 | ``` 42 | 43 | Notice that when a member function is called, the compiler first search for it inside the derived class itself. Only if no definition is found within this scope does the compiler goes inside the base class. In this case, function *Derived::show()* hides the functions with the same name *Base::show()* and *Base::show(int)*, which is called **function hiding**. To fix this, we may use `d.Base::show(10)` to call the corresponding function within the scope of *Base*. 44 | 45 | Notice that function hiding refers to the hiding of functions with the same name defined **in different scopes**, which is very different from function overloading. Recall that the definition of **function overloading** is that, there are functions **in the same scope** whose names are the same, but their parameter lists are different. In the example above, *Base::show()* and *Base::show(int)* are a couple of overloaded functions. 46 | 47 | ## Conversions of Base and Derived 48 | 49 | Can a base class and a derived class be converted into each other? Let's see the following example: 50 | 51 | ```cpp 52 | int main() { 53 | Base b(10); 54 | Derived d(20); 55 | b = d; // Yes 56 | d = b; // No 57 | return 0; 58 | } 59 | ``` 60 | 61 | Here we defined a *Base* object and a *Derived* object. Assume we have overloaded operator `=`, then it is valid to assign *Derived* to *Base*, but invalid to assign *Base* to *Derived*. 62 | 63 | Object pointers are similar: 64 | 65 | ```cpp 66 | int main() { 67 | Base b(10); 68 | Derived d(20); 69 | Base *pb = &d; // Yes 70 | pb->show(); // Base::show() 71 | Derived *pd = &b; // No 72 | return 0; 73 | } 74 | ``` 75 | 76 | Here we can let a *Base* pointer pointing to a *Derived* object, but not vice versa. Moreover, if we use a *Base* pointer which points to a *Derived* object, we can only access the members of *Base*. It is able to compile if we force a *Derived* pointer to point to a *Base* object using type conversion `Derived *pd = (Derived *)&b`, but it is unsafe, since there is not actually a *Derived* object, which may involve illegal memory access. 77 | 78 | In all, in inheritance, only type conversion from bottom (base class) to top (derived class) is supported. Since derived class is a type of base class, which means that a base class is more generic, it make sense if we use something generic to point to something specific, but not the other way around. -------------------------------------------------------------------------------- /Chapter 6/More-about-Virtual-Functions.md: -------------------------------------------------------------------------------- 1 | # More about Virtual Functions 2 | 3 | ## More about `virtual` 4 | 5 | Now we already learn the ability of `virtual`. But is there any function that cannot be implemented as virtual function? Notice that a virtual function must meet two conditions: 6 | 7 | 1. It has an address and can be stored in the virtual function table. 8 | 2. Its existence depends on the object. 9 | 10 | Obviously, the constructor can not be virtual, since no object exists before its constructor is called. In this case, any function called in the constructor is a static binding. Moreover, a static member function can not be virtual, since it belongs to the class itself instead of its objects. 11 | 12 | Is there any function that should always be virtual? Let's look at the following case: 13 | 14 | ```cpp 15 | class Base{ 16 | public: 17 | Base(int data) : ma(data) { 18 | cout << "Base()" << endl; 19 | } 20 | ~Base() { 21 | cout << "~Base()" << endl; 22 | } 23 | protected: 24 | int ma; 25 | }; 26 | 27 | class Derived : public Base { 28 | public: 29 | Derived(int data) : Base(data), mb(data) { 30 | cout << "Derived()" << endl; 31 | } 32 | ~Derived() { 33 | cout << "~Derived()" << endl; 34 | } 35 | private: 36 | int mb; 37 | }; 38 | ``` 39 | 40 | Now we use a *Base* pointer to point to a *Derived* object, and delete it after then. 41 | 42 | ```cpp 43 | int main() { 44 | Base *pb = new Derived(10); 45 | delete pb; 46 | return 0; 47 | } 48 | ``` 49 | 50 | If we look at the output, we may find that only the destructor for class *Base* is called. The destructor of class *Derived* is not called, which will probably cause memory leak. 51 | 52 | ``` 53 | Base() 54 | Derive() 55 | ~Base() 56 | ``` 57 | 58 | In the example above, the destructor of the base class is not marked as `virtual`, so there is a static binding. Good programming practice is to always marked the destructor of a base class as `virtual`, in which case the destructor of the derived class becomes virtual automatically. 59 | 60 | ## More about Dynamic Binding 61 | 62 | There is another question: is the call of a virtual function is always a dynamic binding? We already know that any function call in a constructor is static binding, even if the constructor is marked as `virtual`. Let's look at more examples. 63 | 64 | ```cpp 65 | class Base { 66 | public: 67 | Base(int data = 10) : ma(data) {} 68 | virtual void show() { 69 | cout << "Base::show()" << endl; 70 | } 71 | protected: 72 | int ma; 73 | }; 74 | 75 | class Derived : public Base { 76 | public: 77 | Derive(int data = 20) : Base(data), mb(data) {} 78 | void show() { 79 | cout << "Derived::show()" << endl; 80 | } 81 | private: 82 | int mb; 83 | } 84 | ``` 85 | 86 | Here we create two objects, and call the *show()* method directly with the object. In this case, the function calls are static binding. 87 | 88 | ```cpp 89 | int main() { 90 | Base b; 91 | Derived d; 92 | b.show(); // Base::show() 93 | d.show(); // Derived::show() 94 | return 0; 95 | } 96 | ``` 97 | 98 | Now if we use a *Base* pointer to point to objects, it is a dynamic binding, no matter the object we point at is a *Base* object or a *Derived* object. 99 | 100 | ```cpp 101 | int main() { 102 | Base b; 103 | Derived d; 104 | Base *pb1 = &b; 105 | pb1->show(); // Base::show() 106 | Base *pb2 = &d; 107 | pb2->show(); // Derived::show() 108 | return 0; 109 | } 110 | ``` 111 | 112 | References have the same underlying implementation as pointers, so it is also a dynamic binding. 113 | 114 | ```cpp 115 | int main() { 116 | Base b; 117 | Derived d; 118 | Base &rb1 = b; 119 | rb1.show(); // Base::show() 120 | Base &rb2 = d; 121 | rb2.show(); // Derived::show() 122 | return 0; 123 | } 124 | ``` 125 | 126 | Now we know that dynamic binding is a way to achieve polymorphism. Therefore, only with pointers and references is the function call a dynamic binding. 127 | 128 | -------------------------------------------------------------------------------- /Chapter 6/Understanding-Polymorphism.md: -------------------------------------------------------------------------------- 1 | # Understanding Polymorphism 2 | 3 | We already know that polymorphism is a key feature of Object-Oriented Programming, but what exactly is polymorphism? Why polymorphism is important? 4 | 5 | There are two kinds of polymorphism: **static polymorphism** and **dynamic polymorphism**. We have already learned function overloading and templates, which are two kinds of static polymorphism, for their status is determined under compilations. 6 | 7 | On the other hand, dynamic polymorphism refers to the polymorphism under runtime. It happens when we use a base class pointer to point to a derived class object, and call the member functions through the pointer. In this case, to which derived class object the pointer points to will its corresponding overridden method be called. The bottom layer of dynamic polymorphism is realized through dynamic binding. 8 | 9 | To illustrate the necessity of polymorphism, let's look at the following example. Here we defined a base class *Animal*, which has a member variable *_name* and a member function *bark()*. Then we defined three derived classes *Cat*, *Dog* and *Bear* which inherit from *Animal*. 10 | 11 | ```cpp 12 | class Animal { 13 | public: 14 | Animal(string name) : _name(name) {} 15 | virtual void bark() {} 16 | protected: 17 | string _name; 18 | }; 19 | 20 | class Cat : public Animal { 21 | public: 22 | Cat(string name) : Animal(name) {} 23 | void bark() { 24 | cout << _name << "Meow!" << endl; 25 | } 26 | }; 27 | 28 | class Dog : public Animal { 29 | public: 30 | Dog(string name) : Animal(name) {} 31 | void bark() { 32 | cout << _name << "Woof!" << endl; 33 | } 34 | }; 35 | 36 | class Bear : public Animal { 37 | public: 38 | Bear(string name) : Animal(name) {} 39 | void bark() { 40 | cout << _name << "Rua!" << endl; 41 | } 42 | }; 43 | ``` 44 | 45 | Now in the the main function, we defined three objects of *Cat*, *Dog* and *Bear* respectively. What's more, we want to provide an interface *bark()* to call the member function *bark()* of these animals. In this case, we have to defined three different functions which accept different types of objects as parameters. Apparently, this approach is redundant, and is not conductive to code modification and maintenance, for every time we defined a new class, we need to add a corresponding interface. 46 | 47 | ```cpp 48 | void bark(Cat &cat) { 49 | cat.bark(); 50 | } 51 | 52 | void bark(Dog &dog) { 53 | dog.bark(); 54 | } 55 | 56 | void bark(Bear &bear) { 57 | bear.bark(); 58 | } 59 | 60 | int main() { 61 | Cat cat("Kitty"); 62 | Dog dog("Puppy"); 63 | Bear bear("Winnie"); 64 | bark(cat); 65 | bark(dog); 66 | bark(bear); 67 | return 0; 68 | } 69 | ``` 70 | 71 | In OOP, a good software development principle is the **open-closed principle**, which states that *software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification*. In polymorphism, open-closed principle refer to the use of abstracted interfaces, where the implementations can be changed and multiple implementations could be created and polymorphically substituted for each other. Therefore, we can rewrite our interface as: 72 | 73 | ```cpp 74 | void bark(Animal *p) { 75 | p->bark(); 76 | } 77 | 78 | int main() { 79 | Cat cat("Kitty"); 80 | Dog dog("Puppy"); 81 | Bear bear("Winnie"); 82 | bark(&cat); 83 | bark(&dog); 84 | bark(&bear); 85 | return 0; 86 | } 87 | ``` 88 | 89 | Now we can just pass the addresses of objects inside. With polymorphism, we simply use a *Animal* pointer to call the corresponding method of derived objects. -------------------------------------------------------------------------------- /Chapter 7/Diamond-Problem.md: -------------------------------------------------------------------------------- 1 | # Diamond Problem 2 | 3 | The benefit of multiple inheritance is that more code can be reused in this inheritance structure. But it brings up another problem, which is known as **diamond problem**. Let's look at this example. 4 | 5 | Here we have a base class *A*, and two derived class *B* and *C* that inherit from it. Then we have another class that multiply inherits from *B* and *C*. 6 | 7 | ```cpp 8 | class A { 9 | public: 10 | A(int data) : ma(data) { 11 | cout << "A()" << endl; 12 | } 13 | ~A() { 14 | cout << "~A()" << endl; 15 | } 16 | protected: 17 | int ma; 18 | }; 19 | 20 | class B : public A { 21 | public: 22 | B(int data) : A(data), mb(data) { 23 | cout << "B()" << endl; 24 | } 25 | ~B() { 26 | cout << "~B()" << endl; 27 | } 28 | protected: 29 | int mb; 30 | }; 31 | 32 | class C : public A { 33 | public: 34 | C(int data) : A(data), mc(data) { 35 | cout << "C()" << endl; 36 | } 37 | ~C() { 38 | cout << "~C()" << endl; 39 | } 40 | protected: 41 | int mc; 42 | }; 43 | 44 | class D : public B, public C { 45 | public: 46 | D(int data) : B(data), C(data), md(data) { 47 | cout << "D()" << endl; 48 | } 49 | ~D() { 50 | cout << "~D()" << endl; 51 | } 52 | protected: 53 | int md; 54 | }; 55 | 56 | int main() { 57 | D d(10); 58 | return 0; 59 | } 60 | ``` 61 | 62 | Now in the main function we defined an object of class *D*. The output shows as follow: 63 | 64 | ``` 65 | A() 66 | B() 67 | A() 68 | C() 69 | D() 70 | ~D() 71 | ~C() 72 | ~A() 73 | ~B() 74 | ~A() 75 | ``` 76 | 77 | We can find that the constructor and destructor of class *A* have been called twice. Therefore, there are multiple copies of class *A*'s member *ma* in *D* as well. 78 | 79 | ![diamond problem 2](../assets/diamond-problem-2.png) 80 | 81 | This is apparently a waste of resources, and may cause ambiguity in using the base class's member variables. This problem is called the diamond problem, which happens in two common inheritance structures: 82 | 83 | ![diamond problem](../assets/diamond-problem.png) 84 | 85 | Virtual inheritance is used to solve these kinds of problems in multiple inheritance. Here we can use virtual inheritance in class *B* and *C*. In this case, class *A* is a virtual base class. 86 | 87 | ```cpp 88 | class B : virtual public A { 89 | public: 90 | B(int data) : A(data), mb(data) { 91 | cout << "B()" << endl; 92 | } 93 | ~B() { 94 | cout << "~B()" << endl; 95 | } 96 | protected: 97 | int mb; 98 | }; 99 | 100 | class C : virtual public A { 101 | public: 102 | C(int data) : A(data), mc(data) { 103 | cout << "C()" << endl; 104 | } 105 | ~C() { 106 | cout << "~C()" << endl; 107 | } 108 | protected: 109 | int mc; 110 | }; 111 | ``` 112 | 113 | Remember that in virtual inheritance, the members of the base class is moved to the end of the memory, and their original locations are replaced with a vbptr which points to the vbtable. Now in class *D* we only have one copy of *ma*, and two vbptrs that point to the vbtables for class *B* and class *C*, respectively. There are no more repeated members here. 114 | 115 | ![diamond problem 3](../assets/diamond-problem-3.png) 116 | 117 | Since *ma* is moved to the end of the memory, it is no longer within the scope of *B::* or *C::* anymore. Instead, it is now within the scope of class *D*. Therefore, *ma* is required to be initialized by *D* itself: 118 | 119 | ``` 120 | class D : public B, public C { 121 | public: 122 | D(int data) : A(data), B(data), C(data), md(data) { 123 | cout << "D()" << endl; 124 | } 125 | ~D() { 126 | cout << "~D()" << endl; 127 | } 128 | protected: 129 | int md; 130 | }; 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /Chapter 7/Virtual-Inheritance-and-Virtual-Base-Classes.md: -------------------------------------------------------------------------------- 1 | # Virtual Inheritance and Virtual Base Classes 2 | 3 | Multiple inheritance means that a class can inherit from multiple base classes. **Virtual Inheritance** is a kind of inheritance that is widely used in multiple inheritance, by adding `virtual` before the access modifiers: 4 | 5 | ```cpp 6 | class A { 7 | public: 8 | private: 9 | int ma; 10 | }; 11 | 12 | class B : virtual public A { 13 | public: 14 | private: 15 | int mb; 16 | }; 17 | ``` 18 | 19 | A class that is virtual inherited is a **virtual base class**. In this case, class *A* is a virtual base class. What is the difference between a virtual inheritance and a normal inheritance? The answer is the memory layout. In a normal inheritance, member variables of the base class locate at the front of the memory, and then goes the member variables of the derived class. But in a virtual inheritance, all the member variables of the base class is moved to end of the memory. What's more, the original location of these variables are replaced with a **virtual base pointer** (vbptr). 20 | 21 | ![virtual base class](../assets/virtual-base-class.png) 22 | 23 | Just like vfptr, vbptr points to a **virtual base table** (vbtable). vbtable stores an upward offset which is 0 in this case, and the distance to the member variables of the base class. Here since *A::ma* moves to the end, the distance to its original location (where vbptr locates) is 8. Now class *B* needs an extra 4 bytes to store the vbptr, so its size is 12 bytes comparing to 8 in normal inheritance. 24 | 25 | Now if class *A* has a virtual member function, and *B* virtually inherits from A: 26 | 27 | ```cpp 28 | class A { 29 | public: 30 | virtual void func() { 31 | cout << "A::func()" << endl; 32 | } 33 | private: 34 | int ma; 35 | }; 36 | 37 | class B : virtual public A { 38 | public: 39 | void func() { 40 | cout << "B::func()" << endl; 41 | } 42 | private: 43 | int mb; 44 | }; 45 | ``` 46 | 47 | In the main function, we use an *A* pointer to point to a *B* object on the heap, then calls *func()* with the pointer. The program outputs normally, but you may have an error while deleting *B*. Why's that. 48 | 49 | ```cpp 50 | int main() { 51 | A *p = new B(); 52 | p->func(); // B::func() 53 | delete p; // ERROR 54 | return 0; 55 | } 56 | ``` 57 | 58 | Notice that if we use a base pointer to point to a derived object, the pointer will always point at the start point of the base members. In this case, *\*p* points at the vfptr of *A*. But since it is a virtual inheritance here, all members of *A* is moved to the end, and its original location is replaced with a vbptr. Now what happens when are deleting through *\*p*? 59 | 60 | ![virtual base class 2](../assets/virtual-base-class-2.png) 61 | 62 | It is kinda problematic and hard to figure out if you are using Visual Studio. But fortunately, g++ or clang++ have optimized accordingly and have no problem freeing the memory. 63 | 64 | > Another good reason not to use Windows for development. -------------------------------------------------------------------------------- /Chapter 8/Associative-Containers.md: -------------------------------------------------------------------------------- 1 | # Associative Containers 2 | 3 | There are two kinds of associative containers in STL: unordered associative containers and ordered associative containers. 4 | 5 | ## Unordered Associative Containers 6 | 7 | Unordered associative containers implement unsorted data structures that can be quickly searched. Their underlying data structure are hash tables, which support *O(1)* element access. 8 | 9 | ### *unordered_set*, *unordered_multiset* 10 | 11 | ```cpp 12 | #include 13 | ``` 14 | 15 | Sets are collections of keys, in which *unordered_set* stores unique keys, while keys in *unordered_multiset* can be repeated. 16 | 17 | **Adding:** 18 | 19 | `us.insert(key);` Insert elements into the container. Time complexity is *O(1)*. 20 | 21 | **Inquiry:** 22 | 23 | `unordered_set::iterator` Iterator of the container. 24 | 25 | `us.find(key);` Find element with specific key, and return its iterator. If no key is found, return the *end()* iterator. Time complexity is *O(1)*. 26 | 27 | **Deleting:** 28 | 29 | `us.erase(key);` Delete elements from the container with the key. Time complexity is *O(1)*. 30 | 31 | `us.erase(it);` Delete elements from the container with the iterator. Time complexity is *O(1)*. 32 | 33 | ### *unordered_map*, *unordered_multimap* 34 | 35 | ```cpp 36 | #include 37 | ``` 38 | 39 | Maps are collections of key-value pairs. Keys are unique in *unordered_map*, while keys in *unordered_multimap* can be repeated. 40 | 41 | A *pair* object has two members, *first* and *second*. In map containers, *first* refers to the key, and *second* refers to the value. We can use *make_pair()* to pack key and value into a *pair* object. 42 | 43 | **Adding:** 44 | 45 | `um.insert(makepair(key, value));` Insert pairs into the container. Time complexity is *O(1)*. We can also use `um.insert({key, value});`. 46 | 47 | **Inquiry:** 48 | 49 | `unordered_set::iterator` Iterator of the container. 50 | 51 | `um.find(key);` Find pair with specific key, and return its iterator. If no key is found, return the *end()* iterator. Time complexity is *O(1)*. 52 | 53 | `operator[key]` We can also use operator `[]` to get the value from a key. What needs to be paid attention to is that, if the key doesn't exist, a new pair *{key, value()}* will be inserted into the container, where the default constructor is called for the value. 54 | 55 | **Deleting:** 56 | 57 | `us.erase(key);` Delete pairs from the container with the key. Time complexity is *O(1)*. 58 | 59 | `us.erase(it);` Delete pairs from the container with the iterator. Time complexity is *O(1)*. 60 | 61 | *unordered_set* and *unordered_map* is widely used in checking or eliminating duplicates of massive data because of their excellent performance in accessing elements. In the following example, we use an *unordered_map* to count how many times each number is repeated in 100,000 numbers. 62 | 63 | ```cpp 64 | int main() { 65 | const int ARR_LEN = 100000; 66 | int arr[ARR_LEN] = {0}; 67 | for (int i = 0; i < ARR_LEN; ++i) { 68 | arr[i] = rand() % 10000 + 1; 69 | } 70 | unordered_map dict; 71 | for (int k : arr) { 72 | map[k]++; 73 | } 74 | for (auto it = dict.begin(); it != dict.end(); ++it) { 75 | cout << "key: " << it->first << " count: " << it->second << endl; 76 | } 77 | } 78 | ``` 79 | 80 | Since keys in an *unordered_map* is unique, we use the map to store unique numbers. If we encounter a new number, ` map[k]++` will insert a new key-value pair and increment the count by 1. 81 | 82 | ## Ordered Associative Containers 83 | 84 | ```cpp 85 | #include 86 | #include 87 | ``` 88 | 89 | Associative containers implement sorted data structures. Similar to unordered associative containers, there are also four types of ordered associative containers: *set*, *multiset*, *map* and *multimap*. The difference is that their underlying data structure is a Red-Black Tree, and elements in the container are ordered. Instead of *O(1)* in unordered associative containers, now all the operations have the time complexity of *O(log(n))*. 90 | 91 | ```cpp 92 | int main() { 93 | set s; 94 | for (int i = 0; i < 20; ++i) { 95 | s.insert(rand()%20 + 1); 96 | } 97 | for (int v : s) { 98 | cout << v << " "; // 1 2 3 4 7 8 10 11 13 14 16 17 18 20 99 | } 100 | return 0; 101 | } 102 | ``` 103 | 104 | Since Red-Black Tree needs the elements to be comparable to remain sorted, we need to implement the overloaded function for operator `<` if we want to store custom class objects in the containers. What's more, *map* or *multimap* requires the existence of default constructor, because it needs to be called in operator `[]` to insert a new pair. -------------------------------------------------------------------------------- /Chapter 8/Container-Adaptors.md: -------------------------------------------------------------------------------- 1 | # Container Adaptors 2 | 3 | Container adaptors provide a different interface for sequential containers. They do not have their own data structures. Instead, all their methods are implemented by the dependent containers. An example of a *stack* with *deque* as the default structure shows as follow: 4 | 5 | ```cpp 6 | template> 7 | class Stack { 8 | public: 9 | void push(const T &val) { 10 | con.push_back(val); 11 | } 12 | 13 | void pop() { 14 | con.pop_back(); 15 | } 16 | 17 | T top() const { 18 | return con.back(); 19 | } 20 | private: 21 | Container con; 22 | }; 23 | ``` 24 | 25 | In addition, container adaptors do not have their own iterators as well. 26 | 27 | ## *stack* 28 | 29 | ```cpp 30 | #include 31 | ``` 32 | 33 | *stack* is a FILO (First-In-Last-Out) data structure which adapts a *deque* by default. 34 | 35 | `s.push(20)` Insert element at the top. Time complexity is *O(1)*. 36 | 37 | `s.pop()` Remove the top element. Time complexity is *O(1)*. 38 | 39 | `s.top()` Return the top element. Time complexity is *O(1)*. 40 | 41 | `s.empty()` Check whether the container is empty. 42 | 43 | `s.size()` Return the number of elements in the container. 44 | 45 | ## *queue* 46 | 47 | ```cpp 48 | #include 49 | ``` 50 | 51 | *queue* is a FIFO (First-In-First-Out) data structure which adapts a *deque* by default. 52 | 53 | `q.push(20)` Insert element at the end. Time complexity is *O(1)*. 54 | 55 | `q.pop()` Remove the first element. Time complexity is *O(1)*. 56 | 57 | `q.front()` Return the first element. Time complexity is *O(1)*. 58 | 59 | `q.back()` Return the last element. Time complexity is *O(1)*. 60 | 61 | `q.empty()` Check whether the container is empty. 62 | 63 | `q.size()` Return the number of elements in the container. 64 | 65 | ## *priority_queue* 66 | 67 | ```cpp 68 | #include 69 | ``` 70 | 71 | *priority_queue* is a container that can access queue elements by their priority (larger by default). It adapts a *vector* by default. 72 | 73 | `pq.push(20)` Insert element sorts the container. Time complexity is *O(nlog(n))*. 74 | 75 | `pq.pop()` Remove the top element. Time complexity is *O(1)*. 76 | 77 | `pq.top()` Return the top element. Time complexity is *O(1)*. 78 | 79 | `pq.empty()` Check whether the container is empty. 80 | 81 | `pq.size()` Return the number of elements in the container. 82 | 83 | ## More about Underlying Containers 84 | 85 | **Q:** *stack* and *queue* use *deque* as their default underlying containers. Why don't they use a *vector*? 86 | 87 | **A:** We can answer from the differences between these two data structures. 88 | 89 | 1. The initial memory usage efficiency of *vector* is worse than *deque*, since *deque* allocates more memory when initialized and reduces overhead for expansion. 90 | 91 | 2. *queue* requires insert from the end. These operations are *O(1)* in *deque*, but *O(n)* in *vector*. 92 | 93 | 3. When there are a large amount of elements, *vector* requires large continuous memory, while *deque* only requires segmented memory, thus providing a better memory utilization. 94 | 95 | **Q:** Why does *priority_queue* use a *vector*? 96 | 97 | **A:** The underlying data structure of *priority_queue* is a heap. In a heap structure, we access child nodes with the node index (For example, if a heap starts with 0 and a node has index *i*, then its child nodes are *2i +1* and *2i + 2*). Therefore, heap indexes should be stored in an array of continuous memory. A *vector* makes sure that random access of heap indexes is *O(1)*. -------------------------------------------------------------------------------- /Chapter 8/Function-Objects.md: -------------------------------------------------------------------------------- 1 | # Function Objects 2 | 3 | A **Function Object** is simply any object that can be called as if it is a function. Remember in C, we can use function pointers like this: 4 | 5 | ```c 6 | int sum(int a, int b) { 7 | return a + b; 8 | } 9 | 10 | int main() { 11 | int ret = sum(10, 20); 12 | return 0; 13 | } 14 | ``` 15 | 16 | In C++, a function object is any object for which the function call operator `()` is defined. We can call functions from the object: 17 | 18 | ```cpp 19 | class Sum { 20 | public: 21 | int operator()(int a, int b) { 22 | return a + b; 23 | } 24 | }; 25 | int main() { 26 | Sum sum; 27 | int ret = sum(10, 20); 28 | } 29 | ``` 30 | 31 | What's the benefit of using function objects? Let's look at the following example, where we defined a template function *compare()* for comparing two elements. 32 | 33 | ```cpp 34 | template 35 | bool compare(T a, T b) { 36 | return a < b; 37 | } 38 | int main() { 39 | cout << compare(10, 20) << endl; // 0 40 | cout << compare('b', 'y') << endl; // 1 41 | return 0; 42 | } 43 | ``` 44 | 45 | Here we compare the elements using operator `<`. What if we want to use `>` instead? We need to define another comparing function and provide a unified interface. A solution using function pointers in C style looks like this: 46 | 47 | ```cpp 48 | template 49 | bool greater(T a, T b) { 50 | return a > b; 51 | } 52 | template 53 | bool less(T a, T b) { 54 | return a < b; 55 | } 56 | template 57 | bool compare(T a, T b, Compare comp) { 58 | return comp(a, b); 59 | } 60 | int main() { 61 | cout << compare(10, 20, greater) << endl; // 0 62 | cout << compare('b', 'y', less) << endl; // 1 63 | return 0; 64 | } 65 | ``` 66 | 67 | Here we use function pointers as parameter and call the corresponding function through a function pointer. But remember that template functions are expanded at their call points, which means that *greater()* or *less()* can not be inlined inside *compare()*. This reduces efficiency since there is function call overhead. 68 | 69 | A better approach is to use function objects: 70 | 71 | ```cpp 72 | template 73 | class greater { 74 | public: 75 | bool operator()(T a, T b) { 76 | return a > b; 77 | } 78 | }; 79 | template 80 | class less { 81 | public: 82 | bool operator()(T a, T b) { 83 | return a < b; 84 | } 85 | }; 86 | template 87 | bool compare(T a, T b, Compare comp) { 88 | return comp(a, b); 89 | } 90 | int main() { 91 | cout << compare(10, 20, greater()) << endl; // 0 92 | cout << compare('b', 'y', less()) << endl; // 1 93 | return 0; 94 | } 95 | ``` 96 | 97 | Now inside *compare()*, we simply call the member function operator `()` through objects. In this way, the function call overhead can be omitted, thus improving efficiency. 98 | 99 | Moreover, since function objects are created from classes, we can add member variables to record other information used when a function object is used, for example the total number of calls. This greatly improves programming flexibility. 100 | 101 | Function objects are widely used in STL containers and generic algorithms. For example, we can use function objects *greater* or *less* to change the underlying algorithms of containers: 102 | 103 | ```cpp 104 | priority_queue, greater> pq; 105 | set> s; 106 | ``` 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Chapter 8/Generic-Algorithms-Binders-and-Lambda-Expressions.md: -------------------------------------------------------------------------------- 1 | # Generic Algorithms, Binders and Lambda Expressions 2 | 3 | STL provides us powerful generic algorithms that can be used in containers. 4 | 5 | ```cpp 6 | #include 7 | ``` 8 | 9 | Generic algorithms accepts iterators as parameters. Iterators provide uniform interfaces of containers for traverse. What's more, function objects can also be parameters to customize the algorithm. 10 | 11 | **sort()** 12 | 13 | *sort()* can sort the elements inside a container. Its default order is form small to large using operator *<* for comparison. We can also pass inside a function object *greater* to sort elements from large to small. 14 | 15 | ```cpp 16 | int main() { 17 | int arr[] = {12, 4, 78, 9, 21, 43, 56, 52, 42, 31}; 18 | vector vec(arr, arr+sizeof(arr)/sizeof(arr[0])); 19 | sort(vec.begin(), vec.end()); 20 | for (int v : vec) { 21 | cout << v << " "; // 4 9 12 21 31 42 43 52 56 78 22 | } 23 | sort(vec.begin(), vec.end(), greater()); 24 | for (int v : vec) { 25 | cout << v << " "; // 78 56 52 43 42 31 21 12 9 4 26 | } 27 | ... 28 | return 0; 29 | } 30 | ``` 31 | 32 | **binary_search()** 33 | 34 | *binary_search* is used to search for an element in a ordered container, which returns a bool value. The time complexity is *O(log(n))*. 35 | 36 | ```cpp 37 | int main() { 38 | ... 39 | if (binary_search(vec.begin(), vec.end(), 21)) { 40 | cout << "21 exists" << endl; // 21 exists 41 | } 42 | ... 43 | return 0; 44 | } 45 | ``` 46 | 47 | **find()** 48 | 49 | *find()* can also searches for an element inside the container, which returns the iterator to it. If the element is not found, the *end()* iterator is returned. *find()* searches for the element sequentially, and the time complexity is *O(n)*. 50 | 51 | ```cpp 52 | int main() { 53 | ... 54 | auto it = find(vec.begin(), vec.end(), 21); 55 | if (it != vec.end()) { 56 | cout << "21 exists" << endl; // 21 exists 57 | } 58 | ... 59 | return 0; 60 | } 61 | ``` 62 | 63 | **find_if** 64 | 65 | *find_if* searches for elements that meet the criteria. Let's illustrate it with an example. Suppose the vector is in reverse order. We want to insert number 48 inside it, and keep the vector ordered. To do so, we need to find the first element smaller than 48. In this case, every element should be compared with 48 using operator `>`. Apparently, we want to use the function object *greater*. But notice that *greater* is a binary function which takes two elements and compares them, but here we only need a unary function with only one parameter since the other one is fixed to 48. Unfortunately, there are not other function objects in STL that meets our requirement. To achieve this, we need to use a **binder**. 66 | 67 | ```cpp 68 | #include 69 | ``` 70 | 71 | There are two types of binders to bind binary function into unary function: *bind1st()* and *bind2st()*. The former binds the first parameter of the binary function objects, while the latter binds the second parameter. In this example, we need to bind the first parameter in *greater()* as 48, therefore every element will be compared with 48. 72 | 73 | ```cpp 74 | int main() { 75 | ... 76 | auto it = find_if(vec.begin(), vec.end(), bind1st(greater(), 48)); 77 | vec.insert(it, 48); 78 | for (int v : vec) { 79 | cout << v << " "; // 78 56 52 48 43 42 31 21 12 9 4 80 | } 81 | ... 82 | return 0; 83 | } 84 | ``` 85 | 86 | Similarly, it has the same effect if we use *less* instead and bind its second parameter as 48: 87 | 88 | ```cpp 89 | auto it = find_if(vec.begin(), vec.end(), bind2nd(less(), 48)); 90 | ``` 91 | 92 | Binders are powerful but may be hard to understand for beginners. Fortunately, C++11 introduces **lambda expressions** that can achieve this in a much more elegant way: 93 | 94 | ```cpp 95 | auto it = find_if(vec.begin(), vec.end(),[](int val) -> bool { return val < 48; }); 96 | ``` 97 | 98 | See if you can understand the above expression. We will not elaborate the underlying principles of lambda expressions here. 99 | 100 | **for_each** 101 | 102 | *for_each* traverses all the elements in the container, and apply customized functions to each of them. We can use function objects, or lambda expressions as well. Similarly, a unary function object is required here. 103 | 104 | ```cpp 105 | int main() { 106 | ... 107 | for_each(vec.begin(), vec.end(), 108 | [](int val) -> void { 109 | if (val % 2 == 0) { 110 | cout << val << " "; 111 | } 112 | }); // 78 56 52 48 42 12 4 113 | return 0; 114 | } 115 | ``` 116 | 117 | In the above example, we use a lambda expression to print out all even numbers in the vector. 118 | 119 | > Lambda expressions are widely used in dynamic programming languages such as python. More details about lambda expressions will be illustrated in the future. -------------------------------------------------------------------------------- /Chapter 8/More-about-Iterators.md: -------------------------------------------------------------------------------- 1 | # More about Iterators 2 | 3 | As we have already known, iterators are useful to traverse elements in a STL container. However, there are also other types of iterators that are commonly used. 4 | 5 | ## *iterator* 6 | 7 | *iterator* is the normal type of iterators we have learned before. It supports forward traversal of containers. We can use the deference to access the element it points to, and modify it as well. 8 | 9 | ```cpp 10 | int main() { 11 | vector v; 12 | for (int i = 0; i < 10; i++) { 13 | v.push_back(i); 14 | } 15 | vector::iterator it; 16 | for (it = v.begin(); it != v.end(); ++it) { 17 | *it += 1; 18 | cout << *it << " "; // 1 2 3 4 5 6 7 8 9 10 19 | } 20 | return 0; 21 | } 22 | ``` 23 | 24 | ## *const_iterator* 25 | 26 | *const_iterator* supports forward traversal of containers. We can use the deference to access the element it points to, but cannot modify it, since the deference returns a `const` reference to the element. 27 | 28 | ```cpp 29 | int main() { 30 | vector v; 31 | for (int i = 0; i < 10; i++) { 32 | v.push_back(i); 33 | } 34 | vector::const_iterator it; 35 | for (it = v.begin(); it != v.end(); ++it) { 36 | *it += 1; // ERROR 37 | cout << *it << " "; 38 | } 39 | return 0; 40 | } 41 | ``` 42 | 43 | ## *reverse_iterator* 44 | 45 | *reverse_iterator* supports backward traversal of containers. We can use the deference to access the element it points to, and modify it as well. 46 | 47 | Instead of *begin()* and *end()* in normal iterators, we use *rbegin()* and *rend()* in reverse iterators. *rbegin()* returns a *reverse_iterator* to the last element in the container, while *rend()* returns a *reverse_iterator* to the one before the first element in the container. 48 | 49 | ```cpp 50 | int main() { 51 | vector v; 52 | for (int i = 0; i < 10; i++) { 53 | v.push_back(i); 54 | } 55 | vector::reverse_iterator it; 56 | for (it = v.rbegin(); it != v.rend(); ++it) { 57 | *it += 1; 58 | cout << *it << " "; // 10 9 8 7 6 5 4 3 2 1 59 | } 60 | return 0; 61 | } 62 | ``` 63 | 64 | ## *const_reverse_iterator* 65 | 66 | *reverse_iterator* supports backward traversal of containers. We can use the deference to access the element it points to, but cannot modify it, since the deference returns a `const` reference to the element. 67 | 68 | ```cpp 69 | int main() { 70 | vector v; 71 | for (int i = 0; i < 10; i++) { 72 | v.push_back(i); 73 | } 74 | vector::const_reverse_iterator it; 75 | for (it = v.rbegin(); it != v.rend(); ++it) { 76 | *it += 1; // ERROR 77 | cout << *it << " "; 78 | } 79 | return 0; 80 | } 81 | ``` 82 | 83 | -------------------------------------------------------------------------------- /Chapter 9/Behind-the-Object.md: -------------------------------------------------------------------------------- 1 | # Behind the Object 2 | 3 | In this chapter we are going to learn how to write efficient C++ code. Many performance issues are related to objects, so we must be clear about what is happening when we are using objects. Let's look at this example, where we defined the constructor, destructor, copy constructor and operator`=` of class *Test*. 4 | 5 | ```cpp 6 | class Test { 7 | public: 8 | Test(int a = 10) : ma(a){ 9 | cout << "Test(int)" << endl; 10 | } 11 | ~Test() { 12 | cout << "~Test()" << endl; 13 | } 14 | Test(const Test &t) : ma(t.ma) { 15 | cout << "Test(const Test &)" << endl; 16 | } 17 | Test &operator=(const Test &t) { 18 | cout << "operator=" << endl; 19 | ma = t.ma; 20 | return *this; 21 | } 22 | int getData() const {return ma;} 23 | private: 24 | int ma; 25 | } 26 | ``` 27 | 28 | Now in the main function, we defined four objects in four different ways. What are their outputs respectively? The first object is defined directly without any initialization, so the constructor *Test::Test(int)* is called with a default parameter *a* = 10. Next, *t2* is defined with *t1*, so the copy constructor of *Test* is called. *t3* is constructed in the same way as *t2* does, where the copy constructor is called. But how should *t4* be constructed? For this statement, we may think that a temporary object is constructed first with *a* = 20, and *t4* is then constructed with the copy constructor. But the compiler is much more clever than we think it is. 29 | 30 | ```cpp 31 | int main() { 32 | Test t1; // Test(int) 33 | Test t2(t1); // Test(const Test&) 34 | Test t3 = t1; // Test(const Test&) 35 | Test t4 = Test(20); // Test(int) 36 | ... 37 | } 38 | ``` 39 | 40 | When a temporary object (by temporary we means the object is not named by a variable) is used to defined a new object, the compiler will call its copy constructor directly without constructing any temporary object. In other words, the above statement is the same as `Test t4(20);`. 41 | 42 | If a variable is already defined before, and is assigned with a temporary object, then the temporary object should be created completely: 43 | 44 | ```cpp 45 | int main() { 46 | ... 47 | t4 = Test(30); 48 | ... 49 | } 50 | ``` 51 | 52 | In the above example, a temporary object *Test(30)* is constructed first. Then it is assigned to t4 with operator`=`. After its task is completed, the temporary object is destroyed. So the single statement has three lines of output: 53 | 54 | ``` 55 | Test(int) 56 | operator= 57 | ~Test() 58 | ``` 59 | 60 | The lifecycle of a temporary object is just the current statement. It will be destroyed immediately after the statement ends. 61 | 62 | Another way to defined an object is to use the implicit conversion: 63 | 64 | ```cpp 65 | int main() { 66 | ... 67 | t4 = 30; 68 | ... 69 | } 70 | ``` 71 | 72 | Here *t4* is a *Test* object, but 30 is an integer. How can this be compiled? Again, the compiler is much more clever than we think it is. In this case, the compiler goes to see if *t4* has a constructor with a paramter of type *int*. Then a temporary object is constructed with *a* = 30. Then everything is the same as before. Implicit conversion also requires complete construction of temporary objects. 73 | 74 | What if we use a pointer or a reference to point to a temporary object? Let's look at this example: 75 | 76 | ```cpp 77 | int main() { 78 | ... 79 | Test *p = &Test(40); 80 | const Test &ref = Test(50); 81 | ... 82 | } 83 | ``` 84 | 85 | Remember that a temporary object is destroyed immediately after the current statement ends. In the first case, we use a pointer to store the address of a temporary object. But when the statement ends, the object no longer exists. Then things will go wrong if we continue using the pointer. Then what about references? Though references has the same underlying implementation as pointers, they are different in this case. Remember that a reference is an alias of another object, so the above statement is just the same as `Test ref(50);` Apparently, the object will not be destroyed after the statement ends. The above example has the following output: 86 | 87 | ``` 88 | Test(int) 89 | ~Test() 90 | Test(int) 91 | ``` 92 | 93 | Remember: **NEVER** use pointers to point to temporary variables! -------------------------------------------------------------------------------- /Chapter 9/Member-Functions-with-Rvalue-References.md: -------------------------------------------------------------------------------- 1 | # Member Functions with Rvalue References 2 | 3 | In Chapter 5, we have implemented a class *MyString* that represents a string, in which its copy constructor and operator `=` is defined as 4 | 5 | ```cpp 6 | MyString::MyString(const MyString &other) { 7 | _pstr = new char[strlen(other._pstr) + 1]; 8 | strcpy(_pstr, other._pstr); 9 | } 10 | 11 | MyString::MyString &operator=(const MyString &other) { 12 | if (this == &other) return *this; 13 | delete[] _pstr; 14 | _pstr = new char[strlen(other._pstr) + 1]; 15 | strcpy(_pstr, other._pstr); 16 | return *this; 17 | } 18 | ``` 19 | 20 | The former allocates memory for the new string, and copies the data from the old one. The latter first deletes the old string, and then does the copy. 21 | 22 | Now suppose we have an interface *getString()* which takes a *MyString* object, does some modification and returns a new object. And in the main function, we pass *s1* inside, and use *s2* to receive the return object. 23 | 24 | ```cpp 25 | MyString getString(MyString &str) { 26 | const char* pstr = str.c_str(); 27 | myString tmp(pstr); 28 | // Do something 29 | return tmp; 30 | } 31 | 32 | int main() { 33 | MyString s1 = "aaa"; 34 | MyString s2 = "bbb"; 35 | s2 = getString(s1); 36 | return 0; 37 | } 38 | ``` 39 | 40 | From the last chapter we've already known that three principles of optimizing objects should be applied. However in some circumstances, we cannot either returns a temporary object or receive the returned object by initialization, for example here. In this case, a temporary object has to be constructed on the heap of *main()* and be assigned to *s2*. What our copy constructor and operator `=` do here is to allocate new memory space, and copy the data from the temporary object. But since temporary object is destroyed immediately after then, it is an unnecessary overhead of memory. Why don't we just modify the pointer of the new object, and let it point to the temporary object's data? Fortunately, C++ 11 introduced **Rvalue references** to deal with these circumstances. 41 | 42 | First let's review what is a Rvalue. We already know that a Lvalue has a specific name or a memory address, for example variables and objects. Rvalues are those without a name or memory address, for example, constants and temporary objects. We can only use a constant reference or a Rvalue reference to refer to a Rvalue: 43 | 44 | ```cpp 45 | int main() { 46 | const int &a = 20; 47 | int &&b = 20; 48 | int &&c = MyString("aaa"); 49 | } 50 | ``` 51 | 52 | Knowing this, we can add a new copy constructor which takes a Rvalue reference as the parameter. Inside the copy constructor, we only need to let the new pointer point to the data of the temporary object. After then, we set its pointer to NULL, so that the destructor does nothing. 53 | 54 | ```cpp 55 | MyString(MyString &&other) { 56 | _pstr = other._ptr; 57 | other._pstr = nullptr; 58 | } 59 | ``` 60 | 61 | operator `=` with Rvalue reference can be added in a similar way: 62 | 63 | ```cpp 64 | MyString &operator=(MyString &&other) { 65 | if (this == &other) return *this; 66 | delete[] mptr; 67 | _pstr = other._pstr; 68 | other._pstr = null; 69 | return *this; 70 | } 71 | ``` 72 | 73 | Now when the copy constructor or operator `=` is called on a temporary object, only the pointers are changed. No extra memory allocation or deallocation is needed, which increases memory efficiency. Member functions with Rvalue references are widely used in STL containers to improve performance. 74 | -------------------------------------------------------------------------------- /Chapter 9/Move-Semantics-and-Perfect-Forwarding.md: -------------------------------------------------------------------------------- 1 | # Move Semantics and Perfect Forwarding 2 | 3 | In the previous article, we learned member functions with Rvalue references as parameters. Member functions with Rvalue references are widely used in STL containers to improve performance. For example 4 | 5 | ```cpp 6 | int main() { 7 | vector v; 8 | MyString s = "aaa" 9 | v.push_back(s); 10 | v.push_back(MyString("bbb")); 11 | return 0; 12 | } 13 | ``` 14 | 15 | The first *push_back()* takes a *MyString* object, whose normal copy constructor will be called in the container. The latter one takes a temporary object, whose copy constructor with Rvalue reference will be called. 16 | 17 | We have written a *MyVector* in previous chapters. Now let's modify it to add member functions with Rvalue references. We've already have a normal version of *push_back* as 18 | 19 | ```cpp 20 | void push_back(const T &val) { 21 | if (full()) expand(); 22 | _allocator.construct(_last, val); 23 | _last++; 24 | } 25 | ``` 26 | 27 | which calls the *construct()* method of the allocator. The Rvalue reference version of *push_back()* can be written as 28 | 29 | ```cpp 30 | void push_back(T &&val) { 31 | if (full()) expand(); 32 | _allocator.construct(_last, val); 33 | _last++; 34 | } 35 | ``` 36 | 37 | Inside *push_back()* we called *constrct()*, so its Rvalue reference version should be implemented as well. 38 | 39 | ```cpp 40 | void construct(T *p, T &&val) { new (p) T(val); } 41 | ``` 42 | 43 | But the Rvalue reference *val* is a Lvalue itself, which means that *constrct(_last, val)* will always call the Lvalue reference version. To fix this, we can use *std::move()* here, which is called the **move semantics**. 44 | 45 | ```cpp 46 | #include 47 | ``` 48 | 49 | *std::move()* is used to indicate that an object may be "moved from". In specific, it is exactly equivalent to a `static_cast` to an Rvalue reference type. We can use *std::move()* to cast *val* into its Rvalue reference: 50 | 51 | ```cpp 52 | void push_back(T &&val) { 53 | if (full()) expand(); 54 | _allocator.construct(_last, std::move(val)); 55 | _last++; 56 | } 57 | ``` 58 | 59 | Similarly, *std::move()* should be used in *construct()* as well: 60 | 61 | ```cpp 62 | void construct(T *p, T &&val) { new (p) T(std::move(val)); } 63 | ``` 64 | 65 | Now we have the corresponding Lvalue reference and Rvalue reference member functions. But should we defined two functions every time? It seems not a good way of reusing code. A better approach is to use the type deduction of function templates: 66 | 67 | ```cpp 68 | template 69 | void push_back(Ty &&val) { 70 | if (full()) expand(); 71 | _allocator.construct(_last, std::forward(val)); 72 | _last++; 73 | } 74 | ``` 75 | 76 | In the above code, we defined a template function *push_back()* which takes a parameter of type *Ty &&*. When a Lvalue is passed in, *val* will also be a Lvalue because of the type deduction. Instead, if a Rvalue is passed in, *val* will be a Rvalue. But how could the corresponding version of *construct()* be called properly, since *val* is always treated as a Lvalue? Here we need to use *std::forward()*, which is known as **perfect forwarding**. 77 | 78 | *std::forward()* forwards Lvalues as either Lvalues or as Rvalues, depending on the type. Then the corresponding version of *construct()* can be called. 79 | 80 | Similarly, *construct()* can also be rewritten into template function as 81 | 82 | ```cpp 83 | template 84 | void construct(T *p, Ty &&val) { 85 | new (p) T(std::forward(val)); 86 | } 87 | ``` 88 | 89 | *std::forward()* is also used here to call the corresponding copy constructor of type *T*. -------------------------------------------------------------------------------- /Chapter 9/Optimizing-Objects-in-Functions.md: -------------------------------------------------------------------------------- 1 | # Optimizing Objects in Functions 2 | 3 | This article continues from the previous one. Here we defined a function *getObject()*, which takes a *Test* object as parameter, constructs an object *tmp* and returns it. Remember that objects on the function stack will be destroyed after the function returns, so we cannot returns a pointer or reference to an object. 4 | 5 | ```cpp 6 | Test getObject(Test t) { 7 | int val = t.getData(); 8 | Test tmp(val); 9 | return tmp; 10 | } 11 | ``` 12 | 13 | Now in the main function, we defined two *Test* objects *t1* and *t2*. Then we call *getObject()* with *t1* as argument, and assign the return value to *t2*. In this case, how many member functions will be called in total? Obviously, the constructors for *t1* and *t2* should be called first. 14 | 15 | ```cpp 16 | int main() { 17 | Test t1; // 1. Test(int) 18 | Test t2; // 2. Test(int) 19 | t2 = getObject(t1); 20 | return 0; 21 | } 22 | ``` 23 | 24 | Then we pass *t1* inside the function. Here *t1* is an argument, but *t* is a parameter, so *t* has to be constructed with the copy constructor. Then we get the data of *t*, and use that value to create a new object *tmp*. Now *tmp* needs to be returned, but it is an object on the stack, which means it is going to be destroyed after the function returns. Therefore, we need to create a temporary object on the stack of *main()*, and construct it with the copy constructor. After the function returns, object *tmp* and parameter *t* is destroyed in turn. 25 | 26 | ```cpp 27 | Test getObject(Test t) { // 3. Test(const Test &) 28 | int val = t.getData(); 29 | Test tmp(val); // 4. Test(int) 30 | return tmp; // 5. Test(const Test&) 31 | // 6. ~Test() 32 | // 7. ~Test() 33 | } 34 | ``` 35 | 36 | Then the temporary object is assigned to *t2* with operator`=`. After the statement ends, the temporary object is destroyed immediately. At last, *t2* and *t1* are destroyed one by one. 37 | 38 | ```cpp 39 | int main() { 40 | Test t1; // 1. Test(int) 41 | Test t2; // 2. Test(int) 42 | t2 = getObject(t1); // 8. operator= 43 | // 9. ~Test() 44 | // 10. ~Test() 45 | // 11. ~Test() 46 | return 0; 47 | } 48 | ``` 49 | 50 | In the previous example we only call a single function, but eleven member functions have been called. Let's try to optimize them in order to reduce the number of calls. 51 | 52 | ## Optimization 1 53 | 54 | First, we can pass the argument by reference. It is most commonly used when objects are involved in parameters. In doing so, we don't need to construct the parameter, therefore initialization and destruction of the parameter are eliminated. 55 | 56 | ```cpp 57 | Test getObject(Test &t) { 58 | int val = t.getData(); 59 | Test tmp(val); // 3. Test(int) 60 | return tmp; // 4. Test(const Test&) 61 | // 5. ~Test() 62 | } 63 | 64 | int main() { 65 | Test t1; // 1. Test(int) 66 | Test t2; // 2. Test(int) 67 | t2 = getObject(t1); // 6. operator= 68 | // 7. ~Test() 69 | // 8. ~Test() 70 | // 9. ~Test() 71 | return 0; 72 | } 73 | ``` 74 | 75 | ## Optimization 2 76 | 77 | Before in function *getObject()*, we first construct an object *tmp*, and then return it. Actually, this action is unnecessary. Instead, we can return a temporary object. In this way, the returned object is constructed in *main()* directly. Now the construction and destruction of *tmp* can be eliminated. 78 | 79 | ```cpp 80 | Test getObject(Test &t) { 81 | int val = t.getData(); 82 | return Test(val); // 3. Test(int) 83 | } 84 | 85 | int main() { 86 | Test t1; // 1. Test(int) 87 | Test t2; // 2. Test(int) 88 | t2 = getObject(t1); // 4. operator= 89 | // 5. ~Test() 90 | // 6. ~Test() 91 | // 7. ~Test() 92 | return 0; 93 | } 94 | ``` 95 | 96 | ## Optimization 3 97 | 98 | Since the function returns a temporary object, we can directly use the object to construct *t2*. Remember that when a temporary object is used to defined a new object, the compiler will call its copy constructor directly without constructing any temporary object. In other words, the function *getObject()* directly constructs *t2* without creating a temporary object on the stack of *main()*. In this way, we eliminate the construction and destruction of temporary object, and the call of operator`=` . 99 | 100 | ```cpp 101 | Test getObject(Test &t) { 102 | int val = t.getData(); 103 | return Test(tmp); // 2. Test(int) 104 | } 105 | 106 | int main() { 107 | Test t1; // 1. Test(int) 108 | Test t2 = getObject(t1); 109 | // 3. ~Test() 110 | // 4. ~Test() 111 | return 0; 112 | } 113 | ``` 114 | 115 | ## Summary 116 | 117 | After these three optimizations, the number of member function calls has been reduced from 11 to 4. It may seems trivial, but the performance will be greatly improved if function *getObject()* is called hundreds and thousands of times. Therefore, when using objects in functions, we should remember three principles: 118 | 119 | 1. In passing arguments, objects should be preferably passed by reference, not by value. 120 | 2. Functions should preferably return temporary objects directly instead of defining them first. 121 | 3. When receiving from function whose return value is an object, it is preferably to receive by initialization, not by assignment. -------------------------------------------------------------------------------- /assets/compiling-and-linking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/compiling-and-linking.png -------------------------------------------------------------------------------- /assets/diamond-problem-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/diamond-problem-2.png -------------------------------------------------------------------------------- /assets/diamond-problem-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/diamond-problem-3.png -------------------------------------------------------------------------------- /assets/diamond-problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/diamond-problem.png -------------------------------------------------------------------------------- /assets/function-call-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-1.png -------------------------------------------------------------------------------- /assets/function-call-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-2.png -------------------------------------------------------------------------------- /assets/function-call-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-3.png -------------------------------------------------------------------------------- /assets/function-call-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-4.png -------------------------------------------------------------------------------- /assets/function-call-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-5.png -------------------------------------------------------------------------------- /assets/function-call-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/function-call-6.png -------------------------------------------------------------------------------- /assets/linking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/linking.png -------------------------------------------------------------------------------- /assets/shallow-copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/shallow-copy.png -------------------------------------------------------------------------------- /assets/shared_ptr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/shared_ptr.png -------------------------------------------------------------------------------- /assets/virtual-address-space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/virtual-address-space.png -------------------------------------------------------------------------------- /assets/virtual-base-class-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/virtual-base-class-2.png -------------------------------------------------------------------------------- /assets/virtual-base-class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/virtual-base-class.png -------------------------------------------------------------------------------- /assets/virtual-function-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/virtual-function-1.png -------------------------------------------------------------------------------- /assets/virtual-function-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/navining/gocpp/4c7b235d56009e8568939880b28b7f38c85011eb/assets/virtual-function-2.png -------------------------------------------------------------------------------- /codes/Chapter 10/SmartPtr.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class RefCount { 3 | public: 4 | RefCount(T *ptr = nullptr) : _ptr(ptr) { 5 | if (_ptr != nullptr) _count = 1; 6 | } 7 | void addRef() { _count++; } 8 | int delRef() { return --_count; } 9 | 10 | private: 11 | T *_ptr; 12 | int _count; 13 | }; 14 | 15 | template 16 | class SmartPtr { 17 | public: 18 | SmartPtr(T *ptr = nullptr) : _ptr(ptr) { _refCount = new RefCount(ptr); } 19 | ~SmartPtr() { 20 | if (0 == _refCount->delRef()) { 21 | delete _ptr; 22 | _ptr = nullptr; 23 | } 24 | } 25 | T &operator*() { return *_ptr; } 26 | T *operator->() { return _ptr; } 27 | SmartPtr(const SmartPtr &other) { 28 | _ptr = other._ptr; 29 | _refCount = other._refCount; 30 | if (_ptr != nullptr) _refCount->addRef(); 31 | } 32 | SmartPtr &operator=(const SmartPtr &other) { 33 | if (this == &other) return *this; 34 | if (0 == _refCount->delRef()) { 35 | delete _ptr; 36 | } 37 | _ptr = other._ptr; 38 | _refCount = other._refCount; 39 | _refCount->addRef(); 40 | return *this; 41 | } 42 | 43 | private: 44 | T *_ptr; 45 | RefCount *_refCount; 46 | }; -------------------------------------------------------------------------------- /codes/Chapter 11/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | class Thread { 8 | public: 9 | Thread(function func) : _func(func) {} 10 | 11 | thread start() { 12 | thread t(_func); 13 | return t; 14 | } 15 | 16 | private: 17 | function _func; 18 | }; 19 | 20 | class ThreadPool { 21 | public: 22 | ThreadPool() {} 23 | 24 | ~ThreadPool() { 25 | for (int i = 0; i < _pool.size(); i++) { 26 | delete _pool[i]; 27 | } 28 | } 29 | void startPool(int size) { 30 | for (int i = 0; i < size; i++) { 31 | _pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, i))); 32 | } 33 | 34 | for (int i = 0; i < size; i++) { 35 | _handler.push_back(_pool[i]->start()); 36 | } 37 | 38 | for (thread &t : _handler) { 39 | t.join(); 40 | } 41 | } 42 | 43 | private: 44 | vector _pool; 45 | vector _handler; 46 | void runInThread(int id) { cout << "Thread " << id << endl; } 47 | }; 48 | -------------------------------------------------------------------------------- /codes/Chapter 12/Atomic-Operations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | volatile atomic_bool isReady; 8 | volatile atomic_int myCount; 9 | 10 | void task() { 11 | while (!isReady) { 12 | this_thread::yield(); 13 | } 14 | for (int i = 0; i < 100; i++) { 15 | myCount++; 16 | } 17 | } 18 | 19 | int main() { 20 | list tlist; 21 | for (int i = 0; i < 10; i++) { 22 | tlist.push_back(thread(task)); 23 | } 24 | isReady = true; 25 | for (thread &t : tlist) { 26 | t.join(); 27 | } 28 | cout << myCount << endl; 29 | return 0; 30 | } -------------------------------------------------------------------------------- /codes/Chapter 12/Mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | int numOfTickets = 100; 9 | mutex _mutex; 10 | 11 | void sellTicket(int index) { 12 | while (numOfTickets > 0) { 13 | { 14 | lock_guard lock(_mutex); 15 | if (numOfTickets > 0) { 16 | printf("Window %d sells ticket No. %d\n", index, numOfTickets); 17 | numOfTickets--; 18 | } 19 | } 20 | this_thread::sleep_for(chrono::milliseconds(100)); 21 | } 22 | } 23 | 24 | int main() { 25 | list tlist; 26 | for (int i = 0; i < 3; i++) { 27 | tlist.push_back(thread(sellTicket, i)); 28 | } 29 | for (thread &t : tlist) { 30 | t.join(); 31 | } 32 | return 0; 33 | } -------------------------------------------------------------------------------- /codes/Chapter 12/Producer-Consumer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | mutex _mutex; 10 | condition_variable cv; 11 | 12 | class Queue { 13 | public: 14 | void put(int val) { 15 | unique_lock lock(_mutex); 16 | while (!q.empty()) { 17 | cv.wait(lock); 18 | } 19 | q.push(val); 20 | cv.notify_all(); 21 | printf("Producer produces %d\n", val); 22 | } 23 | 24 | int get() { 25 | unique_lock lock(_mutex); 26 | while (q.empty()) { 27 | cv.wait(lock); 28 | } 29 | int val = q.front(); 30 | q.pop(); 31 | cv.notify_all(); 32 | printf("Consumer consumes %d\n", val); 33 | return val; 34 | } 35 | 36 | private: 37 | queue q; 38 | }; 39 | 40 | void producer(Queue *q) { 41 | for (int i = 1; i <= 10; i++) { 42 | q->put(i); 43 | this_thread::sleep_for(chrono::milliseconds(100)); 44 | } 45 | } 46 | 47 | void consumer(Queue *q) { 48 | for (int i = 1; i <= 10; i++) { 49 | q->get(); 50 | this_thread::sleep_for(chrono::milliseconds(100)); 51 | } 52 | } 53 | 54 | int main() { 55 | Queue q; 56 | thread t1(producer, &q); 57 | thread t2(consumer, &q); 58 | t1.join(); 59 | t2.join(); 60 | return 0; 61 | } -------------------------------------------------------------------------------- /codes/Chapter 13/Abstract-Factory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Car { 6 | public: 7 | Car(string name) : _name(name) {} 8 | virtual void show() = 0; 9 | 10 | protected: 11 | string _name; 12 | }; 13 | 14 | class Bmw : public Car { 15 | public: 16 | Bmw(string name) : Car(name) {} 17 | void show() { cout << "BMW: " << _name << endl; } 18 | }; 19 | 20 | class Audi : public Car { 21 | public: 22 | Audi(string name) : Car(name) {} 23 | void show() { cout << "Audi: " << _name << endl; } 24 | }; 25 | 26 | class Light { 27 | public: 28 | virtual void show() = 0; 29 | }; 30 | 31 | class BmwLight : public Light { 32 | public: 33 | void show() { cout << "BMW Light" << endl; } 34 | }; 35 | 36 | class AudiLight : public Light { 37 | public: 38 | void show() { cout << "Audi Light" << endl; } 39 | }; 40 | 41 | class AbstractFactory { 42 | public: 43 | virtual Car* createCar(string name) = 0; 44 | virtual Light* createLight() = 0; 45 | }; 46 | 47 | class BMWFactory : public AbstractFactory { 48 | public: 49 | Car* createCar(string name) { return new Bmw(name); } 50 | Light* createLight() { return new BmwLight(); } 51 | }; 52 | 53 | class AudiFactory : public AbstractFactory { 54 | public: 55 | Car* createCar(string name) { return new Audi(name); } 56 | Light* createLight() { return new AudiLight(); } 57 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class VGA { 5 | public: 6 | virtual void play() = 0; 7 | }; 8 | 9 | class TV1 : public VGA { 10 | public: 11 | void play() { cout << "TV1: VGA" << endl; } 12 | }; 13 | 14 | class HDMI { 15 | public: 16 | virtual void play() = 0; 17 | }; 18 | 19 | class TV2 : public HDMI { 20 | public: 21 | void play() { cout << "TV2: HDMI" << endl; } 22 | }; 23 | 24 | class Adapter : public VGA { 25 | public: 26 | Adapter(HDMI *p) : pHDMI(p) {} 27 | void play() { pHDMI->play(); } 28 | 29 | private: 30 | HDMI *pHDMI; 31 | }; 32 | 33 | class Computer { 34 | public: 35 | void playVideo(VGA *pVGA) { pVGA->play(); } 36 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Decorator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Car { 5 | public: 6 | virtual void show() = 0; 7 | }; 8 | 9 | class Bmw : public Car { 10 | public: 11 | void show() { cout << "This is a BMW" << endl; } 12 | }; 13 | 14 | class Audi : public Car { 15 | public: 16 | void show() { cout << "This is an Audi" << endl; } 17 | }; 18 | 19 | class Benz : public Car { 20 | public: 21 | void show() { cout << "This is a Benz" << endl; } 22 | }; 23 | 24 | class CarDecorator1 : public Car { 25 | public: 26 | CarDecorator1(Car *p) : pCar(p) {} 27 | ~CarDecorator1() { delete pCar; } 28 | void show() { 29 | pCar->show(); 30 | cout << "Feature 1" << endl; 31 | } 32 | 33 | private: 34 | Car *pCar; 35 | }; 36 | 37 | class CarDecorator2 : public Car { 38 | public: 39 | CarDecorator2(Car *p) : pCar(p) {} 40 | ~CarDecorator2() { delete pCar; } 41 | void show() { 42 | pCar->show(); 43 | cout << "Feature 2" << endl; 44 | } 45 | 46 | private: 47 | Car *pCar; 48 | }; 49 | 50 | class CarDecorator3 : public Car { 51 | public: 52 | CarDecorator3(Car *p) : pCar(p) {} 53 | ~CarDecorator3() { delete pCar; } 54 | void show() { 55 | pCar->show(); 56 | cout << "Feature 3" << endl; 57 | } 58 | 59 | private: 60 | Car *pCar; 61 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Factory-Method.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Car { 6 | public: 7 | Car(string name) : _name(name) {} 8 | virtual void show() = 0; 9 | 10 | protected: 11 | string _name; 12 | }; 13 | 14 | class Bmw : public Car { 15 | public: 16 | Bmw(string name) : Car(name) {} 17 | void show() { cout << "BMW: " << _name << endl; } 18 | }; 19 | 20 | class Audi : public Car { 21 | public: 22 | Audi(string name) : Car(name) {} 23 | void show() { cout << "Audi: " << _name << endl; } 24 | }; 25 | 26 | class Factory { 27 | public: 28 | virtual Car* createCar(string name) = 0; 29 | }; 30 | 31 | class BMWFactory : public Factory { 32 | public: 33 | Car* createCar(string name) { return new Bmw(name); } 34 | }; 35 | 36 | class AudiFactory : public Factory { 37 | public: 38 | Car* createCar(string name) { return new Audi(name); } 39 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Observer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | class Observer { 7 | public: 8 | virtual void handle(int id) = 0; 9 | }; 10 | 11 | class Observer1 : public Observer { 12 | public: 13 | void handle(int id) { 14 | switch (id) { 15 | case 1: 16 | cout << "Observer1: Message 1" << endl; 17 | break; 18 | case 2: 19 | cout << "Observer1: Message 2" << endl; 20 | break; 21 | default: 22 | cout << "Observer1: Unknown message" << endl; 23 | break; 24 | } 25 | } 26 | }; 27 | 28 | class Observer2 : public Observer { 29 | public: 30 | void handle(int id) { 31 | switch (id) { 32 | case 2: 33 | cout << "Observer2: Message 2" << endl; 34 | break; 35 | case 3: 36 | cout << "Observer2: Message 3" << endl; 37 | break; 38 | default: 39 | cout << "Observer2: Unknown message" << endl; 40 | break; 41 | } 42 | } 43 | }; 44 | 45 | class Observer3 : public Observer { 46 | public: 47 | void handle(int id) { 48 | switch (id) { 49 | case 1: 50 | cout << "Observer3: Message 1" << endl; 51 | break; 52 | case 3: 53 | cout << "Observer3: Message 3" << endl; 54 | break; 55 | default: 56 | cout << "Observer3: Unknown message" << endl; 57 | break; 58 | } 59 | } 60 | }; 61 | 62 | class Subject { 63 | public: 64 | void subscribe(Observer *observer, int id) { _map[id].push_back(observer); } 65 | void publish(int id) { 66 | auto it = _map.find(id); 67 | if (it != _map.end()) { 68 | for (Observer *p : it->second) { 69 | p->handle(id); 70 | } 71 | } 72 | } 73 | 74 | private: 75 | unordered_map> _map; 76 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Proxy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Movie { 5 | public: 6 | virtual void freeMovie() = 0; 7 | virtual void vipMovie() = 0; 8 | }; 9 | 10 | class MovieSite : public Movie { 11 | public: 12 | virtual void freeMovie() { cout << "Free Movie" << endl; } 13 | virtual void vipMovie() { cout << "VIP Movie" << endl; } 14 | }; 15 | 16 | class FreeMovieProxy : public Movie { 17 | public: 18 | FreeMovieProxy() { pMovie = new MovieSite(); } 19 | ~FreeMovieProxy() { delete pMovie; } 20 | virtual void freeMovie() { pMovie->freeMovie(); } 21 | virtual void vipMovie() { cout << "Permission denied!" << endl; } 22 | 23 | private: 24 | MovieSite *pMovie; 25 | }; 26 | 27 | class VipMovieProxy : public Movie { 28 | public: 29 | VipMovieProxy() { pMovie = new MovieSite(); } 30 | ~VipMovieProxy() { delete pMovie; } 31 | virtual void freeMovie() { pMovie->freeMovie(); } 32 | virtual void vipMovie() { pMovie->vipMovie(); } 33 | 34 | private: 35 | MovieSite *pMovie; 36 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Simple-Factory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Car { 6 | public: 7 | Car(string name) : _name(name) {} 8 | virtual void show() = 0; 9 | 10 | protected: 11 | string _name; 12 | }; 13 | 14 | class Bmw : public Car { 15 | public: 16 | Bmw(string name) : Car(name) {} 17 | void show() { cout << "BMW: " << _name << endl; } 18 | }; 19 | 20 | class Audi : public Car { 21 | public: 22 | Audi(string name) : Car(name) {} 23 | void show() { cout << "Audi: " << _name << endl; } 24 | }; 25 | 26 | enum CarType { BMW, AUDI }; 27 | 28 | class SimpleFactory { 29 | public: 30 | Car* createCar(CarType ct) { 31 | switch (ct) { 32 | case BMW: 33 | return new Bmw("X1"); 34 | case AUDI: 35 | return new Audi("A6"); 36 | } 37 | return nullptr; 38 | } 39 | }; -------------------------------------------------------------------------------- /codes/Chapter 13/Singleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Singleton1 { 5 | public: 6 | static Singleton1* getInstance() { return &instance; } 7 | 8 | private: 9 | static Singleton1 instance; 10 | Singleton1() {} 11 | Singleton1(const Singleton1&) = delete; 12 | Singleton1& operator=(const Singleton1&) = delete; 13 | }; 14 | Singleton1 Singleton1::instance; 15 | 16 | mutex _mutex; 17 | class Singleton2 { 18 | public: 19 | static Singleton2* getInstance() { 20 | if (instance == nullptr) { 21 | lock_guard lock(_mutex); 22 | if (instance == nullptr) instance = new Singleton2(); 23 | } 24 | return instance; 25 | } 26 | 27 | private: 28 | static Singleton2* volatile instance; 29 | Singleton2() {} 30 | Singleton2(const Singleton2&) = delete; 31 | Singleton2& operator=(const Singleton2&) = delete; 32 | }; 33 | Singleton2* volatile Singleton2::instance = nullptr; 34 | 35 | class Singleton3 { 36 | public: 37 | static Singleton3* getInstance() { 38 | static Singleton3 instance; 39 | return &instance; 40 | } 41 | 42 | private: 43 | Singleton3() {} 44 | Singleton3(const Singleton3&) = delete; 45 | Singleton3& operator=(const Singleton3&) = delete; 46 | }; -------------------------------------------------------------------------------- /codes/Chapter 3/Goods.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Goods { 5 | public: // Methods 6 | // Initialize goods information 7 | Goods(const char *name, double price, int amount); 8 | // Print goods information 9 | void show(); 10 | // Getters 11 | void setName(const char *name) { strcpy(_name, name); } 12 | void setPrice(double price) { _price = price; }; 13 | void setAmount(int amount) { _amount = amount; }; 14 | // Setters 15 | const char *getName() { return _name; }; 16 | double getPrice() { return _price; }; 17 | int getAmount() { return _amount; }; 18 | static void showCounts(); 19 | 20 | private: // Variables 21 | char _name[20]; 22 | double _price; 23 | int _amount; 24 | Date _date; 25 | static int _count; 26 | }; 27 | 28 | int Goods::_count = 0; 29 | 30 | Goods::Goods(const char *name, double price, int amount, int y, int m, int d) 31 | : _date(y, m, d), _price(price), _amount(amount) { 32 | strcpy(_name, name); 33 | count++; 34 | } 35 | 36 | void Goods::show() { 37 | cout << "name: " << _name << endl; 38 | cout << "price: " << _price << endl; 39 | cout << "amount: " << _amount << endl; 40 | _date.show(); 41 | }; 42 | 43 | void Goods::show() const { 44 | cout << "name: " << _name << endl; 45 | cout << "price: " << _price << endl; 46 | cout << "amount: " << _amount << endl; 47 | _date.show(); 48 | }; 49 | 50 | static void Goods::showCounts() { cout << "Counts: " << _count << endl; } 51 | 52 | class Date { 53 | public: 54 | Date(int y, int m, int d) { 55 | _year = y; 56 | _month = m; 57 | _date = d; 58 | } 59 | 60 | void show() { cout << _year << "/" << _month << "/" << _day << endl; } 61 | 62 | void show() const { cout << _year << "/" << _month << "/" << _day << endl; } 63 | 64 | private: 65 | int _yead; 66 | int _month; 67 | int _date; 68 | } -------------------------------------------------------------------------------- /codes/Chapter 3/MyQueue.cpp: -------------------------------------------------------------------------------- 1 | class MyQueue { 2 | public: 3 | MyQueue(int size = 20) { 4 | _pqueue = new int[size]; 5 | _front = _rear = 0; 6 | _size = size; 7 | } 8 | 9 | ~MyQueue() { 10 | delete[] _pqueue; 11 | _pqueue = nullptr; 12 | } 13 | 14 | MyQueue(const MyQueue &other) { 15 | _size = other._size; 16 | _front = other._front; 17 | _rear = other._rear; 18 | _pqueue = new int[_size]; 19 | for (int i = _front; i != _rear; i = (i + 1) % _size) { 20 | _pqueue[i] = other._pqueue[i]; 21 | } 22 | } 23 | 24 | MyQueue &operator=(const MyQueue &other) { 25 | if (this == &other) return *this; 26 | 27 | delete[] _pqueue; 28 | 29 | _size = other._size; 30 | _front = other._front; 31 | _rear = other._rear; 32 | _pqueue = new int[_size]; 33 | for (int i = _front; i != _rear; i = (i + 1) % _size) { 34 | _pqueue[i] = other._pqueue[i]; 35 | } 36 | 37 | return *this; 38 | } 39 | 40 | void push(int val) { 41 | if (full()) resize(); 42 | _pqueue[_rear] = val; 43 | _rear = (_rear + 1) % _size; 44 | } 45 | 46 | void pop() { 47 | if (empty()) return; 48 | _front = (_front + 1) % _size; 49 | } 50 | 51 | int top() { return _pqueue[_front]; } 52 | 53 | bool full() { return (_rear + 1) % _size == _front; } 54 | 55 | bool empty() { return _front == _rear; } 56 | 57 | private: 58 | int *_pqueue; 59 | int _front; 60 | int _rear; 61 | int _size; 62 | 63 | void resize() { 64 | int *ptmp = new int[2 * _size]; 65 | int index = 0; 66 | for (int i = _front; i != _rear; i = (i + 1) % _size) { 67 | ptmp[index++] = _pqueue[i]; 68 | } 69 | delete[] _pqueue; 70 | _pqueue = ptmp; 71 | _front = 0; 72 | _rear = index; 73 | _size *= 2; 74 | } 75 | }; -------------------------------------------------------------------------------- /codes/Chapter 3/MyStack.cpp: -------------------------------------------------------------------------------- 1 | class MyStack { 2 | public: 3 | MyStack(int size = 10) { 4 | _pstack = new int[size]; 5 | _top = -1; 6 | _size = size; 7 | } 8 | 9 | ~MyStack() { 10 | delete[] _pstack; 11 | _pstack = nullptr; 12 | } 13 | 14 | MyStack(const MyStack &other) { 15 | _pstack = new int[other._size]; 16 | for (int i = 0; i < _size; i++) { 17 | _pstack[i] = other._pstack[i]; 18 | } 19 | _top = other._top; 20 | _size = other._size; 21 | } 22 | 23 | MyStack &operator=(const MyStack &other) { 24 | if (this == &other) return *this; 25 | // 1. Release current memory 26 | delete[] _pstack; 27 | // 2. Allocate new memory 28 | _pstack = new int[other._size]; 29 | for (int i = 0; i < _size; i++) { 30 | _pstack[i] = other._pstack[i]; 31 | } 32 | _top = other._top; 33 | _size = other._size; 34 | 35 | return *this; 36 | } 37 | 38 | void push(int val) { 39 | if (full()) resize(); 40 | _pstack[++_top] = val; 41 | } 42 | 43 | void pop() { 44 | if (empty()) return; 45 | --_top; 46 | } 47 | 48 | int top() { return _pstack[_top]; } 49 | 50 | bool empty() { return _top == -1; } 51 | 52 | bool full() { return _top == _size - 1; } 53 | 54 | private: 55 | int *_pstack; 56 | int _top; 57 | int _size; 58 | 59 | void resize() { 60 | int *ptmp = new int[_size * 2]; 61 | for (int i = 0; i < _size; ++i) { 62 | ptmp[i] = _pstack[i]; 63 | } 64 | delete[] _pstack; 65 | _pstack = ptmp; 66 | _size *= 2; 67 | } 68 | }; -------------------------------------------------------------------------------- /codes/Chapter 3/MyString.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class MyString { 4 | public: 5 | MyString(const char *str = nullptr) { 6 | if (str != nullptr) { 7 | m_data = new char[strlen(str) + 1]; 8 | strcpy(this->m_data, str); 9 | } else { 10 | m_data = new char[1]; 11 | *m_data = '\0'; 12 | } 13 | } 14 | 15 | MyString(const MyString &other) { 16 | m_data = new char[strlen(other.m_data) + 1]; 17 | strcpy(m_data, other.m_data); 18 | } 19 | 20 | ~MyString() { 21 | delete[] m_data; 22 | m_data = nullptr; 23 | } 24 | 25 | MyString &operator=(const MyString &other) { 26 | if (this == &other) return *this; 27 | 28 | delete[] m_data; 29 | m_data = new char[strlen(other.m_data) + 1]; 30 | strcpy(m_data, other.m_data); 31 | 32 | return *this; 33 | } 34 | 35 | private: 36 | char *m_data; 37 | }; -------------------------------------------------------------------------------- /codes/Chapter 4/MyStack.cpp: -------------------------------------------------------------------------------- 1 | template 2 | class MyStack { 3 | public: 4 | MyStack(int size = 10) : _pstack(new T[size]), _top(0), _size(size) {} 5 | 6 | ~MyStack() { 7 | delete[] _pstack; 8 | _pstack = nullptr; 9 | } 10 | 11 | MyStack(const MyStack &other) : _top(other.top), _size(other._size) { 12 | _pstack = new T[_size]; 13 | for (int i = 0; i < _top; i++) { 14 | _pstack[i] = other._pstack[i]; 15 | } 16 | } 17 | 18 | MyStack &operator=(const MyStack &other) { 19 | if (*this == other) return *this; 20 | delete[] _pstack; 21 | _top = other.top; 22 | _size = other._size; 23 | _pstack = new T[_size]; 24 | for (int i = 0; i < _top; i++) { 25 | _pstack[i] = other._pstack[i]; 26 | } 27 | return *this; 28 | } 29 | 30 | void push(const T &val) { 31 | if (full()) resize(); 32 | _pstack[_top++] = val; 33 | } 34 | 35 | void pop() { 36 | if (!empty()) --_top; 37 | } 38 | 39 | T top() const { return _pstack[_top - 1]; } 40 | 41 | bool full() const { return _top == _size; } 42 | 43 | bool empty() const { return _top == 0; } 44 | 45 | private: 46 | T *_pstack; 47 | int _top; 48 | int _size; 49 | 50 | void resize() { 51 | T *tmp = new T[_size * 2]; 52 | for (int i = 0; i < _top; i++) { 53 | tmp[i] = _pstack[i]; 54 | } 55 | delete[] _pstack; 56 | _pstack = tmp; 57 | _size *= 2; 58 | } 59 | }; -------------------------------------------------------------------------------- /codes/Chapter 4/MyVector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class Allocator { 5 | public: 6 | T *allocate(size_t size) { return (T *)malloc(sizeof(T) * size); } 7 | 8 | void deallocate(void *p) { free(p); } 9 | 10 | void construct(T *p, const T &val) { new (p) T(val); } 11 | 12 | void destroy(T *p) { p->~T(); } 13 | }; 14 | 15 | template > 16 | class MyVector { 17 | public: 18 | MyVector(int size = 10) { 19 | //_first = new T[size]; 20 | _first = _allocator.allocate(size); 21 | _last = _first; 22 | _end = _first + size; 23 | } 24 | 25 | ~MyVector() { 26 | // delete[] _first; 27 | for (T *p = _first; p != _last; p++) { 28 | _allocator.destroy(p); 29 | } 30 | _allocator.deallocate(_first); 31 | _first = _last = _end = nullptr; 32 | } 33 | 34 | MyVector(const MyVector &other) { 35 | int size = other._end - other._first; 36 | //_first = new T[size]; 37 | _first = _allocator.allocate(size); 38 | int len = other._last - other._first; 39 | for (int i = 0; i < len; i++) { 40 | //_first[i] = other._first[i]; 41 | _allocator.construct(_first + i, other._first[i]); 42 | } 43 | _last = _first + len; 44 | _end = _first + size; 45 | } 46 | 47 | MyVector &operator=(const MyVector &other) { 48 | if (*this == other) return *this; 49 | // delete[] _first; 50 | for (T *p = _first; p != _last; p++) { 51 | _allocator.destroy(p); 52 | } 53 | //_first = new T[size]; 54 | _first = _allocator.allocate(size); 55 | int len = other._last - other._first; 56 | for (int i = 0; i < len; i++) { 57 | //_first[i] = other._first[i]; 58 | _allocator.construct(_first + i, other._first[i]); 59 | } 60 | _last = _first + len; 61 | _end = _first + size; 62 | return *this; 63 | } 64 | 65 | void push_back(T &val) { 66 | if (full()) expand(); 67 | //*_last++ = val; 68 | _allocator.construct(_last, val); 69 | _last++; 70 | } 71 | 72 | void pop_back() { 73 | if (!empty()) { 74 | //--_last; 75 | _last--; 76 | _allocator.destroy(_last); 77 | } 78 | } 79 | 80 | T &back() const { return *(_last - 1); } 81 | 82 | bool full() const { return _last == _end; } 83 | 84 | bool empty() const { return _last == _first; } 85 | 86 | int size() const { return _last - _first; } 87 | 88 | private: 89 | T *_first; // Start point 90 | T *_last; // One step after the last valid element 91 | T *_end; // One step after the last element in the space 92 | Alloc _allocator; 93 | 94 | void expand() { 95 | int size = _last - _first; 96 | // T *tmp = T[2 * size]; 97 | T *tmp = _allocator.allocate(2 * size); 98 | for (int i = 0; i < size; i++) { 99 | // tmp[i] = _first[i]; 100 | _allocator.construct(tmp + i, _first[i]); 101 | } 102 | // delete[] _first; 103 | for (T *p = _first; p != _last; p++) { 104 | _allocator.destroy(p); 105 | } 106 | _allocator.deallocate(_first); 107 | _first = tmp; 108 | _last = _first + size; 109 | _end = _first + size * 2; 110 | } 111 | }; -------------------------------------------------------------------------------- /codes/Chapter 5/MyComplex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class MyComplex { 4 | public: 5 | MyComplex(int r = 0, int i = 0) : mreal(r), mimage(i) {} 6 | 7 | MyComplex operator+(const MyComplex &other) { 8 | return MyComplex(this->mreal + other.mreal, this->mimage + other.mimage); 9 | } 10 | 11 | MyComplex operator++(int) { return MyComplex(mreal++, mimage++); } 12 | 13 | MyComplex &operator++() { 14 | mreal += 1; 15 | mimage += 1; 16 | return *this; 17 | } 18 | 19 | void operator+=(const MyComplex &other) { 20 | mreal += other.mreal; 21 | mimage += other.mimage; 22 | } 23 | 24 | private: 25 | int mreal; 26 | int mimage; 27 | friend MyComplex operator+(const MyComplex &c1, const MyComplex &c2); 28 | friend std::ostream &operator<<(std::ostream &out, const MyComplex &c); 29 | friend std::istream &operator>>(std::istream &in, MyComplex &c); 30 | }; 31 | 32 | MyComplex operator+(const MyComplex &c1, const MyComplex &c2) { 33 | return MyComplex(c1.mreal + c2.mreal, c1.mimage + c2.mimage); 34 | } 35 | 36 | std::ostream &operator<<(std::ostream &out, const MyComplex &c) { 37 | return out << c.mreal << "+" << c.mimage << "i"; 38 | } 39 | 40 | std::istream &operator>>(std::istream &in, MyComplex &c) { 41 | return in >> c.mreal >> c.mimage; 42 | } 43 | 44 | int main() { 45 | MyComplex c1; 46 | MyComplex c2; 47 | std::cin >> c1 >> c2; 48 | std::cout << c1 << " " << c2 << std::endl; 49 | return 0; 50 | } -------------------------------------------------------------------------------- /codes/Chapter 5/MyQueue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | class MyQueue { 5 | public: 6 | MyQueue() { _front = _rear = new QueueItem(); } 7 | 8 | ~MyQueue() { 9 | QueueItem *cur = _front; 10 | while (cur != nullptr) { 11 | _front = _front->_next; 12 | delete cur; 13 | cur = _front; 14 | } 15 | } 16 | 17 | void push(const T &val) { 18 | QueueItem *item = new QueueItem(val); 19 | _rear->_next = item; 20 | _rear = item; 21 | } 22 | 23 | void pop() { 24 | if (empty()) return; 25 | QueueItem *first = _front->_next; 26 | _front->_next = first->_next; 27 | if (_front->_next == nullptr) { 28 | _rear = _front; 29 | } 30 | delete first; 31 | } 32 | 33 | T front() const { return _front->_next->_data; } 34 | 35 | bool empty() const { return _front == _rear; } 36 | 37 | private: 38 | struct QueueItem { 39 | QueueItem(T data = T()) : _data(data), _next(nullptr) {} 40 | 41 | void *operator new(size_t size) { 42 | if (_pool == nullptr) { 43 | _pool = (QueueItem *)new char[POOL_ITEM_SIZE * sizeof(QueueItem)]; 44 | QueueItem *p = _pool; 45 | for (; p < _pool + POOL_ITEM_SIZE; ++p) { 46 | p->_next = p + 1; 47 | } 48 | p->_next = nullptr; 49 | } 50 | QueueItem *p = _pool; 51 | _pool = _pool->_next; 52 | return p; 53 | } 54 | 55 | void operator delete(void *ptr) { 56 | QueueItem *p = (QueueItem *)ptr; 57 | p->_next = _pool; 58 | _pool = p; 59 | } 60 | 61 | T _data; 62 | QueueItem *_next; 63 | static const int POOL_ITEM_SIZE = 10000; 64 | static QueueItem *_pool; 65 | }; 66 | 67 | QueueItem *_front; 68 | QueueItem *_rear; 69 | }; 70 | 71 | template 72 | typename MyQueue::QueueItem *MyQueue::QueueItem::_pool = nullptr; -------------------------------------------------------------------------------- /codes/Chapter 5/MyString.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class MyString { 6 | public: 7 | MyString(const char *p = nullptr) { 8 | if (p != nullptr) { 9 | _pstr = new char[strlen(p) + 1]; 10 | strcpy(_pstr, p); 11 | } else { 12 | _pstr = new char[1]; 13 | *_pstr = '\0'; 14 | } 15 | } 16 | 17 | ~MyString() { 18 | delete _pstr; 19 | _pstr = nullptr; 20 | } 21 | 22 | MyString(const MyString &other) { 23 | _pstr = new char[strlen(other._pstr) + 1]; 24 | strcpy(_pstr, other._pstr); 25 | } 26 | 27 | MyString &operator=(const MyString &other) { 28 | if (this == &other) return *this; 29 | delete[] _pstr; 30 | _pstr = new char[strlen(other._pstr) + 1]; 31 | strcpy(_pstr, other._pstr); 32 | return *this; 33 | } 34 | 35 | bool operator>(const MyString &other) const { 36 | return strcmp(_pstr, other._pstr) > 0; 37 | } 38 | 39 | bool operator<(const MyString &other) const { 40 | return strcmp(_pstr, other._pstr) < 0; 41 | } 42 | 43 | bool operator==(const MyString &other) const { 44 | return strcmp(_pstr, other._pstr) == 0; 45 | } 46 | 47 | int length() const { return strlen(_pstr); } 48 | 49 | char &operator[](int index) { return _pstr[index]; } 50 | 51 | const char &operator[](int index) const { return _pstr[index]; } 52 | 53 | const char *c_str() const { return _pstr; } 54 | 55 | class iterator { 56 | public: 57 | iterator(char *p = nullptr) : _p(p) {} 58 | 59 | bool operator!=(const iterator &other) { return _p != other._p; } 60 | 61 | void operator++() { ++_p; } 62 | 63 | char &operator*() { return *_p; } 64 | 65 | private: 66 | char *_p; 67 | }; 68 | 69 | iterator begin() { return iterator(_pstr); } 70 | 71 | iterator end() { return iterator(_pstr + length()); } 72 | 73 | private: 74 | char *_pstr; 75 | friend std::ostream &operator<<(std::ostream &out, const MyString s); 76 | friend MyString operator+(const MyString &s1, const MyString &s2); 77 | }; 78 | 79 | MyString operator+(const MyString &s1, const MyString &s2) { 80 | MyString tmp; 81 | tmp._pstr = new char[strlen(s1._pstr) + strlen(s2._pstr) + 1]; 82 | strcpy(tmp._pstr, s1._pstr); 83 | strcat(tmp._pstr, s2._pstr); 84 | return tmp; 85 | } 86 | 87 | std::ostream &operator<<(std::ostream &out, const MyString s) { 88 | return out << s._pstr; 89 | } -------------------------------------------------------------------------------- /codes/Chapter 9/MyString.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class MyString { 6 | public: 7 | MyString(const char *p = nullptr) { 8 | if (p != nullptr) { 9 | _pstr = new char[strlen(p) + 1]; 10 | strcpy(_pstr, p); 11 | } else { 12 | _pstr = new char[1]; 13 | *_pstr = '\0'; 14 | } 15 | } 16 | 17 | ~MyString() { 18 | delete _pstr; 19 | _pstr = nullptr; 20 | } 21 | 22 | MyString(const MyString &other) { 23 | _pstr = new char[strlen(other._pstr) + 1]; 24 | strcpy(_pstr, other._pstr); 25 | } 26 | 27 | MyString(MyString &&other) { 28 | _pstr = other._ptr; 29 | other._pstr = nullptr; 30 | } 31 | 32 | MyString &operator=(const MyString &other) { 33 | if (this == &other) return *this; 34 | delete[] _pstr; 35 | _pstr = new char[strlen(other._pstr) + 1]; 36 | strcpy(_pstr, other._pstr); 37 | return *this; 38 | } 39 | 40 | MyString &operator=(MyString &&other) { 41 | if (this == &other) return *this; 42 | delete[] mptr; 43 | _pstr = other._pstr; 44 | other._pstr = null; 45 | return *this; 46 | } 47 | 48 | bool operator>(const MyString &other) const { 49 | return strcmp(_pstr, other._pstr) > 0; 50 | } 51 | 52 | bool operator<(const MyString &other) const { 53 | return strcmp(_pstr, other._pstr) < 0; 54 | } 55 | 56 | bool operator==(const MyString &other) const { 57 | return strcmp(_pstr, other._pstr) == 0; 58 | } 59 | 60 | int length() const { return strlen(_pstr); } 61 | 62 | char &operator[](int index) { return _pstr[index]; } 63 | 64 | const char &operator[](int index) const { return _pstr[index]; } 65 | 66 | const char *c_str() const { return _pstr; } 67 | 68 | class iterator { 69 | public: 70 | iterator(char *p = nullptr) : _p(p) {} 71 | 72 | bool operator!=(const iterator &other) { return _p != other._p; } 73 | 74 | void operator++() { ++_p; } 75 | 76 | char &operator*() { return *_p; } 77 | 78 | private: 79 | char *_p; 80 | }; 81 | 82 | iterator begin() { return iterator(_pstr); } 83 | 84 | iterator end() { return iterator(_pstr + length()); } 85 | 86 | private: 87 | char *_pstr; 88 | friend std::ostream &operator<<(std::ostream &out, const MyString s); 89 | friend MyString operator+(const MyString &s1, const MyString &s2); 90 | }; 91 | 92 | MyString operator+(const MyString &s1, const MyString &s2) { 93 | MyString tmp; 94 | tmp._pstr = new char[strlen(s1._pstr) + strlen(s2._pstr) + 1]; 95 | strcpy(tmp._pstr, s1._pstr); 96 | strcat(tmp._pstr, s2._pstr); 97 | return tmp; 98 | } 99 | 100 | std::ostream &operator<<(std::ostream &out, const MyString s) { 101 | return out << s._pstr; 102 | } --------------------------------------------------------------------------------