Table of Contents for
Mastering C++ Programming

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Mastering C++ Programming by Jeganathan Swaminathan Published by Packt Publishing, 2017
  1. Mastering C++ Programming
  2. Title Page
  3. Copyright
  4. Mastering C++ Programming
  5. Credits
  6. About the Author
  7. About the Reviewer
  8. www.PacktPub.com
  9. Why subscribe?
  10. Customer Feedback
  11. Dedication
  12. Table of Contents
  13. Preface
  14. What this book covers
  15. What you need for this book
  16. Who this book is for
  17. Conventions
  18. Reader feedback
  19. Customer support
  20. Downloading the example code
  21. Errata
  22. Piracy
  23. Questions
  24. C++17 Features
  25. C++17 background
  26. What's new in C++17?
  27. What features are deprecated or removed in C++17?
  28. Key features in C++17
  29. Easier nested namespace syntax
  30. New rules for type auto-detection from braced initializer list 
  31. Simplified static_assert 
  32. The std::invoke( ) method
  33. Structured binding
  34. If and Switch local scoped variables
  35. Template type auto-deduction for class templates
  36. Inline variables
  37. Summary
  38. Standard Template Library
  39. The Standard Template Library architecture
  40. Algorithms
  41. Iterators
  42. Containers
  43. Functors
  44. Sequence containers
  45. Array
  46. Code walkthrough
  47. Commonly used APIs in an array
  48. Vector 
  49. Code walkthrough
  50. Commonly used vector APIs
  51. Code walkthrough
  52. Pitfalls of a vector
  53. List 
  54. Commonly used APIs in a list
  55. Forward list
  56. Code walkthrough
  57. Commonly used APIs in a forward_list container
  58. Deque
  59. Commonly used APIs in a deque
  60. Associative containers
  61. Set
  62. Code walkthrough
  63. Commonly used APIs in a set
  64. Map
  65. Code walkthrough
  66. Commonly used APIs in a map
  67. Multiset
  68. Multimap
  69. Unordered sets
  70. Unordered maps
  71. Unordered multisets
  72. Unordered multimaps
  73. Container adapters
  74. Stack
  75. Commonly used APIs in a stack
  76. Queue
  77. Commonly used APIs in a queue
  78. Priority queue
  79. Commonly used APIs in a priority queue
  80. Summary
  81. Template Programming
  82. Generic programming
  83. Function templates
  84. Code walkthrough
  85. Overloading function templates
  86. Code walkthrough
  87. Class template
  88. Code walkthrough
  89. Explicit class specializations
  90. Code walkthrough
  91. Partial template specialization
  92. Summary
  93. Smart Pointers
  94. Memory management
  95. Issues with raw pointers
  96. Smart pointers
  97. auto_ptr
  98. Code walkthrough - Part 1
  99. Code walkthrough - Part 2
  100. unique_ptr
  101. Code walkthrough
  102. shared_ptr
  103. Code walkthrough
  104. weak_ptr
  105. Circular dependency
  106. Summary
  107. Developing GUI Applications in C++
  108. Qt 
  109. Installing Qt 5.7.0 in Ubuntu 16.04
  110. Qt Core
  111. Writing our first Qt console application
  112. Qt Widgets
  113. Writing our first Qt GUI application
  114. Layouts
  115. Writing a GUI application with a horizontal layout
  116. Writing a GUI application with a vertical layout
  117. Writing a GUI application with a box layout
  118. Writing a GUI application with a grid layout
  119. Signals and slots
  120. Using stacked layout in Qt applications
  121. Writing a simple math application combining multiple layouts
  122. Summary
  123. Multithreaded Programming and Inter-Process Communication
  124. Introduction to POSIX pthreads
  125. Creating threads with the pthreads library
  126. How to compile and run
  127. Does C++ support threads natively?
  128. How to write a multithreaded application using the native C++ thread feature
  129. How to compile and run
  130. Using std::thread in an object-oriented fashion
  131. How to compile and run
  132. What did you learn?
  133. Synchronizing threads
  134. What would happen if threads weren't synchronized?
  135. How to compile and run
  136. Let's use mutex
  137. How to compile and run
  138. What is a deadlock?
  139. How to compile and run
  140. What did you learn?
  141. Shared mutex
  142. Conditional variable
  143. How to compile and run
  144. What did you learn?
  145. Semaphore
  146. Concurrency
  147. How to compile and run
  148. Asynchronous message passing using the concurrency support library
  149. How to compile and run
  150. Concurrency tasks
  151. How to compile and run
  152. Using tasks with a thread support library
  153. How to compile and run
  154. Binding the thread procedure and its input to packaged_task 
  155. How to compile and run
  156. Exception handling with the concurrency library
  157. How to compile and run
  158. What did you learn?
  159. Summary
  160. Test-Driven Development
  161. TDD
  162. Common myths and questions around TDD
  163. Does it take more efforts for a developer to write a unit test? 
  164. Is code coverage metrics good or bad?
  165. Does TDD work for complex legacy projects? 
  166. Is TDD even applicable for embedded or products that involve hardware?
  167. Unit testing frameworks for C++
  168. Google test framework
  169. Installing Google test framework on Ubuntu
  170. How to build google test and mock together as one single static library without installing?
  171. Writing our first test case using the Google test framework
  172. Using Google test framework in Visual Studio IDE
  173. TDD in action
  174. Testing a piece of legacy code that has dependency
  175. Summary
  176. Behavior-Driven Development
  177. Behavior-driven development
  178. TDD versus BDD
  179. C++ BDD frameworks
  180. The Gherkin language
  181. Installing cucumber-cpp in Ubuntu
  182. Installing the cucumber-cpp framework prerequisite software
  183. Building and executing the test cases
  184. Feature file
  185. Spoken languages supported by Gherkin
  186. The recommended cucumber-cpp project folder structure
  187. Writing our first Cucumber test case
  188. Integrating our project in cucumber-cpp CMakeLists.txt
  189. Executing our test case
  190. Dry running your cucumber test cases
  191. BDD - a test-first development approach
  192. Let's build and run our BDD test case
  193. It's testing time!
  194. Summary
  195. Debugging Techniques
  196. Effective debugging
  197. Debugging strategies
  198. Debugging tools
  199. Debugging your application using GDB
  200. GDB commands quick reference
  201. Debugging memory leaks with Valgrind
  202. The Memcheck tool
  203. Detecting memory access outside the boundary of an array
  204. Detecting memory access to already released memory locations
  205. Detecting uninitialized memory access
  206. Detecting memory leaks
  207. Fixing the memory leaks
  208. Mismatched use of new and free or malloc and delete
  209. Summary
  210. Code Smells and Clean Code Practices
  211. Code refactoring
  212. Code smell
  213. What is agile?
  214. SOLID design principle
  215. Single responsibility principle
  216. Open closed principle
  217. Liskov substitution principle
  218. Interface segregation
  219. Dependency inversion
  220. Code smell
  221. Comment smell
  222. Long method
  223. Long parameter list
  224. Duplicate code
  225. Conditional complexity
  226. Large class
  227. Dead code
  228. Primitive obsession
  229. Data class
  230. Feature envy
  231. Summary

