class CRefCounter
{
public:
 CRefCounter() { i_count; }
int iAdd() { return(++i_count); }
int iDec() { return(--i_count); };
int iGet() { return(i_count); }
private:
int i_count;
};//class CRefCounter
The counter object is created in the smart pointer class.
class CMySmartPointer
{
public:
 CMySmartPointer(CSellData *pcPointer)
 {
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
3/8
 pc_pointer = pcPointer;
pc_counter = new CRefCounter();
 pc_counter->iAdd();
 }//CMySmartPointer(CSellData *pcPointer)
 CMySmartPointer(const CMySmartPointer &pcOther)
 {
 pc_pointer = pcOther.pc_pointer;
 pc_counter = pcOther.pc_counter;
 pc_counter->iAdd();
 }//CMySmartPointer(const CMySmartPointer &pcOther)
 ~CMySmartPointer()
 {
 if (pc_counter->iDec() == 0)
 {
 delete pc_pointer;
 delete pc_counter;
 }//if (pc_counter->iDec())
 }//~CMySmartPointer()
CSellData& operator*() { return(*pc_pointer); }
CSellData* operator->() { return(pc_pointer); }
private:
CRefCounter *pc_counter;
CSellData *pc_pointer;
};//class CMySmartPointer
In the case of the above implementation, multiple smart pointers can store the same pointer
and will have the same reference count. However, an error will not occur. The objects pointed
to by pc_counter and pc_pointer will be deleted when the last smart pointer that stores them is
deleted.

„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
4/8
There are situations where we have a statically created object in some function and we want
to pass the value of this object outside. This is a typical situation for operator overloading. For
example:
CNumber CNumber::operator+(CNumber &cNum)
{
CNumber c_result;
/*create result*/
return(c_result);
}//CNumber CNumber::operator+(CNumber &cNum)
The above implementation of the addition operator is convenient because if the c_result
object were dynamically allocated and the operator returned a pointer to CNumber instead of the
value of CNumber, then if the result was not assigned to any variable outside, a leak would
occur. At the same time, if the CNumber class allocates a lot of memory, then deleting c_result
at the end of the operator can be considered wasteful because this object will not be used
anywhere, and the memory it allocated could have been passed on to another object for use.
Instead, the memory will be copied, which is time-consuming.
To handle the above situations, we use move semantics. Consider the example of the class
CTab.
#define DEF_TAB_SIZE 10
class CTab
{
public:
 CTab() { pi_tab = new int[DEF_TAB_SIZE]; i_size = DEF_TAB_SIZE;}
 CTab(const CTab &cOther);
 CTab(CTab &&cOther);
CTab operator=(const CTab &cOther);
 ~CTab();
bool bSetSize(int iNewSize);
int iGetSize() { return(i_size); }
private:
void v_copy(const CTab &cOther);
int *pi_tab;
int i_size;
};//class CTab

„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
5/8
Selected methods of the CTab class.
CTab::CTab(const CTab &cOther)
{
 v_copy(cOther);
 std::cout << "Copy ";
}//CTab::CTab(const CTab &cOther)
CTab::~CTab()
{
if (pi_tab != NULL) delete pi_tab;
 std::cout << "Destr ";
}//CTab::~CTab()
CTab CTab::operator=(const CTab &cOther)
{
if (pi_tab != NULL) delete pi_tab;
 v_copy(cOther);
 std::cout << "op= ";
return(*this);
}//CTab CTab::operator=(const CTab &cOther)
void CTab::v_copy(const CTab &cOther)
{
 pi_tab = new int[cOther.i_size];
 i_size = cOther.i_size;
for (int ii = 0; ii < cOther.i_size; ii++)
 pi_tab[ii] = cOther.pi_tab[ii];
}//void CTab::v_copy(CTab &cOther)
In the traditional way (with copying) we can use the class as in the following program.
CTab cCreateTab()
{
CTab c_result;
 c_result.bSetSize(5);
return(c_result);
}//CTab cCreateTab()
int i_ms_test()
{
CTab c_tab = cCreateTab();
/*DO STH WITH c_tab*/
}//int i_ms_test()
If the compiler does not optimize the code during compilation, the c_tab object will be
created using the copy constructor and the array will be copied. However, we can define a
move constructor with the following content.

„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
6/8
CTab::CTab(CTab &&cOther)
{
 pi_tab = cOther.pi_tab;
 i_size = cOther.i_size;
cOther.pi_tab = NULL;
 std::cout << "MOVE ";
}//CTab::CTab(CTab &&cOther)
Instead of copying the memory, as in the copy constructor, we pass a pointer to the already
allocated array to the new object, and set the pointer to NULL in the old object. This is
important because we don't want the destructor of the old object to free the array's memory.
The array is moved to the new object.
Just declaring a move constructor won't cause it to be used. To do so, you need to use the
std::move function.
CTab cCreateTab()
{
CTab c_result;
 c_result.bSetSize(5);
return(std::move(c_result));
}//CTab cCreateTab()
In the above example, the c_result object is returned by value, but instead of a copy
constructor, a move constructor will be used. Note that when the i_ms_test procedure
executes, the array pointed to by the pi_tab pointer of the c_result object will not be deleted,
but passed to the c_tab object, using the move constructor. At the same time, if the cCreateTab
function were used in the following way, there would be no memory leak, because the move
constructor for the i_ignore_result procedure, whose argument would be the c_result object
from the cCreateTab function, will not be called. Therefore, c_result will delete the array
when its destructor is called.
int i_ignore_result()
{
 cCreateTab();
/*DO STH WITH */
}//int i_ignore_result()

„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
7/8
Task
NOTES:
1. When writing your own program, you may use a different naming convention
than that presented in the task description and examples. However, you should
adhere to some consistent coding conventions in accordance with the course
requirements.
2. You are not allowed to use exceptions in any form (it is only a reminder).
3. You are supposed to use C++98 standard.
4. Starting from this list, you can use smart pointer, but ONLY THOSE that you
have implemented yourself.
1. Using the skills you learned in the previous labs, convert the CMySmartPointer class to
a template class
2. Implement operator= so that one smart pointer can be assigned the value of another.
Remember that if the smart pointer being modified pointed to or stored another pointer
before being assigned, the reference count must be decremented and deallocated if
necessary.
3. Consider what happens when a smart pointer stores a pointer to statically allocated
memory.
4. The move constructor for the tree class (lists 3 and 4, here we will call it CMyTree) is a
convenient mechanism. However, it would be convenient if we could use move
semantics, also in the case of the following code:
CMyTree c_tree;
CMyTree c_oter;
/*initialize c_tree, c_other*/
 c_tree = std::move(c_other);
The default content of the transfer operator (CMyTree operator =( CMyTree &&cOther);)
is empty, so it will not meet the user's expectations. Write a transfer operator content that
is valid for the CMyTree class.
5. Modify the CMyTree class created in Exercises 3 and 4. Program at least operator= and
operator+ for it. Make them return by value. Then program the move operator and the
move constructor (use move semantics). Check how much the number of copies made
decreases with and without move semantics.  do this task