Compile these files with the flags: -fsanitize=address -fsanitize=leak
to check that memory is handled properly.
For example:
g++ -Wfatal-errors -fsanitize=address -fsanitize=leak ex11_709.cpp
Exercise 10 of Chapter 19 on page 709.
Implement a simple unique_ptr
supporting only a constructor, destructor,
–>
, *
, and release()
. In particular, don’t try to implement an assignment
or a copy constructor.
// compile with flags: -Wfatal-errors -fsanitize=address -fsanitize=leak
#include<iostream>
#include<vector>
#include<memory>
using namespace std;
class MyException {};
// Begin: this is what the exercise asks
template<class T> class unique_pointer {
public:
typedef T* pointer;
typedef T element_type;
private:
pointer p;
element_type e;
public:
// Constructors.
unique_pointer() { p=nullptr; };
explicit unique_pointer(pointer _p) : p(_p){};
~unique_pointer() {
cout<<"delete"<<endl;
delete p;
};
pointer get() const { return p; }
T& operator*() const
{
return *get();
}
pointer operator->() const
{
return get();
}
pointer release()
{
pointer _p = get();
p = nullptr;
return _p;
}
};
// End: this is what the exercise asks
// This is a specialization: a different implementation for a template when a specific type is passed as template
// parameter
template<class T> class unique_pointer<T[]> {
public:
typedef T* pointer;
typedef T element_type;
private:
pointer p;
element_type e;
public:
// Constructors.
unique_pointer() { p=nullptr; };
explicit unique_pointer(pointer _p) : p(_p){};
~unique_pointer() {
cout<<"delete []"<<endl;
delete [] p;
}
pointer get() const { return p; }
T& operator[](size_t __i) const
{
return get()[__i];
}
T& operator*() const
{
return *get();
}
pointer operator->() const
{
return get();
}
pointer release()
{
pointer _p = get();
p = nullptr;
return _p;
}
};
vector<int>* make_vec1()
{
unique_pointer<vector<int>> p {new vector<int>}; // allocate on free store
// do something with the container
for (int i=0; i<10; i++)
p->push_back(i);
if (p->size()>=10) throw MyException{};
// if an exception is thrown while the vector<int> is being filled, or if we return prematurely
// from make_vec , the vector<int> is properly destroyed. The p.release() extracts
// the contained pointer (to the vector<int> ) from p so that we can return it, and it
// also makes p hold the nullptr so that destroying p (as is done by the return ) does
// not destroy anything.
return p.release();
};
unique_pointer<vector<int>> make_vec2()
{
unique_pointer<vector<int>> p(new vector<int>); // allocate on free store
// do something with the container
if (true) throw MyException{};
return p;
};
int* make_array1()
{
unique_pointer<int[]> p(new int[10]); // allocate on free store
// do something with the container
for (int i=0; i<10; i++)
p[i]=i;
if (true) throw MyException{};
return p.release();
};
int main (int argc, char **argv) {
try {
vector<int> *pv;
pv=make_vec1();
// no need to delete, the destructor of vector<int> is called when the pointer goes out of scope.
} catch (MyException) {
cerr<<"The object the size of 10 and an exception was thrown before it could be deallocated\n";
cerr<<"but it was correctly deallocated by destructor\n";
}
try {
int *pa;
pa=make_array1();
delete [] pa;
} catch (MyException) {
cerr<<"The object the size of 10 and an exception was thrown before it could be deallocated\n";
cerr<<"but it was correctly deallocated by destructor\n";
}
try {
unique_pointer<vector<int>> pv;
pv=make_vec2();
// unique_pointer destructor will handle the deallocation of pv;
} catch (MyException) {
cerr<<"The object the size of 10 and an exception was thrown before it could be deallocated\n";
cerr<<"but it was correctly deallocated by destructor\n";
}
exit(EXIT_SUCCESS);
}
Exercise 11 of Chapter 19 on page 709.
Design and implement a counted_ptr<T>
that is a type that holds a
pointer to an object of type T
and a pointer to a “use count” (an int
)
shared by all counted pointers to the same object of type T
. The use
count should hold the number of counted pointers pointing to a given
T
. Let the counted_ptr
’s constructor allocate a T
object and a use count
on the free store. Let counted_ptr
’s constructor take an argument to be
used as the initial value of the T
elements. When the last counted_ptr
for a T
is destroyed, counted_ptr
’s destructor should delete the T
. Give
the counted_ptr operations that allow us to use it as a pointer. This is
an example of a “smart pointer” used to ensure that an object doesn’t
get destroyed until after its last user has stopped using it. Write a set
of test cases for counted_ptr
using it as an argument in calls, container
elements, etc.
// compile with flags: -Wfatal-errors -fsanitize=address -fsanitize=leak
#include<iostream>
#include<vector>
using namespace std;
template<class T> class counted_pointer {
public:
typedef T* pointer;
typedef T element_type;
private:
pointer p;
element_type e;
int *use_count;
public:
// Constructors.
counted_pointer() { p=nullptr; }
explicit counted_pointer(pointer _p) : p(_p) {use_count=new int(1);}
~counted_pointer() {
cout<<*use_count<<endl<<flush;
(*use_count)--;
if (*use_count==0)
{
cout<<"delete"<<endl;
delete p;
}
}
int* get_counter() {
return use_count;
}
counted_pointer& operator=(counted_pointer &u)
{
use_count=u.get_counter();
(*use_count)++;
cout<<*use_count<<endl;
p=u.get();
return *this;
}
pointer get() { return p; }
pointer get() const { return p; }
T& operator*() const
{
return *get();
}
pointer operator->() const
{
return get();
}
pointer release()
{
pointer _p = get();
*use_count--;
p = nullptr;
return _p;
}
};
int main (int argc, char **argv) {
//counted_pointer<vector<int>> p;
counted_pointer<vector<int>> p1(new vector<int>);
p1->push_back(2);
p1->push_back(3);
{
counted_pointer<vector<int>> p2;
p2=p1;
//p2 goes out of scope but it does not delete the object
}
// now p1 goes out of scope and since the *use_count is just one it deletes the object.
exit(EXIT_SUCCESS);
}