Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

Hi, please help me with this project, I have so many questions I don't even know where to start (also please try to avoid using

Hi, please help me with this project, I have so many questions I don't even know where to start (also please try to avoid using malloc)! Thank you!

For this project you will create your own SmartPtr (Smart Pointer) class. A Smart Pointer serves the purpose of wrapping a set of useful behaviors around a common Raw Pointer, such as:

Automatically handle allocation of Dynamic Memory if necessary, when a SmartPointer object is created.

Automatically handle deallocation of Dynamic Memory if appropriate, when a SmartPointer object lifetime ends.

Provide access to the Dynamic Memory it encapsulates (via the actual Raw Pointer) using the same notation (the same operators) as a Raw Pointer, so that it is exactly as easy to use.

Automatically handle cases such as a) when a Smart Pointer is used to point to the data already allocated by another SmartPointer, and avoid re-allocation, or b) when a SmartPointers lifetime ends but there also exists another SmartPointer pointing to the same data, and avoid deallocating early (understand when the last SmartPointer corresponding to that memory is destroyed, and only then delete the data).

The following header file extract gives the required specifications for the class:

//Necessary preprocessor #define(s)

...

//Necessary include(s)

...

//Class specification

class SmartPtr{

public:

SmartPtr(); //(1)

SmartPtr(DataType *data); //(2)

SmartPtr(const SmartPtr& other ); //(3)

~SmartPtr(); //(4)

SmartPtr& operator=( const SmartPtr& rhs ); //(5)

DataType& operator*(); //(6)

DataType* operator->(); //(7)

private:

size_t * m_refcount; //(8)

DataType * m_ptr; //(9)

};

...

Specifications explained:

You will notice that the Smart Pointer encapsulates a raw pointer m_pt of type DataType (9). (This means that the Smart Pointer works with dynamically allocated DataType objects, but since DataType is a class that you can define yourselves, it is still flexible and modular enough given what C++ practices we know so far). DataType is a Class which is given to you (Header .h & Implementation .cpp files both), and it is simple enough to be considered self-explanatory. The SmartPtr

Class will contain the following private data members:

(9) m_ptr, a DataType Pointer, pointing to the Dynamically Allocated data. These is the Dynamic Memory encapsulated by the SmartPtr class, i.e. the memory that needs to be: a) allocated by the class own methods when appropriate, b) deallocated by the class own methods when appropriate, c) addressable and accessible via the class own methods.

(8) m_refcount, a size_t Pointer, pointing to a dynamically allocated positive integer (size_t) variable. It is a reference-counting helper variable, keeping track of how many SmartPtr objects refer to the same Dynamic Memory behind m_ptr. The value (0,1,2,...) pointed-to by m_refcount denotes how many SmartPointer objects are

currently pointing to the same Dynamically Allocated memory as the one pointed by m_ptr. But m_refcount is not a size_t but a Pointer to a size_t. This is because it needs to be a shared value between different SmartPtr objects, so that when one SmartPtr object updates it, all others can see the change. E.g. when one SmartPtr object gets destroyed, it should update the value pointed-to by m_refcount by decrementing it, to denote that there is one less SmartPtr object alive that points to the Dynamically Memory pointed to by m_ptr and will have the following public member functions:

(1) Default Constructor will:

a) Dynamically Allocate a new DataType object and keep track of its address via m_ptr. b) Dynamically allocate a new size_tvariable and set the value it points-to

to 1 to denote that there exists one SmartPtr object pointing the newly allocated Dynamic Memory behind m_ptr. Remember: This will be assigned to m_refcount and will be shared among any other future SmartPtr objects that will be used to point to the same Dynamic Memory as the one behind m_ptr, so that they all know when it is time to deallocate that memory. c) Right before it returns, it should print out: "SmartPtr Default Constructor for new allocation, RefCount=" where the actual value pointed-to by m_refcount.

(2) Parametrized Constructor will:

take a Pointer to DataType as a parameter. This means that this Constructor takes in already pre-allocated data, and wraps itself around that raw pointer. It will: a) Not perform any Dynamic Allocation since the data Pointer should correspond to pre-allocated data, therefore it will use m_ptr to keep track of that data directly. b) Dynamically allocate a new size_t variable and keep track of it via m_refcount.