Let's use mutex

Now, let's refactor the threadProc function and synchronize the critical section that modifies and accesses the balance. We need a locking mechanism that will only allow one thread to either read or write the balance. The C++ thread support library offers an apt lock called mutex. The mutex lock is an exclusive lock that will only allow one thread to operate the critical section code within the same process boundary. Until the thread that has acquired the lock releases the mutex lock, all other threads will have to wait for their turn. Once a thread acquires the mutex lock, the thread can safely access the shared resource.   

The main.cpp file can be refactored as follows; the changes are highlighted in bold:

#include <iostream>
#include <thread>
#include <mutex>
#include "Account.h"
using namespace std;

enum ThreadType {
DEPOSITOR,
WITHDRAWER
};

mutex locker;

Account account(5000.00);

void threadProc ( ThreadType typeOfThread ) {

while ( 1 ) {
switch ( typeOfThread ) {
case DEPOSITOR: {

locker.lock();

cout << "Account balance before the deposit is "
<< account.getBalance() << endl;

account.deposit( 2000.00 );

cout << "Account balance after deposit is "
<< account.getBalance() << endl;

locker.unlock();
this_thread::sleep_for( 1s );
}
break;

case WITHDRAWER: {

locker.lock();

cout << "Account balance before withdrawing is "
<< account.getBalance() << endl;

account.deposit( 1000.00 );
cout << "Account balance after withdrawing is "
<< account.getBalance() << endl;

locker.unlock();
this_thread::sleep_for( 1s );
}
break;
}
}
}

