MQL5Book/Scripts/p3/ShapesCasting.mq5

300 lines
8.3 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:09:41 +02:00
//+------------------------------------------------------------------+
//| 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;
}
//+------------------------------------------------------------------+