Depending on whether the data Pointer passed is NULL or not, the value pointed-to by m_refcount should be set to 0 or 1 to denote that the SmartPtr object does not correspond to valid memory, or that there exists one SmartPtr object pointing the Dynamic Memory behind m_ptr. c) Right before it returns, it should print out: "SmartPtr Parametrized Constructor from data pointer, RefCount=" where the actual value pointed-to by m_refcount.

(3) Copy Constructor will:

take another SmartPtr object as a parameter. This means that this Constructor has access to the pre-allocated data of the other object, and the pre-allocated reference-counting variable too (which will already have a point-to value). a) Not perform any Dynamic Allocation since the other objects m_ptr should correspond to pre-allocated data, therefore it will use m_ptr to keep track of that data directly. b) Bind its m_refcount to the same shared reference-counting variable as the other objects m_refcount when appropriate. Note: Depending on whether the other objects m_ptr is NULL or not, the m_refcount of the newly instantiated SmartPtr should either be newly allocated (and the value it points-to should be initialized to 0), or it should be bound to the other objects m_refcount and the value it points-to should be incremented (++), to denote that there now exists one additional SmartPtr object pointing the Dynamic Memory behind m_ptr. Hint: Generally speaking in this implementation, SmartPtr objects corresponding to the same Dynamic Memory should also be bound to the same *m_refcount object. SmartPtr objects that correspond to no valid Dynamic Memory however (NULL), should each have their own *m_refcount object. c) Right before it returns, it should print out: "SmartPtr Copy Constructor, RefCount=" where the actual value pointed-toby m_refcount.

(4) Destructor will:

a) Decrement the valuepointed-to by m_refcount to denote that one less SmartPtr object is now pointing to the Dynamic Memory behind m_ptr. b) Examine whether the calling SmartPtr objectd (the one whose lifetime is just now expiring, so its Destructor is getting called) is the last one referencing the Dynamic Memory behind m_ptr. To do that, it will have to examine the value pointed-to by m_refcount (after it has been decremented). c) If it is the last one, it should dellocate the Dynamic Memory both behind m_ptr, and the shared variable m_refcount. d) Right before any deallocation happens, it should print out: "SmartPtr Destrcutor, RefCount=" where the actual value pointed-to by m_refcount.

(5) operator= will:

perform assignment from a SmartPtr object. This means that it will: a) First take care of releasing its handle on its own Dynamic Memory. Note: This does not necessarily mean to directly deallocate it, there might be other SmartPtr objects referencing the same Dynamic Memory! Review the description of the Destructor to understand how releasing with respect for other SmartPtr objcets will need to work. b) Then switch to referencing the same values as the other SmartPtr object. This means that both m_ptr and m_refcount will need to be repointed there, and any additional considerations (such as mutating the value pointed-by m_refcount, and how to handle a case where the other object holds a NULL pointed for its Dynamic Memory, etc) should be handled as per the Copy Constructor. c) Right before it returns, it should print out: "SmartPtr Copy Assignment, RefCount=" where the actual value pointed-to by m_refcount.

(6) operator* will:

act in the same way that it is used on Raw Pointers, i.e. it will Dereference the Dynamic Memory Object that is encapsulated within the SmartPtr: When you have a Raw Pointer, dereferencing returns a Reference-to the underlying object:

DataType *data_pt = new DataType; //data_pt is a raw pointer

DataType &data_ref = *( data_pt ); //operator* on a raw pointer

In the same principle, operator* will act as a Smart Pointer Dereference:

SmartPtr data_pt; //data_pt is now a smart pointer

DataType &data_ref = *( data_pt ); //operator* on a smart pointer

and again have the same result (the goal is to maintain the same notation semantics so that the user of a SmartPtr class has near-zero things to learn in order to use it). Hint: Given the SmartPtr class declaration that you already know, where you are also given the return type, it should be straightforward how to implement this method.

(7) operator-> will:

act in the same way that it is used on Raw Pointers, i.e. it will Allow Object Member Access via the Dynamic Memory Pointer that is encapsulated within the SmartPtr: When you have a Raw Pointer, member-access is performed like:

DataType *data_pt = new DataType; //data_pt is a raw pointer

Int intVar = data_pt->GetIntVar(); //operator-> on a raw pointer

In the same principle, operator-> will act as a Member Access via Smart Pointer:

SmartPtr data_pt; //data_pt is now a smart pointer

Int intVar = data_pt->GetIntVar(); //operator-> on a smart pointer

and again have the same result (the goal is to maintain the same notation semantics so that the user of a SmartPtr class has near-zero things to learn in order to use it). Hint: Given the SmartPtr class declaration that you already know, where you are also given the return type, it should be straightforward how to implement this method.

