680 likes | 701 Views
Learn the differences between declaration and definition in programming, constructors, pass by value, deep copy methods, and more. Explore dynamic memory and copy constructors for safe coding practices.
E N D
CSI 1340Introduction to Computer Science II Chapter 6 Lists Plus
Definition vs. Declaration • Declaration: Associates an identifier with a data object, an action (e.g., function), or a data type. Ex: typedef int IDType; void PrintArray(int x[ ], int noElmts); • Definition: A declaration becomes a definition when it binds storage to the identifier Ex: void PrintArray(int x[ ], int noElmts){ ; }
next Chains struct AgeNode { int age; AgeNode* next; }; How do I access the third node? head 2 4 6 8 head->next->next->age
Constructors with new How do you call the constructor function when using new? class Employee { string m_name; // Are these variables private or public? int m_salary; public: Employee( ); Employee(string name, int salary); }; Employee* bob = new Employee(“Bob”, 500); Employee* jane = new Employee; // Calls default constructor
Perilous Pass By Value How can you get in trouble using pass by value?
Referring to Yourself A class instance by any other name is still a class instance - Shakespere What is exactly is the “other” name?
‘B’ ‘C’ ‘L’ ‘T’ ‘V’ ‘Y’ listData What is a Circular Linked List? • A circular linked list is a list in which every node has a successor; the “last” element is succeeded by the “first” element.
Circular Linked List Operations: • Find(item) • Insert(item) • Delete(item)
What is a Doubly Linked List? • A doubly linked list is a list in which each node is linked to both its successor and its predecessor. ‘A’ ‘C’ ‘F’ ‘T’ ‘Z’ listData
Doubly Linked List Operations: • Find(item) • Insert(item) • Delete(item)
Each node contains two pointers template< class ItemType > struct NodeType { ItemType info;// Data member NodeType<ItemType>* back;// Pointer to predecessor NodeType<ItemType>* next; // Pointer to successor }; 3000 ‘A’ NULL .back .info .next
StackType Private data: topPtr What happens . . . • When a function is called that uses pass by value for a class object like our dynamically linked stack? MakeEmpty IsEmpty 20 30 IsFull Push Pop ~StackType
Passing a class object by value // FUNCTION CODE template<class ItemType> void MyFunction( StackType<ItemType> SomeStack ) // Uses pass by value { . . . . }
Pass by value makes a shallow copy StackType<int> MyStack; // CLIENT CODE . . . MyFunction( MyStack ); // function call MyStack SomeStack Private data: topPtr 7000 Private data: 7000 6000 topPtr 7000 20 30 shallow copy
Shallow Copyvs. Deep Copy • A shallow copycopies only the class data members, and does not copy any pointed-to data. • A deep copycopies not only the class data members, but also makes separately stored copies of any pointed-to data.
What’s the difference? • A shallow copyshares the pointed to data with the original class object. • A deep copystores its own copy of the pointed to data at different locations than the data in the original class object.
Making a deep copy MyStack Private data: 7000 6000 topPtr 7000 20 30 SomeStack Private data: 5000 2000 topPtr 5000 20 30 deep copy
Suppose MyFunction Uses Pop // FUNCTION CODE template<class ItemType> void MyFunction( StackType<ItemType> SomeStack ) // Uses pass by value { ItemType item; SomeStack.Pop(item); . . . } WHAT HAPPENS IN THE SHALLOW COPY SCENARIO?
MyStack.topPtris left dangling StackType<int> MyStack; // CLIENT CODE . . . MyFunction( MyStack ); MyStack SomeStack Private data: topPtr 6000 Private data: 7000 6000 topPtr 7000 ? 30 shallow copy
MyStack.topPtris left dangling NOTICE THAT NOT JUST FOR THE SHALLOW COPY, BUT ALSO FOR ACTUAL PARAMETER MyStack, THE DYNAMIC DATA HAS CHANGED! MyStack SomeStack Private data: topPtr 6000 Private data: 7000 6000 topPtr 7000 ? 30 shallow copy
As a result . . . • This default method used for pass by value is not the best way when a data member pointer points to dynamic data. • Instead, you should write what is called acopy constructor, which makes a deep copy of the dynamic data in a different memory location.
Classes that Use Dynamic Memory • Always require the following member functions: • Destructor • Copy constructor • Overloaded assignment operator NOTE: If you do not implement one or more of the above, you MUST document that they are unsafe to use.
More about copy constructors • When there is a copy constructor provided for a class, the copy constructor is used to make copies for pass by value. • You do not call the copy constructor. • Like other constructors, it has no return type. • Because the copy constructor properly defines pass by value for your class, it must use pass by reference in its definition.
Copy Constructor • Copy constructor is a special member function of a class that is implicitly called in these three situations: • passing object parameters by value, • initializing an object variable in a declaration, • returning an object as the return value of a function.
// DYNAMICALLY LINKED IMPLEMENTATION OF STACK template<class ItemType> class StackType { public: StackType( ); // Default constructor. // POST: Stack is created and empty. StackType( const StackType<ItemType>& anotherStack ); // Copy constructor. // Implicitly called for pass by value. . . . ~StackType( ); // Destructor. // POST: Memory for nodes has been deallocated. private: NodeType<ItemType>* topPtr ; };
. . . int main( ) { StackType s; s.Push(85); s.Push(50); StackType t(s); // Makin’ a copy
StackType::StackType(const StackType& source) { topPtr source.topPtr 50 85 NULL
tempPtr StackType::StackType(const StackType& source) { NodeType *tempPtr; topPtr source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr = NULL; topPtr NULL source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr = NULL; if (source.topPtr != NULL) { topPtr NULL source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr = NULL; if (source.topPtr != NULL) { tempPtr = source.topPtr; topPtr NULL source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr = NULL; if (source.topPtr != NULL) { tempPtr = source.topPtr; topPtr = new NodeType; topPtr source.topPtr 50 85 NULL
tempPtr location StackType::StackType(const StackType& source) { NodeType *tempPtr; NodeType *location; topPtr = NULL; if (source.topPtr != NULL) { tempPtr = source.topPtr; // copy first node topPtr = new NodeType; topPtr->info = tempPtr->info; topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { location->next = new NodeType; topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { location->next = new NodeType; location = location->next; topPtr 50 source.topPtr 50 85 NULL
tempPtr location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { location->next = new NodeType; location = location->next; location->info = tempPtr->info; topPtr 50 85 source.topPtr 50 85 NULL
tempPtr NULL location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { location->next = new NodeType; location = location->next; location->info = tempPtr->info; tempPtr = tempPtr->next; } topPtr 50 85 source.topPtr 50 85 NULL
tempPtr NULL location location = topPtr; // copy remaining nodes tempPtr = tempPtr->next; while (tempPtr != NULL) { location->next = new NodeType; location = location->next; location->info = tempPtr->info; tempPtr = tempPtr->next; } location->next = NULL; } } topPtr 50 85 NULL source.topPtr 50 85 NULL
Stack After Using Copy Constructor topPtr 50 85 NULL source.topPtr 50 85 NULL
What about the assignment operator? • The default method used for assignment of class objects makes a shallow copy. • If your class has a data member pointer to dynamic data, you should write a member function to overload the assignment operator to make a deep copy of the dynamic data.
Main.cpp: QueType s; s.Enqueue(‘C’); QueType t; t = s; overloaded assignment operator function is called: #include “Queue.h” // Declares qFront and qRear void QueType::operator=(const QueType& source) // when the member function is entered, t and s are: qFront source.qFront NULL C NULL t s source.qRear qRear NULL
void QueType::operator=(const QueType& source) { // Check for case where a queue is set equal to itself if (source.qFront != qFront) { // Return the queue to the heap (i.e., MakeEmpty) NodeType *remove_ptr; while (qFront != NULL) { remove_ptr = qFront; qFront = qFront->next; delete remove_ptr; } // NOTE: qRear is set to NULL in next section
// Code identical to the copy constructor follows qFront = NULL; qRear = NULL; qFront source.qFront NULL C NULL t s source.qRear qRear NULL
// Code identical to copy constructor follows qFront = NULL; qRear = NULL; // If source is empty, no copying is needed if (source.qFront !=NULL) { qFront source.qFront NULL C NULL t s source.qRear qRear NULL
// Code identical to copy constructor follows qFront = NULL; qRear = NULL; // If source is empty, no copying is needed if (source.qFront !=NULL) { NodeType *insert_ptr; NodeType *temp_ptr; insert_ptr temp_ptr qFront source.qFront NULL C NULL t s source.qRear qRear NULL