int main( ) {
thread depositor ( threadProc, ThreadType::DEPOSITOR );
thread withdrawer ( threadProc, ThreadType::WITHDRAWER );

depositor.join();
withdrawer.join();

return 0;
}

You may have noticed that the mutex is declared in the global scope. Ideally, we could have declared the mutex inside a class as a static member as opposed to a global variable. As all the threads are supposed to be synchronized by the same mutex, ensure that you use either a global mutex lock or a static mutex lock as a class member.  

The refactored threadProc in main.cpp source file looks as follows; the changes are highlighted in bold:

void threadProc ( ThreadType typeOfThread ) {

while ( 1 ) {
switch ( typeOfThread ) {
case DEPOSITOR: {

locker.lock();

cout << "Account balance before the deposit is "
<< account.getBalance() << endl;

account.deposit( 2000.00 );

cout << "Account balance after deposit is "
<< account.getBalance() << endl;

locker.unlock();
this_thread::sleep_for( 1s );
}
break;

case WITHDRAWER: {

locker.lock();

cout << "Account balance before withdrawing is "
<< account.getBalance() << endl;

account.deposit( 1000.00 );
cout << "Account balance after withdrawing is "
<< account.getBalance() << endl;

locker.unlock();
this_thread::sleep_for( 1s );
}
break;
}
}
}

The code that is wrapped between lock() and unlock() is the critical section that is synchronized by the mutex lock. 

As you can see, there are two critical section blocks in the threadProc function, so it is important to understand that only one thread can enter the critical section. For instance, if the depositor thread has entered its critical section, then the withdrawal thread has to wait until the depositor thread releases the lock and vice versa.


Technically speaking, we could replace all the raw lock() and unlock() mutex methods with lock_guard as this ensures the mutex is always unlocked even if the critical section block of the code throws an exception. This will avoid starving and deadlock scenarios.

It is time to check the output of our refactored program:

Great, did you check the balance reported by DEPOSITOR and WITHDRAWER threads? Yep, they are always consistent, aren't they? Yes, the output confirms that the code is synchronized and it is thread-safe now.  

Though our code is functionally correct, there is room for improvement. Let's refactor the code to make it object-oriented and efficient.  

Let's reuse the Thread class and abstract all the thread-related stuff inside the Thread class and get rid of the global variables and threadProc.

To start with, let's observe the refactored Account.h header, as follows: 

#ifndef __ACCOUNT_H
#define __ACCOUNT_H

#include <iostream>
using namespace std;

class Account {
private:
double balance;
public:
Account( double balance );
double getBalance();
void deposit(double amount);
void withdraw(double amount);
};

#endif

As you can see, the Account.h header hasn't changed as it already looks clean.

The respective Account.cpp source file looks as follows:  

#include "Account.h"

Account::Account(double balance) {
this->balance = balance;
}

double Account::getBalance() {
return balance;
}

