Midterm Review
The following practice problems are meant to be supplemental to the lecture notes, and give some extra practice for problem types you might encounter on the midterm.
Disclaimer: These problems are meant to be supplemental to the Professor's given practice problems, and may or may not be representative of the exact exam questions (that said, I'm told they're a fair likeness).
Constructors & Destructors
Why not a little practice with our old friend NoisyClass?
class NoisyClass {
private:
string s;
public:
NoisyClass () {
cout << "[C] Default constructor" << endl;
}
NoisyClass (string stuff) {
s = stuff;
cout << "[P] Parameterized constructor" << endl;
}
NoisyClass (const NoisyClass& other) {
s = other.s;
cout << "[~] Copy constructor" << endl;
}
~NoisyClass () {
cout << "[D] Destructor" << endl;
}
NoisyClass& operator= (const NoisyClass& other) {
s = other.s;
cout << "[=] Assignment" << endl;
return *this;
}
};
Using the NoisyClass definition from above, what will the following functions print out?
// Start with a warmup...
struct SoMeta {
NoisyClass n[2];
NoisyClass* ptr[2];
};
int main () {
SoMeta s;
SoMeta* ptr;
SoMeta* dynamicYo = new SoMeta();
delete dynamicYo;
}
Using the NoisyClass definition from above, what will the following functions print out?
// [!] Remember what it means for a function to pass
// by value, and subsequently, what it means to have
// a local parameter!
void whatsAllThisNoise (NoisyClass n) {
NoisyClass* copier = new NoisyClass();
n = *copier;
delete copier;
}
int main () {
NoisyClass n;
whatsAllThisNoise(n);
}
Pen & Pencil Exercises
There are just like... a ton of issues with the following DeadHorse class... can you find them all? (PS. It's meant to [poorly] implement a basic dynamic int array)
// Because you're not sick of dynamic memory
// arrays, and copy constructors, this example
// will beat a...
struct DeadHorse {
private:
int* i;
int capacity;
int size;
public:
DeadHorse () {
size = 0;
capacity = 2;
i = new int[2];
}
DeadHorse (const DeadHorse other) {
i = other.i;
capacity = other.capacity;
size = other.size;
}
~DeadHorse () {
delete i;
}
// Andrew's example levels reaching
// critical!
void addSomething (int toAdd) {
if (capacity == size) {
int* temp = i;
int newCap = capacity * 5;
delete[] i;
i = new int[newCap];
for (int j = 0; j < capacity; j++) {
i[j] = temp[j];
}
capacity = newCap;
}
i[size] = toAdd;
size++;
}
void print () {
for (int j = 0; j < size; j++) {
cout << i[j] << " ";
}
cout << endl;
}
};
int main () {
DeadHorse d1;
d1.addSomething(1);
d1.addSomething(2);
d1.print();
DeadHorse d2 = d1;
d2.addSomething(3);
d1.print();
d2.print();
}
Here are just a few that I remember adding... probably more:
Copy constructor param other not passed by reference!
Copy constructor copies the pointer to the int array, but does not create a new list for the copied object.
Destructor calls the wrong form of delete; should be delete[]
The array-expanding attempt of addSomething tries to save the values of the array in temp, but then frees the memory, thus flooding the array with junk.
Hand Coding
Using the Stack class from discussion, implement the popUntil member function, which prints out all of the string elements popped from the Stack until the input argument is found, or the end of the Stack is reached, whichever comes first.
Stack smoke;
smoke.push("Poppin");
smoke.push("Nodes");
smoke.push("All");
smoke.push("Day");
smoke.push("Long");
smoke.popUntil("Nodes");
// Prints: Long Day All Nodes
// smoke would only have the "Poppin" Node left
NoisyClass? Again? Really?
Yes really!
You'll thank me later...
Here's the latest inheritance and polymorphism version:
class NoisyBase {
private:
string s;
public:
NoisyBase (string sBase) {
cout << "[B] Base Constructor: " << sBase << endl;
s = sBase;
}
virtual ~NoisyBase () {
cout << "[B] Base Destructor!" << endl;
}
virtual string getS () {
return s;
}
};
class NoisyDerived: public NoisyBase {
private:
string s;
public:
NoisyDerived (string sBase, string sDerived): NoisyBase(sBase) {
cout << "[D] Derived Constructor: " << sDerived << endl;
s = sDerived;
}
~NoisyDerived () {
cout << "[D] Derived Destructor!" << endl;
}
virtual string getS () {
return s;
}
};
So, let's start off with the simple stuff and then ramp it up. Make sure you remember the order of construction and destruction!
Using the NoisyBase and NoisyDerived implementations above, what will the following code print out?
int main() {
NoisyBase b("base!");
NoisyDerived d("base!", "derived!");
}
Using the NoisyBase and NoisyDerived implementations above, what will the following code print out?
int main() {
NoisyDerived d("base!", "derived!");
NoisyBase* bPtr = &d;
cout << bPtr->getS() << endl;
}
Using the NoisyBase and NoisyDerived implementations above, what will the following code print out?
int main() {
NoisyBase* bPtr0 = new NoisyBase("base!");
NoisyBase* bPtr1 = new NoisyDerived("base!", "derived!");
delete bPtr0;
// [!] Remember your order of destruction!
delete bPtr1;
}
Using the NoisyBase and NoisyDerived implementations above, what will the following code print out?
// [!] Note: no noisy copy constructor defined, but
// b is still passed by value... so what happens
// at the end of this function?
void NoisyFunc (NoisyBase b) {
cout << b.getS() << endl;
}
int main() {
NoisyDerived d("base!", "derived!");
NoisyFunc(d);
}
Using the NoisyBase and NoisyDerived implementations above, what will the following code print out?
void f(int n) {
NoisyDerived* d = new NoisyDerived("base!", "derived!");
if (n <= 0) {
delete d;
return;
} else {
f(n - 1);
}
delete d;
}
int main() {
f(1);
}