First publication date : 2020/04/16
Modeling exercise
To do this exercise, you need to read the note about the streams and the note about catch and unit tests. The program skeleton is provided with the lesson
The goal is to help a statistician that uses data from different sources (data producers).
The source file main.cpp
what can happens with a demo.txt
. The file is parsed and some "statistics" are computed...
Statistician s;
s.acquire("demo.txt");
std::cout << "Count: " << s.getCount() << std::endl;
std::cout << "Sum: " << s.getSum() << std::endl;
std::cout << "Mean: " << s.getMean() << std::endl;
With the catch library, you will write:
- a
ConstantProducer
that creates a file with the same given value - a
IncrementalProducer
that creates a file with incremented value
Each producer will have a produce()
method to create the given file
bool produce(std::string name, int a, int b);
name
is the filename to createa
is the number of integer to generateb
is the value for the constant producer and is unused for the incremental producer.
Each producer will be able to count the number of times the produce()
method is called with an attribute work
and the appropriate get method.
TEST_CASE("Producer_Initialization") {
ConstantProducer p;
REQUIRE( p.getWork() == 0);
}
TEST_CASE("Producer_work2") {
ConstantProducer p;
p.produce(10, "test01.txt");
p.produce(10, "test01.txt");
p.produce(10, "test01.txt");
REQUIRE( p.getWork() == 3);
}
The test file for the Incremental Producer can looks like this:
TEST_CASE("Producer_Work3") {
const int DEMAND = 10;
const std::string FILE_NAME("test01.txt");
int reading, i;
IncrementalProducer p;
p.produce(DEMAND, FILE_NAME.c_str());
std::ifstream file(FILE_NAME.c_str());
REQUIRE(file.is_open());
if (!file.eof()) {
file >> reading;
REQUIRE(DEMAND == reading);
for (i = 0; i < DEMAND; ++i) {
file >> reading;
REQUIRE( reading == (i+1) );
}
REQUIRE(i == DEMAND);
// CHECK(file.eof());
file.close();
REQUIRE(p.getWork() == 1);
}
}
We can add more tests. If you want to test your statistcian, do not forget that the sum of the n first integers is equals to n*(n+1)/2.
You can also write a Random Producer and add a menu to the application !!
Producer * p = new RandomProducer();
p->produce("file.txt", 30, -1);
Simple public inheritance
Illustration
- Define a class
Mother
whose constructor and destructor display some stuff on standard output (like theGossip
class). - Define a class
Daughter
that (publicly) inherits fromMother
. Do not put anything in the class right now ! - Instanciate the class
Daughter
. What happens ?
An object of class Daughter
IS before anything else an object of class Mother
, that is why constructing a new Daughter
object calls first the Mother
constructor.
We can see destructions are called in reverse way.
- To better see what is going on, implement a gossip constructor and destructor for
Daughter
. - If the
Daughter
constructor does not explicitly call theMother
constructor, what happens ?
An object of Daughter
class is by definition Mother
object as well, that is why the constructor of the super class is called even if it is not officially mentioned. Do not forget it though, it kinda "comment" the code.
- Update the default constructor of
Daughter
to call explicitly the one ofMother
- Add an integer attribute to th
Mother
class - Check if this attribute can be accessed in a method of
Daughter
depending on its visibility :public
,protected
orprivate
. For the next questions, we will consider this attribute NOT public. - Use this attribute to count the number of created
Mother
instances. Display this number at instanciation. - Check this number is incremented when a
Daughter
object is created.
You need a class member attribute to do the trick (Mother
class)!
- Add a method
getCounter()
(getter) to this attribute - Call this method from an object of
Mother
type, then fromDaughter
class.
The method is inherited : it is available in the daughter class even if it not defined.
- You can even call
getCounter()
from outside any class, in themain()
function for example. - Add a
name
data member of typestd::string
to theMother
class - Add or modify the constructor to initialize this attribute with a given parameter value.
- Add a
getName()
method on this attribute - Instanciate a
Mother
object with the new or upgraded constructor. - Call the
getName()
from an object of the classMother
, then from an object of classDaughter
. - Try to instanciate an object of the class
Daughter
with a given name. What happens ?
The constructor with parameter from Mother
class is not inherited. You need to give one (write it) in the Daughter
class.
C++11 allows to get all the constructors of the mother class.
- Write a constructor for
Daughter
that accepts a string given in parameter and that calls explicitly the one ofMother
clas. - What happens if the constructor with a string is not specified ?
- Add now a method
display()
in the classMother
that displays the object of classMother
. - Do the same for
Daughter
class - Check that the execution is correct for an instance of
Mother
and for an instance ofDaughter
- Check now that the following code runs correctly. Adapt your code if this is not the case
Mother *pm = new Mother("mere_dyn");
Daughter *pf = new Daughter("dynamic mother");
Mother *pp = new Daughter("daughter seen as a mother");
pm->display(); // displays "Mother"
pf->display(); // displays "Daughter"
pp->display(); // displays "Daughter"
What about ...
What does the following program ?
class Mother {
protected:
string name;
public:
Mother(string s="not provided"):name(s) {
}
void method1() {
cout << "M1" << name << endl;
}
};
class Daughter : public Mother {
private:
string name;
public:
Daughter():Mother("noname") {
}
void method2() {
cout << "M2" << name << endl;
}
};
int main(int, char**) {
Daughter d;
d.method1();
d.method2();
}
You can copy-paste or type this code but you can also get it with git
(question.cpp
- not translated)
If you have already set up the git
environment, type the command :
git clone https://gitlab.com/kiux/CPP3.git
- Change the
method1()
declaration to make itvirtual
dansMother
. What happens then ? - Copy
method1()
dansDaughter
. What happens ?
The name
attribute of the Mother
class is hidden in the Daughter
class. To reach it again, you need to qualify it like this : Mother::name
Messages
This exercice is not difficult but it is mandatory to have the declaration and the implementation in two separate files. Give up if you spend more than 10 minutes on it.
Write two classes A
and B
. The class A
declares a i
integer attribute and the class B
another one called j
.
Both classes have a method exec()
and a method send()
that allows to send data to the other class.
- The
send()
method of thA
class takes a pointer to an object of the other classB
and reciprocally. - The
exec()
method of each classtakes an integer as parameter and add the value of the parameter to thei
orj
datamember of the given object (A
ouB
).
Running send()
method calls an exec()
method on a remote object with a constant of your choice. Thus oneA.send(&oneB)
activates the send()
method of a oneA
object (class A
) that calls the exec()
method of object oneB
(class B
).
To do this, you need
- to use a
makefile
to handle the files :A.hpp
,A.cpp
,B.hpp
,B.cpp
andmain.cpp
- the proper classes declarations have to be in their own files, without forgetting the file watchers
- to use the forward declarations.
B class needs A class and A class needs B class. There is a circle in the declarations. You have to use forward declarations to make it work. To do this, you have to restrict the use to references or pointers of other class objects. You cannot handle object that ar not entirely defined.
Red string...gesture
We will continue the application.
- Create a
List
class with two arrays as data members : one containingCircle
and the other one containingRectangle
. The arrays have a given constant capacity (a true constantconst
and NOT a symbolic constant). These attributes are public (not recommanded but...). If you want to respect the encapsulation, use pointeurs arrays. We have also to know the number of elements in each array (their "size").
List |
+ circles : array + nb_c : integer + rectangles : array + nb_r : integer + counter : integer |
+ List() + getCounter() : integer + toString() : string |
- Add to classes
Rectangle
andCircle
a new attributeorder
. This attribute will be handled by theList
each time an object is added to the list. We naturally assume that an object can only belongs to one list exactly. - Write a method
toString()
that fills a string returning the list of rectangles and circles defined in the list. You can display one list at a time but at the end, dipsplay the objects in the order they were added.
This way of doing is not handy nor efficient. We can of course do best but we will do it later on !
- Define a class
Point
with anx
and ay
to represent coordinates - Create a
Shape
class with a point, a widthw
et and a heighth
. - Add a class attribute
shapesCounter
incremented each time an object is instanciated ( and decremented when an object is destroyed). - Check that everything goes fine !!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10