void Account::withdraw(double amount) {
if ( balance < amount ) {
cout << "Insufficient balance, withdraw denied." << endl;
return;
}

balance = balance - amount;
}

void Account::deposit(double amount) {
balance = balance + amount;
}

It is better if the Account class is separated from the thread-related functionalities to keep things neat. Also, let's understand how the Thread class that we wrote could be refactored to use the mutex synchronization mechanism as shown ahead:

#ifndef __THREAD_H
#define __THREAD_H

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
#include "Account.h"

enum ThreadType {
DEPOSITOR,
WITHDRAWER
};

class Thread {
private:
thread *pThread;
Account *pAccount;
static mutex locker;
ThreadType threadType;
bool stopped;
void run();
public:
Thread(Account *pAccount, ThreadType typeOfThread);
~Thread();
void start();
void stop();
void join();
void detach();
};

#endif

In the Thread.h header file shown previously, a couple of changes are done as part of refactoring. As we would like to synchronize the threads using a mutex, the Thread class includes the mutex header of the C++ thread support library. As all the threads are supposed to use the same mutex lock, the mutex instance is declared static. Since all the threads are going to share the same Account object, the Thread class has a pointer to the Account object as opposed to a stack object.

 

The Thread::run() method is the Thread function that we are going to supply to the Thread class constructor of the C++ thread support library. As no one is expected to invoke the run method directly, the run method is declared private. As per our Thread class design, which is similar to Java and Qt, the client code would just invoke the start method; when the OS scheduler gives a green signal to run, the run thread procedure will be called automatically. Actually, there is no magic here since the run method address is registered as a Thread function at the time of creating the thread.

Generally, I prefer to include all the dependent headers in the user-defined header file, and the user-defined source file includes only its own header. This helps organize the headers in one place, and this discipline helps maintain the code cleaner and also improves the overall readability and code maintainability.

The Thread.cpp source can be refactored as follows:

#include "Thread.h"

mutex Thread::locker;

Thread::Thread(Account *pAccount, ThreadType typeOfThread) {
this->pAccount = pAccount;
pThread = NULL;
stopped = false;
threadType = typeOfThread;
}

Thread::~Thread() {
delete pThread;
pThread = NULL;
}

void Thread::run() {
while(1) {
switch ( threadType ) {
case DEPOSITOR:
locker.lock();

cout << "Depositor: current balance is " << pAccount->getBalance() << endl;
pAccount->deposit(2000.00);
cout << "Depositor: post deposit balance is " << pAccount->getBalance() << endl;

locker.unlock();

this_thread::sleep_for(1s);
break;

case WITHDRAWER:
locker.lock();

cout << "Withdrawer: current balance is " <<
pAccount->getBalance() << endl;
pAccount->withdraw(1000.00);
cout << "Withdrawer: post withraw balance is " <<
pAccount->getBalance() << endl;

locker.unlock();

this_thread::sleep_for(1s);
break;
}
}
}

void Thread::start() {
pThread = new thread( &Thread::run, this );
}

void Thread::stop() {
stopped = true;
}

void Thread::join() {
pThread->join();
}

void Thread::detach() {
pThread->detach();
}

The threadProc function that was there in main.cpp has moved inside the Thread class's run method. After all, the main function or the main.cpp source file isn't supposed to have any kind of business logic, hence they are refactored to improve the code quality.

Now let's see how clean is the main.cpp source file after refactoring:

#include "Account.h"
#include "Thread.h"

int main( ) {

Account account(5000.00);

Thread depositor ( &account, ThreadType::DEPOSITOR );
Thread withdrawer ( &account, ThreadType::WITHDRAWER );

depositor.start();
withdrawer.start();

depositor.join();
withdrawer.join();

return 0;
}

The previously shown main() function and the overall main.cpp source file looks short and simple without any nasty complex business logic hanging around.   

C++ supports five types of mutexes, namely mutex, timed_mutex, recursive_mutex, recursive_timed_mutex, and shared_timed_mutex.