forked from rosh/MQL5Book
299 lines
8.3 KiB
MQL5
299 lines
8.3 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| ShapesCasting.mq5 |
| |
//| Copyright 2021, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#include "..\..\Include\TypeName.mqh"
| |
| |
#define PRT(A) Print(#A, "=", (A))
| |
| |
//+------------------------------------------------------------------+
| |
//| Base shape class for drawing |
| |
//+------------------------------------------------------------------+
| |
class Shape
| |
{
| |
public:
| |
enum SHAPES
| |
{
| |
RECTANGLE,
| |
ELLIPSE,
| |
TRIANGLE,
| |
SQUARE,
| |
CIRCLE,
| |
NUMBER_OF_SHAPES
| |
};
| |
| |
struct Pair
| |
{
| |
int x, y;
| |
Pair(int a, int b): x(a), y(b) { }
| |
};
| |
| |
protected:
| |
Pair coordinates;
| |
color backgroundColor;
| |
const string type; // remove 'const' here to make Shape's copyable
| |
// without custom operator= implementation
| |
| |
Shape(int px, int py, color back, string t) :
| |
coordinates(px, py),
| |
backgroundColor(back),
| |
type(t)
| |
{
| |
}
| |
| |
public:
| |
virtual void draw()
| |
{
| |
}
| |
| |
Shape *setColor(const color c)
| |
{
| |
backgroundColor = c;
| |
return &this;
| |
}
| |
| |
Shape *moveX(const int x)
| |
{
| |
coordinates.x += x;
| |
return &this;
| |
}
| |
| |
Shape *moveY(const int y)
| |
{
| |
coordinates.y += y;
| |
return &this;
| |
}
| |
| |
Shape *move(const Pair &pair)
| |
{
| |
coordinates.x += pair.x;
| |
coordinates.y += pair.y;
| |
return &this;
| |
}
| |
| |
string toString() const
| |
{
| |
return type + " " + (string)coordinates.x + " " + (string)coordinates.y;
| |
}
| |
};
| |
| |
class Square;
| |
//+------------------------------------------------------------------+
| |
//| Rectangle shape |
| |
//+------------------------------------------------------------------+
| |
class Rectangle : public Shape
| |
{
| |
protected:
| |
int dx, dy; // size (width, height)
| |
| |
Rectangle(int px, int py, int sx, int sy, color back, string t) :
| |
Shape(px, py, back, t), dx(sx), dy(sy)
| |
{
| |
}
| |
| |
public:
| |
Rectangle(int px, int py, int sx, int sy, color back) :
| |
Shape(px, py, back, TYPENAME(this)), dx(sx), dy(sy)
| |
{
| |
}
| |
| |
/*
| |
// Uncomment this to fix the ERROR caused by
| |
// default Rectangle::operator= is deleted duy to 'const' field in Shape
| |
Rectangle *operator=(const Rectangle &r)
| |
{
| |
coordinates.x = r.coordinates.x;
| |
coordinates.y = r.coordinates.y;
| |
backgroundColor = r.backgroundColor;
| |
dx = r.dx;
| |
dy = r.dy;
| |
return &this;
| |
}
| |
*/
| |
| |
bool amISquare() const
| |
{
| |
return dx == dy;
| |
}
| |
| |
void draw() override
| |
{
| |
::Print("Drawing rectangle"); // will print via global Print(...)
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Square shape |
| |
//+------------------------------------------------------------------+
| |
class Square : public Rectangle
| |
{
| |
public:
| |
Square(int px, int py, int sx, color back) :
| |
Rectangle(px, py, sx, sx, back, TYPENAME(this))
| |
{
| |
}
| |
| |
void draw()
| |
{
| |
Print("Drawing square");
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Ellipse shape |
| |
//+------------------------------------------------------------------+
| |
class Ellipse : public Shape
| |
{
| |
protected:
| |
int dx, dy; // large and small radiuses
| |
| |
Ellipse(int px, int py, int rx, int ry, color back, string t) :
| |
Shape(px, py, back, t), dx(rx), dy(ry)
| |
{
| |
}
| |
| |
public:
| |
Ellipse(int px, int py, int rx, int ry, color back) :
| |
Shape(px, py, back, TYPENAME(this)), dx(rx), dy(ry)
| |
{
| |
}
| |
| |
void draw()
| |
{
| |
Print("Drawing ellipse");
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Circle shape |
| |
//+------------------------------------------------------------------+
| |
class Circle : public Ellipse
| |
{
| |
public:
| |
Circle(int px, int py, int rx, color back) :
| |
Ellipse(px, py, rx, rx, back, TYPENAME(this))
| |
{
| |
}
| |
| |
void draw()
| |
{
| |
Print("Drawing circle");
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Triangle shape |
| |
//+------------------------------------------------------------------+
| |
class Triangle: public Shape
| |
{
| |
int dx; // single side
| |
public:
| |
Triangle(int px, int py, int side, color back) :
| |
Shape(px, py, back, TYPENAME(this)), dx(side)
| |
{
| |
}
| |
| |
void draw()
| |
{
| |
Print("Drawing triangle");
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Return random number in a range |
| |
//+------------------------------------------------------------------+
| |
int random(int range)
| |
{
| |
return (int)(rand() / 32767.0 * range);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Create a random shape |
| |
//+------------------------------------------------------------------+
| |
Shape *addRandomShape(Shape::SHAPES t = Shape::SHAPES::NUMBER_OF_SHAPES)
| |
{
| |
Shape::SHAPES type = ((t == Shape::SHAPES::NUMBER_OF_SHAPES) ?
| |
(Shape::SHAPES)random(Shape::SHAPES::NUMBER_OF_SHAPES): t);
| |
| |
int cx = random(500), cy = random(500), dx = random(200), dy = random(200);
| |
color clr = (color)((random(256) << 16) | (random(256) << 8) | random(256));
| |
| |
switch(type)
| |
{
| |
case Shape::SHAPES::RECTANGLE:
| |
return new Rectangle(cx, cy, dx, dy, clr);
| |
case Shape::SHAPES::ELLIPSE:
| |
return new Ellipse(cx, cy, dx, dy, clr);
| |
case Shape::SHAPES::TRIANGLE:
| |
return new Triangle(cx, cy, dx, clr);
| |
case Shape::SHAPES::SQUARE:
| |
return new Square(cx, cy, dx, clr);
| |
case Shape::SHAPES::CIRCLE:
| |
return new Circle(cx, cy, dx, clr);
| |
}
| |
| |
return NULL;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Script program start function |
| |
//+------------------------------------------------------------------+
| |
void OnStart()
| |
{
| |
Rectangle *r = addRandomShape(Shape::SHAPES::RECTANGLE);
| |
Square *s = addRandomShape(Shape::SHAPES::SQUARE);
| |
Circle *c = NULL;
| |
| |
// OBJECTS by value
| |
// ERROR:
| |
//
| |
// attempting to reference deleted function 'void Rectangle::operator=(const Rectangle&)'
| |
// function 'void Rectangle::operator=(const Rectangle&)' was implicitly deleted
| |
// because it invokes deleted function 'void Shape::operator=(const Shape&)'
| |
// function 'void Shape::operator=(const Shape&)' was implicitly deleted
| |
// because member 'type' has 'const' modifier
| |
//
| |
// Rectangle r3(100, 100, 10, 10, clrWhite);
| |
// r3 = s; // copy and conversion by value
| |
//
| |
// will work if remove 'const' for 'string type' in Shape,
| |
// or if you provide an explicit Rectangle::operator= (see above)
| |
| |
Shape *p;
| |
Rectangle *r2;
| |
| |
// OK
| |
p = c; // Circle -> Shape
| |
p = s; // Square -> Shape
| |
r = p;
| |
p = r; // Rectangle -> Shape
| |
r2 = p; // Shape -> Rectangle
| |
r2.amISquare();
| |
| |
// COMPILE ERRORs
| |
// p.amISquare(); // undeclared identifier: amISquare is in Rectangle
| |
// Circle and Rectangle are in different branches of hierarchy
| |
// c = r; // type mismatch
| |
| |
Square *s2;
| |
// RUNTIME ERROR
| |
// s2 = p; // Incorrect casting of pointers
| |
s2 = dynamic_cast<Square *>(p); // attempt to cast, NULL on failure
| |
Print(s2); // 0
| |
| |
c = dynamic_cast<Circle *>(r); // attempt to cast, NULL on failure
| |
Print(c); // 0
| |
| |
void *v;
| |
v = s; // point to a Square object
| |
PRT(dynamic_cast<Shape *>(v));
| |
PRT(dynamic_cast<Rectangle *>(v));
| |
PRT(dynamic_cast<Square *>(v));
| |
PRT(dynamic_cast<Circle *>(v)); // 0
| |
PRT(dynamic_cast<Triangle *>(v)); // 0
| |
| |
delete r;
| |
delete s;
| |
}
| |
//+------------------------------------------------------------------+
|