Some language-specific issues with C# and with C++
public override bool Equals(System.Object obj) {
if (obj == null) return false;
// If parameter cannot be cast to StrList return false.
StrList s2 = obj as StrList; // safe downcast
if (s2 == null) return false;
// now we check the actual data:
if (currsize != s2.currsize) return false;
for (int i=0; i< currsize; i++) {
//if (!(elements[i].equals(s2.elements[i]))) return false;
if (!(elements[i]==s2.elements[i])) return false;
}
return true;
}
For this to work properly in all contexts, we also have to implement
GetHashCode(), below.
foreach (KeyValuePair<string,int> kvp in d)The hashtable is an array of linked lists; the linked-list cell type is
Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
public class Cell {
private K key_;
private V val_;
private Cell next_;
public Cell(K k, V v, Cell n) {key_ = k; val_ = v; next_ = n;}
public K getKey() {return key_;}
public V getVal() {return val_;}
public Cell next() {return next_;}
public void setVal(V v) {val_ = v;}
public void setNext(Cell c) {next_ = c;}
}
To start, I must have class dictionary<K,V> inherit from
System.Collections.Generic.whatever. This works: class dictionary<K,V> : System.Collections.Generic.IEnumerable<KeyValuePair<K,V>> {
Then I must implement the IEnumerable method. The exact method signature is
as follows; note the return type. IEnumerator<KeyValuePair<K,V>> IEnumerable<KeyValuePair<K,V>>.GetEnumerator() {
return foonumerator();
}
What is up with foonumerator()? That's here: IEnumerator<KeyValuePair<K,V>> foonumerator() {
for (int i = 0; i<htablesize; i++) {
Cell p = htable[i];
while (p != null) {
yield return new .KeyValuePair<K,V>(p.getKey(), p.getVal());
p = p.next();
}
}
yield break;
}
Why didn't I just define this in IEnumerable, above? Because we also must
implement the non-generic form of IEnumerable, due to inheritance
constraints. I did that this way: System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return foonumerator();
}
Otherwise I would have to type everything twice.
Here are a few notes on this: Intro to C+++
What about installing it?
Macs sometimes have xcode. Or you can get it at https://developer.apple.com/xcode/ (or maybe the Apple App Store).
For windows, you can install MS Visual Studio, or mingw.
The link to the MSDNAA site for Visual Studio is http://e5.onthehub.com/WebStore/ProductsByMajorVersionList.aspx?ws=afe1b6ef-7d9b-e011-969d-0030487d8897&vsro=8/.
Be sure to click register the first time you connect. Your account identifier is your Loyola email address, with the "@luc.edu".
~LinkedList() {
if (head == NULL) return;
cout << "calling destructor on LinkedList:" << endl;
printList();
Cell<T> * q = head; // we checked above head != null
Cell<T> * p = q->next();
while (q != NULL) {
cout << "deleting " << q->data() << endl;
delete q;
q = p;
if (p != NULL) p = p->next();
}
cout << "done with destructor" << endl;
}
(demo of lab3/pldlinkedlist)(Much of this material comes from Using C++11's Smart Pointers, by David Kieras of the University of Michigan.)
Yet another strategy, though, is so-called smart pointers: we create a Pointer object, and overload the (unary) * and -> operators. When a smart pointer goes out of scope, the pointer is deconstructed. However, we put into the object pointed to a reference counting mechanism (there are some other smart-pointer implementations, but this is the most common).The reference-counting smart pointer described above is shared_ptr.
Here is some code:
Thing * p = new Thing(); // raw pointer
shared_ptr<Thing> ps(new Thing()); // ps is constructed from a raw pointer
We can now do things like call (*ps).foo(), or ps->foo(). This amounts to overloading the * and -> operators.
We can even do this:
shared_ptr<Thing> ps1 (new Thing());
ps = ps1; // old Thing pointed to by ps gets deleted (or ref count gets decremented); new Thing gets its refcount incremented
Now consider this:
shared_ptr<Thing> ps2 = new Thing(); // equiv to the creation above of ps, but suspicious
Why is this suspicious? Compare it to the following (recall p is a raw pointer to a Thing):
shared_ptr<Thing> ps3 = p; // this is dangerous!
In ps3, nothing prevents us from passing p to some other part of the program, or calling delete p.This messes up the sharing count achieved via ps3. (For the record, note that the above represents a call to the constructor for ps3, and is not an assignment.)
What about a derived class ThingSpawn?
Thing* p = new ThingSpawn(); // legal
shared_ptr<Thing> ps(new ThingSpawn()); // also legal!
When we run
shared_ptr<Thing> ps (new Thing());
there are two object allocations going on: first we create the new Thing(), and then we create the new smart pointer ps. Life would be faster if we could combine these, which we can do like this:
shared_ptr<Thing> ps (make_shared<Thing>());
shared_ptr<Thing> ps (make_shared<ThingSpawn>());
What's really going on with a shared_ptr is that it points literally to a manager object, which in turn points to the managed object, which is the Thing in the examples above:
ps ----> manager[count=1] -----------> Thing
When we add a couple pointers
shared_ptr<Thing> ps4 = ps
shared_ptr<Thing> ps5 = ps
we then have a picture like this:
ps---------------> manager[count=3] ---------> Thing
ps4------------>----/ /
ps5------------>---------/