First publication date : 2020/04/06
Object Oriented Programming : cooking
Can you create the Recipe
class to make this piece of code working ?
#include <iostream>
class Recipe {
// work to do
};
int main(int, char**) {
Recipe cake;
cake.display();
// Here is the display :
// nothing to prepare
cake.setEggs(4); // number of eaggs
cake.setButter(240); // weight
cake.setFlour(240);
cake.setSugar(240);
cake.display();
// Here here the display :
// Eggs : 4, Flour : 240, …
cake.bake(180, 40); // temperature, time
cake.display();
// Here here the display :
// Recipe is ready to eat
}
To give you a hint :
Recipe |
- eggs : real - butter : real - flour : real - sugar : real |
+ constructor ? + setEggs(e: int) + setButter(b: real) + setFlour(f: real) + setSugar(s: real) + display() + bake(temp: integer, time: integer) |
The display()
method has a different behavior depending on actions on the object. How can you model that, here is a solution if you need it ...
An easy way to model the behavior is to add anoter data member called state
. For example, if state is equals to 0, it means that no information on content was given. If state
is equals to 1, it means at least one information is given. If state
is equals to 2, it means that the cake is cooked whatever the result is :-)
If you need more, I hide a piece of code :
class Recipe {
public:
Recipe() {
state = 0;
eggs = 0;
flour = .0;
}
void setEggs(int e) {
eggs = e;
state = 1;
}
void setFlour(double f) {
flour = f;
state = 1;
}
void bake(int, int) {
state = 2;
}
void display() {
if (state==0)
std:cout << "Nothing to do" << std::endl;
else if (state==1) {
std::cout << "Eggs:" << eggs << std::end;
std::cout << "Flour:" << flour << std::end;
}
else if (state==2)
std::cout << "Ready to eat ..." << std::endl;
}
private:
int state:
int eggs;
double flour;
};
Here we will see how a class data member works !!!
Introduction to Object Oriented Programming, from lecture to practical
The goal of this exercice is to implement and to instanciate a Point
class with some data members while ensuring encapsulation.
Point |
- x : real - y : real |
+ Point() + Point(x, y) + setX(x : real) + getX() : real + setY(y : real) + getY() : real + moveTo(x, y) + moveFrom(dx, dy) |
Course examples are given as a reminder.
Your work has to be split into 2 or 3 files :
- a header file
Point.hpp
that contains the declaration of the class - a code file
Point.cpp
that contains the definition/implementation of the class - It is mandatory to have a
main()
function in another filemain.cpp
or, at the pinch,Point.cpp
The skeleton to use is the following :
// Point.hpp file
// Watchers are missing, you have to add them
// Similar to C language
class Point {
// By default, all is private in a "class"
int x;
public:
int getX();
};
A header file as Point.hpp
should never be compiled ! If you do so, you will have a "precompiled" header ! Delete this particular header if you do not know what you are doing.
Here are the implementation file of the class Point
:
// File Point.cpp
#include <iostream> // To include a standard file
#include "Point.hpp" // To include a file from the current working directory
int Point::getX() {
return x;
}
You find now the entry point in main.cpp
. Have a look at the #include
with the quotation marks :
// File main.cpp
int main(int, char**) {
Point p;
std::cout << p.getX();
return 0;
}
#include "file"
allows to include a file from the current/working directory.
#include <file>
allows to include a file from a standard foler (usually /usr/[local]/include
OR from a path given by the compiler option -I
(uppercase i)
- Compile and run the program. What can we notice about the data members ?
Initialization is not automatic ! It depends on the compiler. Valgrind will help for that
- Add the
setX()
(with the proper prototype) in the header file and in the implementation file. Check that calling the method inmain()
changes the value ofx
attribute - Remove the semicolon at the end of the class and try to compile. My compiler displays several errors and among them : (perhaps a semicolon is missing after the definition of ‘Point’)
- Add the
y
attribute and the methodsmoveTo()
andmoveFrom()
- Check that the methods do what they really meant to.
Let's continue:
- Add two constructors, one with no argument and the other one with. The constructors have to write on the standard output their name (prototype) when they are called.
- Instanciate several objects of
Point
by calling the different constructors. - Instanciate also an object with the
new
operator - Add a class member named
counter
with the proper accessors. - Check that this attribute is properly defined before any object creation the check that the value is incremented when required
- Check the different ways to call the accessor (class and object)
- Check that this is not possible to access
x
ory
attributes in a class method (the opposite is possible) - Check that this is not possible to access a private member (class or instance) from outside the class
There is another way to define a class : you can use the struct
keyword . All members (methods or data) are public instead of private with the class
keyword.
Diaper ...
The following exercices aim at emphasizing some situations to avoid.
Gossip : different kinds of allocation
Complete the code of theGossip
class with a constructor and a destructor
that display respectively "contruction of %" et "destruction of %" where % is a parameter given at the construction
(with a default value of 0).
#include <iostream>
class Gossip {
//
// Put you code in here
//
} weird(1);
Gossip global(2);
void dummy(Gossip g) {
std::cout << "dummy function code";
}
int main(int, char **) {
Gossip g;
Gossip * p = new Gossip(3);
// dummy(b);
return 0;
}
If you put the given code in separate files (as you should do for header and implementation) , be careful to the definitions of weird
and global
.
If the notion of destructor is unclear at this time, you just need to known that it is a special method called when the object is detroyed. Its definition may look like :
~Gossip() {
std::cout << "Destruction " << n << std::endl;
}
- What is
weird
? - After checking the running code with
valgrind
, what do you need to do ?
Maybe, you have to loock after *p
.
- Uncomment le line 18. Why can we see more destructions than constructions ?
An instance is destroyed without its construction is displayed. There should be an implicit construction somewhere. We will see it in class : parameters in C++ are passed by value (copy) as in C language.
- Add a method that returns the parameter given at the construction (called a getter).
- Insert this code in the
main()
function. What can you deduce about the lifespan of the object ?
int main(int, char **) {
std::cout << Gossip(0).get() << std::endl;
}
Gossiping arrays
Add a display()
method that displays on the standard output "Display of %" and run the following code :
int main(int, char **) {
const int SIZE = 20;
Gossip arr1[SIZE];
Gossip * arr2 = new Gossip[SIZE];
for (int i =0; i < SIZE; ++i)
{
arr1[i].display();
arr2[i].display();
}
return 0;
}
Of course, the allocated memory to arr2
is not freed...You have to do it with the proper version of delete
.
You have to notice that instances have been made when the arrays were created.
A complex object : a pair
Write a Pair
class that have two objects of type Gossip
as data members.
- Instanciate and check that 3 objects are indeed created and the destroyed (valgrind ...)
- Use an initialization list to give distinct values for
Gossip
instances - Check that desallocations are made in the reverse order of allocations.
- The attributes have to be initialized in the order of declaration. Try to reorder them to discover the warning/error message.
The option -Wreorder
produces a message, it is included in -Wall
A complex object : a big family
- Write a class
Family
that defines a pointer to handle an array ofGossip
elements - Add a constructor that allocates an array whose size is given (a parameter). The value 0 is acceptable.
- Test a program that instanciates one or more
Family
objects with valgrind and notice what is going on. - Add the proper destructor
malloc/free vs new/delete
Instantiate an object of Gossip
class with malloc()
. Display the "value" field of the obect. What is happening ? (to compare with the use of new
)
Run valgrind !!!
To include a C header file (malloc()
and free()
are defined in stdlib.h
), it is easy, you have to prefix the library name with the -c-
and to forget the extension :
#include <cstdlib>
Makefile
You will find a example of makefile file with automatic dependancies generation. It uses the -MMD option of the C++ compiler. Dependancies files (*.d) and objects files (*.o) are created in a disposable folder called build.
You have to run the command make
to do the magic.
If you copy the following code, pay attention to the fact the lines begin with tabulations and not spaces !
SRC=main.cpp obj.cpp
#SRC=$(wildcard *.cpp)
EXE=exe_name
CXXFLAGS+=-Wall -Wextra -MMD -g -O2 -fdiagnostics-color=auto
LDFLAGS= #-lSDL
OBJ=$(addprefix build/,$(SRC:.cpp=.o))
DEP=$(addprefix build/,$(SRC:.cpp=.d))
all: $(OBJ)
$(CXX) -o $(EXE) $^ $(LDFLAGS)
build/%.o: %.cpp
@mkdir -p build
$(CXX) $(CXXFLAGS) -o $@ -c $<
clean:
rm -rf build core *.gch
-include $(DEP)
An @ deactivates the display of the line (=> no standard output).
The include
command allows including other makefiles like makefile.dep
. If the included file do not exist, error is ignored if the "-" prefix is given .
This makefile erases the precompiled headers if they exist (clean
rule).
To conclude, if your are compiling on a more than one processor or core and if the command takes time, you can use the machine architecture by activating the -j n
flag where n
is the number of processors/cores that you want to use to compile.
You want more ? Read the manual !!!
The red string...gesture
We will do a project that is called in french "fil rouge" or "red string" if you translate it literally. This project will be used to go through notions you have seen in previous practical works and thay will have to fit together for a bigger work
The objective is to design a program that handles vectorial shapes in text mode :-(
We will handle shapes like rectangles or circles.
Create a Rectangle
class with the following attributes : coordinates x
and y
, a width w
and a height h
. All the values are integer. Write a constructor that initializes all the parameters. We will later model the coordinates as a Point
Rectangle |
- x : integer - y : integer - w : integer - h : integer |
+ Rectangle(x, y, w, h) + toString() : string |
Create a Circle
class with the same attributes than the Rectangle
class. We will consider that a circle is bounds by a rectangle. You will add another constructor to define a circle knowning its center and radius.
Circle |
- x : integer - y : integer - w : integer - h : integer |
+ Circle(x, y, w, h) + Circle(x, y , rayon) + toString() : string |
The toString()
method returns a character string (std::string
) that gives a text representation. Here is a possible output :
CIRCLE 10 10 30 30
RECTANGLE 30 30 15 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10