The SmartPtr.h header file should be as per the specifications. The SmartPtr.cpp source file you create will hold the required implementations. You should also create a source file projX.cpp which will be a test driver for your class. The test driver has to demonstrate that your SmartPtr class works as specified:

You should use all the meaningful test cases you can identify to demonstrate the use of all the class methods. The following example is considered as a starting point:

Cout << endl << "Testing SmartPtr Default ctor"<< endl;

SmartPtrsp1; // Default-ctor

sp1->SetIntVal(1);

sp1->SetDoubleVal(0.25);

cout << "Dereference Smart Pointer 1: " << *sp1 << endl;

cout << endl << "Testing SmartPtr Copy ctor" << endl;

SmartPtr sp2 = sp1; // Copy-initalization (Copy-ctor)

sp2->SetIntVal(2);

sp2->SetDoubleVal(0.5);

cout << "Dereference Smart Pointer 1: " << *sp1 << endl;

cout << "Dereference Smart Pointer 2: " << *sp2 << endl;

cout << endl << "Testing SmartPtr Assignment operator" << endl;

SmartPtr sp3;

sp3 = sp1; // Assignment operator

sp3->SetIntVal(4);

sp3->SetDoubleVal(0.0);

cout << "Dereference Smart Pointer 1: " << *sp1 << endl;

cout << "Dereference Smart Pointer 2: " << *sp2 << endl;

cout << "Dereference Smart Pointer 3: " << *sp3 << endl;

cout << endl << "Testing SmartPtr Parametrized ctor with NULLdata" << endl;

SmartPtr spNull( NULL ); // NULL-data initialization

cout << endl << "Testing SmartPtr Copy ctor with NULLdata SmartPtr" << endl;

SmartPtr spNull_cpy( spNull ); // NULL-data copy constructor

cout << endl <<"Testing SmartPtr Assignment with NULLdata SmartPtr"

<< endl;

SmartPtr spNull_assign;

spNull_assign = spNull; // NULL-data assign

cout << endl << "End-of-Scope, Destructors called in reverse order of SmartPtr creation (spNull_assign, spNull_cpy, spNull, sp3, sp2, sp1): "<< endl;

DataType.h:

#ifndef DATATYPE_H_

#define DATATYPE_H_

#include

class DataType{

friend std::ostream& operator<<(std::ostream& os, const DataType& dataType);

friend std::istream& operator>>(std::istream& is, DataType& dataType);

public:

DataType();

DataType(int intVal, double doubleVal);

bool operator==(const DataType& other_dataType) const;

DataType& operator= (const DataType& other_dataType);

int GetIntVal() const;

void SetIntVal(int i);

double GetDoubleVal() const;

void SetDoubleVal(double d);

private:

int m_intVal;

double m_doubleVal;

};

#endif //DATATYPE_H_

DataType.cpp:

#include "DataType.h"

#include

DataType::DataType(){

m_intVal = 0;

m_doubleVal = 0.0;

}

DataType::DataType(int intVal, double doubleVal){

m_intVal = intVal;

m_doubleVal = doubleVal;

}

bool DataType::operator==(const DataType& rhs) const{

return m_intVal==rhs.m_intVal && m_doubleVal==rhs.m_doubleVal;

}

DataType& DataType::operator=(const DataType& rhs){

if (this != &rhs){

m_intVal = rhs.m_intVal;

m_doubleVal = rhs.m_doubleVal;

}

return *this;

}

int DataType::GetIntVal() const{

return m_intVal;

}

void DataType::SetIntVal(int i){

m_intVal = i;

}

double DataType::GetDoubleVal() const{

return m_doubleVal;

}

void DataType::SetDoubleVal(double d){

m_doubleVal = d;

}

std::ostream& operator<<(std::ostream& os, const DataType& dt){

os << "{" << dt.m_intVal << "," << dt.m_doubleVal << "}";

return os;

}

std::istream& operator>>(std::istream& is, DataType& dt){

char in_buf[255];

is >> in_buf;

dt.m_doubleVal = atof(in_buf);

dt.m_intVal = (int)dt.m_doubleVal;

dt.m_doubleVal -= dt.m_intVal;

return is;

}

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Logistics Lifeline Supply Chain Strategies

Authors: Ehsan Sheroy

1st Edition

7419377502, 978-7419377503

More Books

Students also viewed these Databases questions