Here is an example where two threads execute the same function and guard access to std::cout object by locking and unlocking mutexes:
#include
#include
#include
#include
std::mutex m; // will guard std::cout
void myfunction(const std::string& param)
{
for (int i = 0; i < 10; i++)
{
m.lock();
std::cout << "Executing function from a " << param << '
';
m.unlock();
}
}
int main()
{
std::thread t1{ myfunction, "Thread 1" };
std::thread t2{ myfunctiosn, "Thread 2" };
t1.join();
t2.join();
}
We can forget to unlock the mutex manually. A better approach is to use the std::lock_guard function instead. It locks the mutex, and once it goes out of scope, it automatically unlocks the mutex. Example:
#include
#include
#include
#include
std::mutex m; // will guard std::cout
void myfunction(const std::string& param)
{
for (int i = 0; i < 10; i++)
{
std::lock_guard lg(m);
std::cout << "Executing function from a " << param << '
';
} // lock_guard goes out of scope here and unlocks the mutex
}
int main()
{
std::thread t1{ myfunction, "Thread 1" };
std::thread t2{ myfunction, "Thread 2" };
t1.join();
t2.join();
}
11.14 Deleted and Defaulted Functions
If we do not supply a default constructor, the compiler will generate one for us so that we can write:
class MyClass
{
};
int main()
{
MyClass o; // OK, there is an implicitly defined default constructor
}
However, in certain situations, the default constructor will not be implicitly generated. For example, when we define a copy constructor for our class, the default constructor is implicitly deleted. Example:
#include
class MyClass
{
public:
MyClass(const MyClass& other)
{
std::cout << "Copy constructor invoked.";
}
};
int main()
{
MyClass o; // Error, there is no default constructor
}
To force the instantiation of a default, compiler-generated constructor, we provide the =default specifier in its declaration. Example:
#include
class MyClass
{
public:
MyClass() = default; // defaulted member function
MyClass(const MyClass& other)
{
std::cout << "Copy constructor invoked.";
}
};
int main()
{
MyClass o; // Now OK, the defaulted default constructor is there
MyClass o2 = o; // Invoking the copy constructor
}
The =default specifier, when used on a member function, means: whatever the language rules, I want this default member function to be there. I do not want it to be implicitly disabled.
Similarly, if we want to disable a member function from appearing, we use the =delete specifier. To disable the copy constructor and copy assignment, we would write:
#include
class MyClass
{
public:
MyClass()
{
std::cout << "Default constructor invoked.";
}
MyClass(const MyClass& other) = delete; // delete the copy constructor
MyClass& operator=(const MyClass& other) = delete; // delete the copy // assignment operator
};
int main()
{
MyClass o; // OK
MyClass o2 = o; // Error, a call to deleted copy constructor
MyClass o3;
o3 = o; // Error, a call to deleted copy assignment operator
}
These specifiers are mostly used in situations where we want to:
a. force or the instantiation of implicitly defined member functions such as constructors and assignment operators, when we use the =default; expression
b. disable the instantiation of implicitly defined member functions using the =delete; expression
These expressions can also be used on other functions as well.
115 Type Aliases
A type alias is a user-provided name for the existing type. If we want to use a different name for the existing type, we write: