Unit 5 Exercises
Here is a C++ scratchpad:
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
}
Constructors And Const
Try the various combinations of constructing an object using the default
constructor and an
- With initializing ctor only (don't define any ctor in A1)
- With default constructor only (don't define any ctor in A1)
- Then define an initializing ctor and try to get obj2 definition to compile
- Then define both a default and an initializing ctor and try to get both objects to compile
Learning goal:
- When does the compiler generate a default constructor and when does it not?
- What does the default constructor do?
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
double x;
};
int main()
{
// A1 obj1;
// cout << "obj1's value of x: " << obj1.x << endl;
A1 obj2(3.9);
cout << "obj2's value of x: " << obj2.x << endl;
return 0;
}
Learning goal:
- When does the compiler generate a default constructor and when does it not?
- What does the default constructor do?
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
A1(double myx) { x = myx; }
double x;
};
int main()
{
A1 obj[5];
for(int i=0; i < 5; i++){
cout << "obj[" << i << "]'s value of x: " << obj[i].x << endl;
}
return 0;
}
Learning goal:
- When one object is composed of others, what ordering of constructor calls are made?
- What is the ordering of destructor calls?
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
A1() { cout << "Default A1 ctor" << endl; x = 1.0; }
A1(double myx) { cout << "Init A1 ctor" << endl; x = myx; }
~A1() { cout << "A1 dtor" << endl; }
double x;
};
class A2 {
public:
A2() { cout << "Default A2 ctor" << endl;}
~A2() { cout << "A2 dtor" << endl; }
void print() const { cout << "m1=" << m1.x << " m2=" << m2.x << endl;}
private:
A1 m1;
A1 m2;
};
int main()
{
A2 obj1;
obj1.print();
return 0;
}
Learning goal:
- When you need to call constructors for members, you must do so in the constructor initialization list
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
A1(double myx) { cout << "Init A1 ctor" << endl; x = myx; }
~A1() { cout << "A1 dtor" << endl; }
double x;
};
class A2 {
public:
A2() { cout << "Default A2 ctor" << endl;}
~A2() { cout << "A2 dtor" << endl; }
void print() const { cout << "m1=" << m1.x << " m2=" << m2.x << endl;}
private:
A1 m1;
A1 m2;
};
int main()
{
A2 obj1;
obj1.print();
return 0;
}
Learning goal:
- You should take advantage of the constructor initialization list.
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
A1() {
cout << "A1 default ctor" << endl;
for(double i = 0; i < 10; i += 0.2) { x += i; }
}
A1(double myx) { cout << "Init A1 ctor" << endl; x = myx; }
A1& operator=(const A1& other)
{ cout << "A1 operator=" << endl; x = other.x; return *this; }
double x;
};
class A2 {
public:
A2() : m1(0), m2(0) { cout << "Default A2 ctor" << endl;}
A2(const A1& mym1, const A1& mym2) {
cout << "Init A2 ctor" << endl;
m1 = mym1;
m2 = mym2;
}
void print() const { cout << "m1=" << m1.x << " m2=" << m2.x << endl;}
private:
A1 m1;
A1 m2;
};
int main()
{
A1 mym1(3.8); A1 mym2(3.95);
A2 obj1(mym1, mym2);
obj1.print();
return 0;
}
Learning goal:
- Beware of declaring variables that shadow a member
- Use the constructor initialization list to initialize members
#include <iostream>
#include <algorithm>
using namespace std;
struct A1 {
A1() { x = 1.0; cout << "Default A1 ctor - x = " << x << endl; }
A1(double myx) { x = myx; cout << "Init A1 ctor - x = " << x << endl; }
~A1() { cout << "A1 dtor - x = " << x << endl; }
double x;
};
class A2 {
public:
A2() { A1 m1(3.8); A1 m2(3.95); cout << "Default A2 ctor" << endl;}
~A2() { cout << "A2 dtor" << endl; }
void print() const { cout << "m1=" << m1.x << " m2=" << m2.x << endl;}
private:
A1 m1;
A1 m2;
};
int main()
{
A2 obj1;
obj1.print();
return 0;
}
Tru using default arguments to reduce the number of constructors needed.
Learning goal:
- How to take advantage of and use default arguments.
- Where to place default arguments (in either the prototype (preferred) or the implementation but not both!
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class Student {
public:
Student();
Student(string n);
Student(string n, int i);
Student(string n, int i, double g);
void print() { cout << "Student:\n" << name << endl;
cout << id << endl << gpa << endl; }
private:
string name;
int id;
double gpa;
};
Student::Student()
{ name = "Tommy Trojan"; id = 0; gpa = 3.2; }
Student::Student(string n)
{ name = n; id = 0; gpa = 3.2; }
Student::Student(string n, int i)
{ name = n; id = i; gpa = 3.2; }
Student::Student(string n, int i, double g)
{ name = n; id = i; gpa = g; }
int main()
{
Student s1;
s1.print();
Student s2("Alice");
s2.print();
Student s3("Bob",2);
s3.print();
Student s4("Charlie",3,3.9);
s4.print();
return 0;
}
Both seem to have a problem in that they don't yield what we expect when we print the deque. Try both and then let your instructor show you how to use conditional compilation to try both approaches and add some debug statements that can easily removed.
Learning goal:
- Use #define, #ifdef, #ifndef, #else, #endif
- Understand their use to have different code be compiled more easily.
#include <iostream>
#include <deque>
#include <vector>
using namespace std;
int main()
{
deque<int> q(5);
// Fill at back approach
//cout << "Fill at back" << endl;
//for(int i=0; i<5; i++){
// q.push_back(i);
//}
// Fill from front approach
cout << "Fill from front" << endl;
for(int i=4; i>0; i--){
q.push_front(i);
}
for(size_t i = 0; i < q.size(); i++){
cout << q[i] << " ";
}
cout << endl;
return 0;
}
Example:
class A { public: void print() const; } void A::print() const { cout << "Hi" << endl; }
Experiment below with what combinations of declaring getValue() as a const member function and passing Item& i to the function, f1
#include <iostream>
using namespace std;
class Item {
public:
// experiement with placing const
// after the member func. declaration
int getValue() ;
void setValue(int value);
private:
int value;
};
// If you mark a member function const in the declaration
// you need to also add 'const' in the definition
int Item::getValue()
{
return value;
}
void Item::setValue(int val)
{
this->value = val;
}
// experiment
void f1( Item& i)
{
int val = i.getValue();
cout << val << endl;
}
int main()
{
Item x;
x.setValue(2);
f1(x);
return 0;
}
In the function f1() we pass the Item as a const reference.
Determine whether printValue() and/or getValue need to be marked as const member functions.
#include <iostream>
using namespace std;
class Item {
public:
// experiement with placing const
// after the member func. declaration
void printValue() ;
int getValue()
{ return value; }
void setValue(int value);
private:
int value;
};
// If you mark a member function const in the declaration
// you need to also add 'const' in the definition
void Item::printValue()
{
cout << this->getValue() << endl;
}
void Item::setValue(int val)
{
this->value = val;
}
// experiment
void f1(const Item& i)
{
i.printValue();
}
int main()
{
Item x;
x.setValue(2);
f1(x);
return 0;
}
We illustrate the need for both versions through two functions: increment_it and print_it which pass an Item via non-const and then const reference.
In increment_it you see the chain of set/get that is needed. But if you realize the function is returning a reference, you can simplify this code. Please attempt to do so.
Also compile/submit the code to see why the type of reference returned by the const version must also be const
#include <iostream>
using namespace std;
class Item {
public:
// notice the two versions of getValue()
// this is the non-const version
int& getValue()
{ cout << "non-const" << endl; return value; }
// this is the const version...what should it return
int & getValue() const
{ cout << "const" << endl; return value; }
void setValue(int val) { this->value = val; }
private:
int value;
};
void increment_it(Item& i)
{
// how could we do this using
// the non-const getValue()
i.setValue(i.getValue() + 1);
}
// experiment
void print_it(const Item& i)
{
cout << i.getValue() << endl;
}
int main()
{
Item x;
x.setValue(2);
increment_it(x);
print_it(x);
return 0;
}
Inheritance
#include <iostream>
#include <string>
using namespace std;
enum {CS=0, CECS, CSBA, EE};
class Person {
public:
Person() : name_(), id_(0)
{ }
Person(std::string myname, int myid) : name_(myname), id_(myid)
{ }
~Person() {}
std::string get_name() const { return name_; }
int get_id() const { return id_; }
private:
std::string name_;
int id_;
};
class Student : public Person
{
public:
Student() {} // we should initialize members
// but let's not worry about this
Student(string n, int ident, int mjr);
int get_major() const { return major_; }
double get_gpa() const { return gpa_; }
void print_info() const;
private:
int major_;
double gpa_;
};
Student::Student(string n, int ident, int mjr)
{
name_ = n;
id_ = ident;
major_ = mjr;
gpa_ = 3.7;
}
void Student::print_info() const
{
cout << get_name() << " has ID=" << get_id() << " and major " << major_ << endl;
}
int main()
{
Student s1("Tommy", 53221, CECS);
cout << s1.get_name() << endl;
s1.print_info();
}
Based on the inheritance type, check whether:
- Student member functions (e.g. print_info()) can access name_ and get_name()
- Non members (i.e. main() can call get_name())
#include <iostream>
#include <string>
using namespace std;
enum {CS=0, CECS, CSBA, EE};
class Person {
public:
Person() : name_(), id_(0)
{ }
Person(std::string myname, int myid) : name_(myname), id_(myid)
{ }
~Person() {}
std::string get_name() const { return name_; }
int get_id() const { return id_; }
private:
std::string name_;
int id_;
};
class Student : Person
{
public:
Student() {} // we should initialize members
// but let's not worry about this
Student(string n, int ident, int mjr);
int get_major() const { return major_; }
double get_gpa() const { return gpa_; }
void print_info() const
{
// Experiment accessing Person::name_ or Person::get_name()
// based on public/private inheritance
cout << name_ << " has a gpa of " << gpa_ << endl;
}
private:
int major_;
double gpa_;
};
int main()
{
Student s1("Tommy", 53221, CECS);
// Experiement with access to get_name() based on public/private inh.
cout << s1.get_name() << endl;
}
For the object allocations and deallocations in main(), predict what will be printed by the constructors and destructors.
Remember:
- Constructors start at the base and work outward.
- Destructors start at the outer-most level and work inward.
#include <iostream>
using namespace std;
#include <iostream>
using namespace std;
class A {
int a;
public:
A() { a=0; cout << "A:" << a << endl; }
~A() { cout << "~A" << endl; }
A(int mya) { a = mya; cout << "A:" << a << endl; }
};
class B : public A {
int b;
public:
B() { b = 0; cout << "B:" << b << endl; }
~B() { cout << "~B "; }
B(int myb) { b = myb; cout << "B:" << b << endl; }
};
class C : public B {
int c;
public:
C() { c = 0; cout << "C:" << c << endl; }
~C() { cout << "~C "; }
C(int myb, int myc) : B(myb) { c = myc; cout << "C:" << c << endl; }
};
int main()
{
cout << "Allocating a B object" << endl;
B b1;
cout << "Allocating first C object" << endl;
C* c1 = new C;
cout << "Allocating second C object" << endl;
C c2(4,5);
cout << "Deleting c1 object" << endl;
delete c1;
cout << "Quitting" << endl;
return 0;
}
Polymorphism
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
Person(std::string myname, int myid) : name_(myname), id_(myid)
{ }
~Person() { cout << "~Person" << endl; }
std::string get_name() const { return name_; }
int get_id() const { return id_; }
void print_info() const
{ cout << "Person: " << name_ << " with ID=" << id_ << endl; }
private:
std::string name_;
int id_;
};
class Student : public Person
{
public:
Student(string n, int ident, double gpa);
~Student() { cout << "~Student "; }
double get_gpa() const { return gpa_; }
void print_info() const
{
cout << "Student: " << get_name() << " has a gpa of " << gpa_ << endl;
}
private:
double gpa_;
};
class CSStudent : public Student
{
public:
CSStudent(string n, int ident, double gpa, string favlang);
~CSStudent() { cout << "~CSStudent "; }
string get_lang() const { return favlang_; }
void print_info() const
{
cout << "CS Student: " << get_name();
cout << " has favorite lang: " << favlang_ << endl;
}
private:
string favlang_;
};
int main()
{
bool shouldDelete = false ;
Person *p1 = new Student("Tommy", 1234, 3.2);
Person *p2 = new CSStudent("Jill", 5678, 3.7, "c++");
Student *p3 = new CSStudent("Tanya", 9876, 3.5, "!c++");
p1->print_info();
p2->print_info();
p3->print_info();
if(shouldDelete) {
delete p1;
delete p2;
delete p3;
}
return 0;
}
#include <iostream>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
class Shape
{
public:
virtual ~Shape() { }
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
virtual string getType() = 0;
};
class RightTriangle :
{
public:
RightTriangle(double b, double h)
~RightTriangle() { }
double getArea() { return }
double getPerimeter() { return sqrt(_b*_b + _h*_h) + _h + _b; }
string getType() { return "Right Triangle"; }
private:
};
class Rectangle :
{
public:
Rectangle(double b, double h)
~Rectangle() { }
double getArea() { return _b * _h; }
double getPerimeter() { return ; }
string getType() { return "Rectangle"; }
private:
};
class Square :
{
public:
Square(double s)
// Override other virtual functions as needed
};
int main()
{
vector<Shape *> shapeList;
int selection = -1;
while(selection != 0){
cout << "Choose an option:" << endl;
cout << "=================" << endl;
cout << "Enter '0' to quit" << endl;
cout << "Enter '1 base height' for a right triangle with given base and height" << endl;
cout << "Enter '2 base height' for a rectangle with given base and height" << endl;
cout << "Enter '3 side' for a square with given side length" << endl;
cout << "> ";
cin >> selection;
// Right Triangle case
if(selection == 1){
double b, h;
cin >> b >> h;
shapeList.push_back(new );
}
// Rectangle case
else if(selection == 2){
double b, h;
cin >> b >> h;
// Add the rest of the code to allocate a new rectangle
// and add it to the shapeList
shapeList.push_back(new );
}
// Square case
else if(selection == 3){
double s;
cin >> s;
// Add the rest of the code to allocate a new square
// and add it to the shapeList
shapeList.push_back(new );
}
}
cout << endl;
for (vector<Shape *>::iterator it = shapeList.begin() ;
it != shapeList.end();
++it)
{
Shape *s = *it;
cout << s->getType() << ": Area=" << s->getArea() << " Perim=" << s->getPerimeter() << endl;
delete s;
}
return 0;
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Bracketer {
public:
virtual ~Bracketer() {}
string bracket(string s) {
return start() + s + end();
}
protected:
virtual string start() { return ""; }
virtual string end() { return ""; }
private:
};
class SquareBracketer : public Bracketer {
protected:
string start() { return "["; }
string end() { return "]"; }
};
class AngleBracketer : public Bracketer {
protected:
string start() { return "<"; }
string end() { return ">"; }
};
int main()
{
Bracketer *b = new Bracketer;
Bracketer *sb = new SquareBracketer;
Bracketer *ab = new AngleBracketer;
string s1 = "123";
cout << b->bracket(s1) << endl;
cout << sb->bracket(s1) << endl;
cout << ab->bracket(s1) << endl;
delete b; delete sb; delete ab;
return 0;
}