adding source code files
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Lab 12 — Garbage Collection & Smart Pointers
|
||||
# Lab 13 — Garbage Collection & Smart Pointers
|
||||
|
||||
## Checkpoint 1:
|
||||
|
||||
|
||||
77
labs/13_smart_memory/ds_smart_pointers.h
Normal file
77
labs/13_smart_memory/ds_smart_pointers.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <cstdlib>
|
||||
|
||||
// ==============================================================================
|
||||
// basic auto pointer implementation
|
||||
// see also http://ootips.org/yonat/4dev/smart-pointers.html
|
||||
|
||||
template <class T>
|
||||
class dsAutoPtr {
|
||||
public:
|
||||
explicit dsAutoPtr(T* p = NULL) : ptr(p) {} /* prevents cast/conversion */
|
||||
~dsAutoPtr() { delete ptr; }
|
||||
T& operator*() { return *ptr; }
|
||||
T* operator->() { return ptr; }
|
||||
private:
|
||||
T* ptr;
|
||||
};
|
||||
|
||||
|
||||
// ==============================================================================
|
||||
// basic reference counting pointer borrowed from:
|
||||
// http://www.codeproject.com/Articles/15351/Implementing-a-simple-smart-pointer-in-c
|
||||
|
||||
class ReferenceCount {
|
||||
public:
|
||||
ReferenceCount() { count = 0; }
|
||||
void addReference() { count++; }
|
||||
int releaseReference() { return --count; }
|
||||
private:
|
||||
int count;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
class dsSharedPtr {
|
||||
public:
|
||||
dsSharedPtr(T* pValue = NULL) : pData(pValue) {
|
||||
// Create a new reference counter & increment the count
|
||||
reference = new ReferenceCount();
|
||||
reference->addReference();
|
||||
}
|
||||
dsSharedPtr(const dsSharedPtr<T>& sp) : pData(sp.pData), reference(sp.reference) {
|
||||
// use the same reference counter, increment the count
|
||||
reference->addReference();
|
||||
}
|
||||
dsSharedPtr<T>& operator= (const dsSharedPtr<T>& sp) {
|
||||
if (this != &sp) {
|
||||
// Decrement the old reference count
|
||||
// if reference become zero delete the old data
|
||||
if(reference->releaseReference() == 0) {
|
||||
delete pData;
|
||||
delete reference;
|
||||
}
|
||||
// Copy the data and reference pointer
|
||||
// and increment the reference count
|
||||
pData = sp.pData;
|
||||
reference = sp.reference;
|
||||
reference->addReference();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
// destructor
|
||||
~dsSharedPtr() {
|
||||
if (reference->releaseReference() == 0) {
|
||||
delete pData;
|
||||
delete reference;
|
||||
}
|
||||
}
|
||||
bool operator== (const dsSharedPtr<T>& sp) { return pData == sp.pData; }
|
||||
T& operator* () { return *pData; }
|
||||
T* operator-> () { return pData; }
|
||||
private:
|
||||
// REPRESENTATION
|
||||
T* pData;
|
||||
ReferenceCount* reference;
|
||||
};
|
||||
|
||||
// ==============================================================================
|
||||
208
labs/13_smart_memory/main_smart_pointers.cpp
Normal file
208
labs/13_smart_memory/main_smart_pointers.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
// simple homemade smart pointers
|
||||
#include "ds_smart_pointers.h"
|
||||
|
||||
|
||||
// ====================================================
|
||||
// BALLOON: a toy class with dynamically allocated memory
|
||||
// ====================================================
|
||||
|
||||
#define MAX_NUM_ROPES 10
|
||||
|
||||
class Balloon {
|
||||
public:
|
||||
// CONSTRUCTOR & DESTRUCTOR
|
||||
Balloon(const std::string& name_) : name(name_) {
|
||||
std::cout << "Balloon constructor " << name << std::endl;
|
||||
num_ropes = 0;
|
||||
ropes = new Balloon*[MAX_NUM_ROPES];
|
||||
}
|
||||
~Balloon() {
|
||||
std::cout << "Balloon destructor " << name << std::endl;
|
||||
// don't try to destroy attached balloons, just delete array
|
||||
delete [] ropes;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
const std::string& getName() const { return name; }
|
||||
int numRopes() const { return num_ropes; }
|
||||
const Balloon* getRope(int i) const { return ropes[i]; }
|
||||
|
||||
// print the balloons we are attached to
|
||||
void print() {
|
||||
std::cout << "Balloon " << name << " is connected to: ";
|
||||
for (int i = 0; i < num_ropes; i++) {
|
||||
std::cout << ropes[i]->name << " ";
|
||||
}
|
||||
if (num_ropes == 0) std::cout << "nothing";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// add a rope connecting this balloon to another
|
||||
void addRope(Balloon* b) {
|
||||
assert (num_ropes < MAX_NUM_ROPES);
|
||||
ropes[num_ropes] = b;
|
||||
num_ropes++;
|
||||
}
|
||||
// detach a rope connecting this balloon to another
|
||||
void removeRope(Balloon* b) {
|
||||
for (int i = 0; i < MAX_NUM_ROPES; i++) {
|
||||
if (ropes[i] == b) { ropes[i] = ropes[num_ropes-1]; num_ropes--; break; }
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
int num_ropes;
|
||||
// a dynamically allocated C-style array of ropes connecting to other Balloons
|
||||
Balloon** ropes;
|
||||
};
|
||||
|
||||
|
||||
// ====================================================
|
||||
// ====================================================
|
||||
|
||||
int main() {
|
||||
|
||||
std::cout << "start of main" << std::endl;
|
||||
|
||||
// ====================================================
|
||||
// SINGLE OWNER SMART POINTERS
|
||||
// ====================================================
|
||||
|
||||
// first, without smart pointers!
|
||||
Balloon* alice(new Balloon("Hello Kitty"));
|
||||
|
||||
// now, with our homemade single owner smart pointer
|
||||
dsAutoPtr<Balloon> bob(new Balloon("Spiderman"));
|
||||
|
||||
// both alice & bob work like regular pointers...
|
||||
alice->print();
|
||||
bob->print();
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2A: INSERT NECESSARY EXPLICIT DEALLOCATION
|
||||
//
|
||||
|
||||
|
||||
|
||||
// ====================================================
|
||||
// SIMPLE SHARED POINTERS
|
||||
// ====================================================
|
||||
|
||||
// first, without smart pointers
|
||||
Balloon* cathy(new Balloon("Buzz Lightyear"));
|
||||
Balloon* daniel(cathy);
|
||||
Balloon* elaine(new Balloon("Pokemon"));
|
||||
Balloon* fred(elaine);
|
||||
daniel = fred;
|
||||
fred = NULL;
|
||||
elaine = cathy;
|
||||
cathy = NULL;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2B: INSERT NECESSARY EXPLICIT DEALLOCATION
|
||||
//
|
||||
|
||||
|
||||
|
||||
daniel = NULL;
|
||||
elaine = NULL;
|
||||
|
||||
|
||||
// now, with our homemade shared pointer
|
||||
dsSharedPtr<Balloon> cathy2(new Balloon("Buzz Lightyear2"));
|
||||
dsSharedPtr<Balloon> daniel2(cathy2);
|
||||
dsSharedPtr<Balloon> elaine2(new Balloon("Pokemon2"));
|
||||
dsSharedPtr<Balloon> fred2(elaine2);
|
||||
daniel2 = fred2;
|
||||
fred2 = NULL;
|
||||
elaine2 = cathy2;
|
||||
cathy2 = NULL;
|
||||
// NOTE: no explicit destruction required!
|
||||
daniel2 = NULL;
|
||||
elaine2 = NULL;
|
||||
|
||||
|
||||
// ====================================================
|
||||
// SHARED POINTERS WITH INTERCONNECTED STRUCTURES
|
||||
// ====================================================
|
||||
|
||||
Balloon* georgette(new Balloon("Mr Potato Head"));
|
||||
Balloon* henry(new Balloon("Snoopy"));
|
||||
|
||||
georgette->addRope(henry);
|
||||
henry = new Balloon("Tigger");
|
||||
georgette->addRope(henry);
|
||||
georgette->print();
|
||||
henry->print();
|
||||
|
||||
Balloon* isabelle(new Balloon("Shrek"));
|
||||
henry->addRope(isabelle);
|
||||
isabelle = new Balloon("Barney the Purple Dinosaur");
|
||||
georgette->addRope(isabelle);
|
||||
|
||||
henry->print();
|
||||
georgette->print();
|
||||
isabelle->print();
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2C: REWRITE THE ABOVE EXAMPLE TO USE SHARED POINTERS
|
||||
//
|
||||
|
||||
|
||||
|
||||
// ====================================================
|
||||
// CYCLIC STRUCTURES
|
||||
// ====================================================
|
||||
|
||||
|
||||
// FOR CHECKPOINT 3
|
||||
|
||||
|
||||
/*
|
||||
Balloon* jacob(new Balloon("Dora the Explorer"));
|
||||
Balloon* katherine(new Balloon("Kung Fu Panda"));
|
||||
Balloon* larry(new Balloon("Scooby Doo"));
|
||||
Balloon* miranda(new Balloon("SpongeBob SquarePants"));
|
||||
Balloon* nicole(new Balloon("Papa Smurf"));
|
||||
|
||||
jacob->addRope(katherine);
|
||||
katherine->addRope(larry);
|
||||
larry->addRope(jacob);
|
||||
miranda->addRope(jacob);
|
||||
nicole->addRope(miranda);
|
||||
larry->addRope(nicole);
|
||||
|
||||
katherine = NULL;
|
||||
larry = NULL;
|
||||
miranda = NULL;
|
||||
nicole = NULL;
|
||||
|
||||
// jacob points to a cyclic structure!
|
||||
|
||||
// to cleanup this structure:
|
||||
deleteAll(jacob);
|
||||
|
||||
jacob = NULL;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
std::cout << "end of main" << std::endl;
|
||||
return 0;
|
||||
|
||||
//
|
||||
// NOTE: when smart pointers go out of scope, the destructors for
|
||||
// those objects will be called automatically
|
||||
//
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
58
labs/13_smart_memory/main_stop_and_copy.cpp
Normal file
58
labs/13_smart_memory/main_stop_and_copy.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "stop_and_copy.h"
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
StopAndCopy m;
|
||||
std::cout << "TESTING StopAndCopy" << std::endl;
|
||||
std::cout << std::endl << "empty memory:" << std::endl << m;
|
||||
|
||||
// create an interesting data structure
|
||||
m.root = m.my_new('a',MY_NULL,MY_NULL);
|
||||
m.my_new('b',MY_NULL,m.root);
|
||||
m.root = m.my_new('c',m.root,MY_NULL);
|
||||
m[m.root].right = m.my_new('d',m[m.root].left,MY_NULL);
|
||||
|
||||
std::cout << std::endl << "4 cells allocated:" << std::endl << m;
|
||||
|
||||
m.root = m.my_new('e',MY_NULL,m.root);
|
||||
m[m.root].right = m.my_new('f',m[m.root].right,MY_NULL);
|
||||
m[m[m.root].right].right = m.my_new('g',m[m.root].right,MY_NULL);
|
||||
m.root = m.my_new('h',m.root,MY_NULL);
|
||||
m.root = m[m[m.root].left].right;
|
||||
|
||||
std::cout << std::endl << "8 cells allocated:" << std::endl << m;
|
||||
|
||||
// force garbage collection
|
||||
m.collect_garbage();
|
||||
std::cout << std::endl << "after forced garbage collection:" << std::endl << m;
|
||||
|
||||
// allocate more cells to force garbage collection
|
||||
m[m.root].left = m.my_new('i',m.root,MY_NULL);
|
||||
m.root = m.my_new('j',m.root,MY_NULL);
|
||||
m.root = m.my_new('k',m.root,MY_NULL);
|
||||
|
||||
std::cout << std::endl << "after adding 3 more cells:" << std::endl << m;
|
||||
|
||||
|
||||
// Walk through the Stop And Copy garbage collection algorithm on
|
||||
// the memory at this point in the program. Draw a pencil & paper
|
||||
// diagram to show your work.
|
||||
|
||||
|
||||
// UNCOMMENT THESE LINES AFTER YOU FINISH CHECKPOINT 1 (to check your work)
|
||||
/*
|
||||
m.root = m.my_new('l',m.root,MY_NULL);
|
||||
std::cout << std::endl << "adding another cell triggers automatic garbage collection:" << std::endl << m;
|
||||
|
||||
// "forget" the root pointer
|
||||
m.root = MY_NULL;
|
||||
m.collect_garbage();
|
||||
std::cout << std::endl << "root set to null & forced garbage collection:" << std::endl << m;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
96
labs/13_smart_memory/stop_and_copy.cpp
Normal file
96
labs/13_smart_memory/stop_and_copy.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "stop_and_copy.h"
|
||||
|
||||
|
||||
// Return the node corresponding to a particular address
|
||||
Node& StopAndCopy::operator[](Address addr) {
|
||||
if (addr == MY_NULL) {
|
||||
std::cerr << "ERROR: NULL POINTER EXCEPTION!" << std::endl; exit(1); }
|
||||
if (addr < OFFSET || addr >= OFFSET+CAPACITY) {
|
||||
std::cerr << "ERROR: SEGMENTATION FAULT!" << std::endl; exit(1); }
|
||||
return memory[addr-OFFSET];
|
||||
}
|
||||
|
||||
|
||||
Address StopAndCopy::my_new(char v, Address l, Address r) {
|
||||
// if we are out of memory, collect garbage
|
||||
if (next == partition_offset+CAPACITY/2) {
|
||||
collect_garbage();
|
||||
// update the addresses (since memory has been shuffled!)
|
||||
if (l != MY_NULL) l = memory[l-OFFSET].left;
|
||||
if (r != MY_NULL) r = memory[r-OFFSET].left;
|
||||
}
|
||||
// if we are still out of memory, we can't continue
|
||||
if (next == partition_offset+CAPACITY/2) {
|
||||
std::cerr << "ERROR: OUT OF MEMORY!" << std::endl; exit(1); }
|
||||
// assign the next available node
|
||||
memory[next].value = v;
|
||||
memory[next].left = l;
|
||||
memory[next].right = r;
|
||||
return OFFSET + next++;
|
||||
}
|
||||
|
||||
|
||||
// Print function for debugging
|
||||
std::ostream& operator<<(std::ostream &ostr, StopAndCopy &m) {
|
||||
ostr << "root-> " << m.root << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << i+OFFSET << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr << " "; ostr.width(1); ostr << m.memory[i].value << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << m.memory[i].left << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << m.memory[i].right << " "; }
|
||||
ostr << std::endl;
|
||||
// print "FREE" or "used" for each node in the current partition
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
if (i >= m.next && i < m.partition_offset+CAPACITY/2)
|
||||
ostr << "FREE ";
|
||||
else if (i >= m.partition_offset && i < m.partition_offset+CAPACITY/2)
|
||||
ostr << "used ";
|
||||
else // print nothing for the other half of memory
|
||||
ostr << " "; }
|
||||
ostr << std::endl;
|
||||
return ostr;
|
||||
}
|
||||
|
||||
|
||||
void StopAndCopy::collect_garbage() {
|
||||
// switch to the other partition
|
||||
partition_offset = (partition_offset == 0) ? CAPACITY/2 : 0;
|
||||
// scan & next start at the beginning of the new partition
|
||||
Address scan;
|
||||
next = scan = partition_offset;
|
||||
// copy the root
|
||||
copy_help(root);
|
||||
// scan through the newly copied nodes
|
||||
while (scan != next) {
|
||||
// copy the left & right pointers
|
||||
copy_help(memory[scan].left);
|
||||
copy_help(memory[scan].right);
|
||||
scan++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StopAndCopy::copy_help(Address &old) {
|
||||
// do nothing for NULL Address
|
||||
if (old == MY_NULL) return;
|
||||
// look for a valid forwarding address to the new partition
|
||||
int forward = memory[old-OFFSET].left;
|
||||
if (forward-OFFSET >= partition_offset &&
|
||||
forward-OFFSET < partition_offset+CAPACITY/2) {
|
||||
// if already copied, change pointer to new address
|
||||
old = forward;
|
||||
return;
|
||||
}
|
||||
// otherwise copy it to a free slot and leave a forwarding address
|
||||
memory[next] = memory[old-OFFSET];
|
||||
memory[old-OFFSET].left = next+OFFSET;
|
||||
old = next+OFFSET;
|
||||
next++;
|
||||
}
|
||||
|
||||
53
labs/13_smart_memory/stop_and_copy.h
Normal file
53
labs/13_smart_memory/stop_and_copy.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <iostream>
|
||||
|
||||
// size of memory available for this process
|
||||
#define CAPACITY 16
|
||||
// first valid address for this process
|
||||
#define OFFSET 100
|
||||
#define MY_NULL 0
|
||||
|
||||
|
||||
typedef int Address;
|
||||
|
||||
|
||||
// A helper class for the StopAndCopy memory system
|
||||
class Node {
|
||||
public:
|
||||
Node() { value='?'; left=-1; right=-1; } // initialized with "garbage" values
|
||||
char value;
|
||||
Address left;
|
||||
Address right;
|
||||
};
|
||||
|
||||
|
||||
// A simple implementation of the basic StopAndCopy garbage collector
|
||||
class StopAndCopy {
|
||||
public:
|
||||
StopAndCopy() {
|
||||
root = MY_NULL;
|
||||
partition_offset = 0;
|
||||
next = 0;
|
||||
}
|
||||
// Return the node corresponding to a particular address
|
||||
Node& operator[](Address addr);
|
||||
// allocate a new node
|
||||
Address my_new(char v, Address l, Address r);
|
||||
// a print function for debugging
|
||||
friend std::ostream& operator<<(std::ostream &ostr, StopAndCopy &m);
|
||||
// force automatic memory management
|
||||
void collect_garbage();
|
||||
// REPRESENTATION
|
||||
public:
|
||||
// the user must set this value such that all useful memory is
|
||||
// reachable starting from root (NOTE: publicly accessible)
|
||||
Address root;
|
||||
private:
|
||||
// total machine memory
|
||||
Node memory[CAPACITY];
|
||||
// which half of the memory is active
|
||||
int partition_offset;
|
||||
// next available node
|
||||
int next;
|
||||
// a private helper function
|
||||
void copy_help(Address &old_address);
|
||||
};
|
||||
Reference in New Issue
Block a user