- static storage
- thread-local storage
- automatic storage
- dynamic storage
appropriate for data that will be used for the whole life of the program
Open the static directory.
What kind of variable is r ?
Make r static.
What do you observe ?
Initialize m_count to 10 in the resource class.
Make m static and r a static member of manager class.
Create a print() function in manager class which prints Hello manager and acquire the resource.
What do you observe ?
Remove the static member to the resource.
Create a function class which returns a reference to a static resource created in this function.
Use this function when you want to acquire the resource.
A pointer variable is a variable which stores a memory address.
A pointer variable must be assigned to be used.
double d = 0.5;
double* p = &d;
float* p_f = nullptr;
std::cout << d << std::endl;
std::cout << *p << std::endl;
std::cout << p << std::endl;
std::cout << p_f << std::endl;
std::cout << *p_f << std::endl; // Error: segfault
int i = 4;
double* p2 = &i; // Error: cannot convert from int* to double*
int i = 4;
int* pi = &i;
// double* d = (double*)pi;
double* d = reinterpret_cast<double*>(pi);
std::cout << i << std::endl;
std::cout << pi << std::endl;
std::cout << d << std::endl;
std::cout << *pi << std::endl;
std::cout << *d << std::endl;
int* i = new int; // allocates an uninitialized integer
int* i2 = new int(5); // allocates an integer initialized to 5
int* i3 = new int[5]; // allocates an array of 5 integers
delete[] i3; // free the memory used by the array i3
delete i2; // free the memory used by i2
delete i; // free the memory used by i
How to allocate and deallocate properly a double**
with nx rows and ny columns.
struct A
{
double x;
double y;
};
A* a = new A;
std::cout << (*a).x << std::endl;
// or
std::cout << a->x << std::endl;
delete a;
int* ar = new int[10];
// Or
std::vector<int> v(10);
int* ar = v.data();
int* ar2 = ar + 2;
std::cout << *ar2 << std::endl;
std::cout << ar[2] << std::endl;
std::cout << (ar2 - ar) << std::endl;
int* ar3 = ar2 - 1;
std::cout << *ar3 << std::endl;
std::cout << ar[1] << std::endl;
Implement the palindrome algortihm using two pointers.
The string will be stored in an std::string
.
void mean(const std::vector<double>& param)
{
if(param.size() == 0)
{
throw std::runtime_error("param size is 0")
}
else
{
// ...
}
}
try
{
std::vector<double> v(0);
mean(v);
}
catch(std::exception& e)
{
std::cout << "caught exception - " << e.what() << std::endl;
}
Try to compile the example found in the exception directory
and fix it.
void test_resource()
{
resource r;
try
{
r.acquire();
if (...)
{
r.release();
return;
}
r.print_message();
r.release();
}
catch(std::exception& e)
{
std::cout << "exception caught: " << e.what() << std::endl;
r.release();
}
}
What do you think about this code ?
Extract from the talk of Arthur O'Dwyer at CppCon 2019
Extract from the talk of Arthur O'Dwyer at CppCon 2019
void test_resource()
{
resource r;
resource_guard g(r);
r.print_message();
}
Write what resource_guard should do.
class resource_guard
{
public:
resource_guard(resource& r);
~resource_guard();
private:
resource& m_r;
};
resource_guard::resource_guard(resource& r)
: m_r(r)
{
m_r.acquire();
}
resource::~resource_guard()
{
m_r.release();
}
void function() noexcept;
The noexcept keyword
Result* make_computation(class_A& a, class_B& b);
Result* make_computation(class_A& a, class_B& b)
{
Result* r = new Result();
...
return r;
}
or
Result* make_computation(class_A& a, class_B& b)
{
...
return a.get_result(b);
}
The owner of a resource should be readable and obvious when you read the source code.
This makes it clear who is reponsible for the destruction of the data and to avoid potential memory leakage.
Smart pointers help to specify the ownership.
There are two versions:
std::unique_ptr
: unique ownershipstd::shared_ptr
: shared ownershipstd::unique_ptr
class coord
{
public:
coord(double x, double y)
: m_x(x), m_y(y)
{}
void print() const
{
std::cout << "coords(" << m_x << ", " << m_y << ")" << std::endl;
}
private:
double m_x, m_y;
};
std::unique_ptr
int main()
{
auto p_1 = std::unique_ptr<double>(new double(4.));
auto p_c = std::make_unique<coord>(4., 3.);
auto p_a = std::unique_ptr<double[]>(new double[1000]);
p_c->print();
// auto p_2 = p_1; // Error: unique_ptr is not copyable
auto p_2 = std::move(p_1);
std::cout << *p_2 << std::endl;
std::cout << p_1 << std::endl;
}
std::shared_ptr
std::shared_ptr<int> p = new int;
std::shared_ptr<int> p2(new int);
auto p3 = std::make_shared<int>();
void function()
{
auto p = std::make_shared<int>(); // ref_count = 1;
{
std::shared_ptr<int> p2 = p; // ref_count = 2;
// ...
} // p2 destructor is called, ref_count = 1
} // p destructor is called, ref_count = 0, deletes the internal pointer
to be used with caution because it is expensive !