[C + +] object oriented (polymorphic)

Posted by Mathy on Sat, 30 Oct 2021 14:09:54 +0200

1. Understand polymorphism

(1) Concept of polymorphism

1. Determine the specific target of function call according to the actual object type;
2. The same call statement has many different manifestations in actual operation, which is polymorphism. The same statement has many forms. Overloads of functions and operators are polymorphic.
3. In C + +, one of the manifestations of polymorphism is that functions with different functions can use the same function name, so that functions with different contents can be called with one function name.
4. From the perspective of system implementation, polymorphism is divided into two categories: static polymorphism and dynamic polymorphism. Static polymorphism is realized by function overloading and dynamic polymorphism is realized by virtual function.

(2) Concept of virtual function

The virtual function in C + + is used to solve the problem of dynamic polymorphism. The so-called virtual function is to declare that the function is virtual and does not actually exist in the base class, and then formally define this function in the derived class. During the running of the program, point to a derived class object with a pointer, so that the function in the derived class object pointed to by the pointer can be called, Instead of calling functions in other derived classes.

(3) Significance of polymorphism

1. Show dynamic characteristics in program operation;
2. Function rewriting must be implemented polymorphically, otherwise it has no meaning;
3. Polymorphism is the basic characteristic of object-oriented component programming.

2. Programming to deepen the understanding of polymorphism

(1) General polymorphism

Create a new CAnimal class. Move() is its internal virtual function. Create CCat class, CEagle class and COwl class to inherit the CAnimal class, and rewrite the implementation method of its internal function move() to achieve the effect of polymorphism.
code:

#include <iostream>  
using namespace std;
class CAnimal
{
public:
    CAnimal() {
        m_nLegs = 8;
    }
    CAnimal(int nLeg) {
        m_nLegs = nLeg;
    }
	 void Move() {
		cout << "animal has many legs" << endl;
	};
protected:
	int m_nLegs;
};

class CCat : virtual public CAnimal {
public:
    CCat() {
        m_nLegs = 4;
    }
    CCat(int nLegs) {
        m_nLegs = nLegs;
    }
	void Move() {
		cout << "cat has four legs" << endl;
	};
};

class CEagle :virtual public CAnimal {
public:
    CEagle() {
        m_nLegs = 2;
    }
    CEagle(int nLegs) {
        m_nLegs = nLegs;
    }
	void Move() {
		cout << "eagle has two legs" << endl;
	};
};

class COwl : public CCat, public CEagle
{
public:
    //non-parameter constructor 
    COwl() {
        m_nLegs = 2;
    }
    COwl(int nLegs) {
        m_nLegs = nLegs;
    }
    void Move() {
        cout << "owl has two legs" << endl;
    }
};

void TestAnimal() {
	CAnimal* Animal[4];
	Animal[0] = new CAnimal;
	Animal[1] = new CCat;
	Animal[2] = new CEagle;
    Animal[3] = new COwl;
	for(int i = 0; i < 4; i++)
	{
		Animal[i]->Move();
	}
}
int main()
{
	TestAnimal();
	return 0;
}

Experimental screenshot:

You can see that the output is all the same.

(2) Special polymorphism function

Input or output parameters are pointers to parent classes or references to base classes in subclasses, and pointers to subclasses or references to subclasses in subclasses
code:

 void callMove(CAnimal* a) {
    a->Move();
}

void TestAnimal()
{
    CAnimal animal;
    CCat cat;
    CEagle eagle;
    COwl owl;
    callMove(&animal);
    callMove(&cat);
    callMove(&eagle);
    callMove(&owl);
}

int main()
{
	TestAnimal();
	return 0;
}

Operation screenshot:

You can see that the output of the Move function is the same.
In order for each subclass to output the function Move rewritten in its own class, we only need to add virtual before the Move function of the parent class of each subclass. That is, change the Move function of the parent class into a virtual function and tell the compiler that it is a virtual function and needs to be bound late.
Screenshot of changed operation:

(3) Polymorphism of destructors

code:

#include <iostream>
using namespace std;

class Base {
public:
    Base() { cout << "Base constructor... \n"; }
    ~Base() { cout << "Base destructor... \n"; }
    virtual void fun() const { cout << "Base functoin...\n"; }
};

class Derived :public Base {
public:
    Derived() {
        p = new int(0);
        cout << "Derived Constructor...\n";
    }
    ~Derived() {
        cout << "Derived destructor...\n";
        delete p;
    }
    void fun() const { cout << "Derived function...\n"; }
private:
    int* p;
};

int main() {
    Base* pd = new Derived;
    pd->fun();
    delete pd;
    return 0;
}

Operation screenshot:

It can be seen that when the pointer to the base class is released, the destructor of the derived class is not called, resulting in the space applied in the derived class is not released, resulting in memory leakage.
In order to prevent this situation in practical application, we only need to add virtual to the ~ base() {cout < < mammal destructor... \ n "; in the above code, that is, by declaring the destructor of the base class as a virtual destructor, we successfully call the destructor of the derived class through the base class pointer to release the memory.

Screenshot of changed operation:

(4) Multiple inheritance

c + + can inherit not only single but also multiple. If a class has multiple base classes, this inheritance relationship is called multiple inheritance.
Declaration of multiple inheritance:
Class derived class name: access control base class name 1, access control base class name 2
{
Member list
}

What are the problems with using multiple inheritance
Multiple inheritance is more complex and more prone to problems than single inheritance.
The two main problems of multi inheritance are:
① Inherit methods with the same name from two different base classes
② Indirectly inherit multiple instances of the same class from multiple base classes

class A
{
public:
	int a;
};

class B1 : public A 
{
public:
	int b1;
};

class B2 : public A
{
public:
	int b2;
};

class C :public B1, public B2
{
public:
	void fun()
	{
		int  i = a; 
	}
};


It can be seen that if B1 and B2 do not virtual inherit, an error will be reported. Because of multiple inheritance, C inherits B1 and B2 at the same time, and the base classes of B1 and B2 are the same, the program does not know whether a is B1 or B2. At this time, it only needs to make B1 and B2 virtual inherit a to solve the problem.

(5) Multiple inheritance instances

Design vector graphics, using multi inheritance design combination graphics, requires the ability to create different types of vector graphics, select graphics, move graphics, display graphics with different colors (indicating whether it is selected or not), and manage graphics with vector or array.

CShape.h

#ifndef CSHAPE_H
#define CSHAPE_H
#include<string>
#include<math.h>
using namespace std;

class CPoint;
class CRect;
class CShape
{
public:
	CShape();
	CShape(const CShape& shape);
	virtual ~CShape();
	virtual double GetArea() const;
	virtual bool ptIn(const CPoint& pt) const;
	virtual bool InRect(const CRect& rc) const;
	virtual void Draw() const;
	virtual void DrawColor();
	virtual CShape* Clone() const;
	virtual CShape& Move(int nOffsetX, int nOffsetY);

protected:
	string m_sName;
};

class CPoint :public CShape {
public:
	int m_nPosX;
	int m_nPosY;
	CPoint() {
		m_nPosX = 0;
		m_nPosY = 0;
	}
	CPoint(int nPosX, int nPosY);
	CPoint(const CPoint& pt);
	virtual ~CPoint();
	double GetArea() const;
	bool ptIn(const CPoint& pt) const;
	bool InRect(const CRect& rc) const;
	void Draw() const;
	void DrawColor();
	CPoint* Clone() const;
	CPoint& Move(int nOffsetX, int nOffsetY);
};
class CTriangle :virtual public CShape {
public:
	CTriangle() {}
	CTriangle(const CPoint& pt1, const CPoint& pt2, const CPoint& pt3);
	CTriangle(const CTriangle& rc);
	CTriangle(const CPoint& pt);
	virtual ~CTriangle();
	double GetArea() const;
	bool ptIn(const CPoint& pt) const;
	bool InRect(const CRect& rc) const;
	void Draw() const;
	void DrawColor();
	CShape* Clone() const;
	CShape& Move(int nOffsetX, int nOffsetY);
	CPoint m_pts[3];
};

class CRect :virtual public CShape {
public:
	CRect() {}
	CRect(CPoint pt1, CPoint pt2);
	CRect(const CRect& rc);
	CRect(CPoint pt1);
	virtual ~CRect();
	double GetArea() const;
	bool ptIn(const CPoint& pt) const;
	bool InRect(const CRect& rc) const;
	void Draw() const;
	void DrawColor();
	CShape* Clone() const;
	CShape& Move(int nOffsetX, int nOffsetY);
	CPoint m_ptLT;
	CPoint m_ptBR;
};

class Comgraphics :public CRect, public CTriangle {
public:
	Comgraphics(const CRect& pt1);
	Comgraphics(const Comgraphics& rc);
	Comgraphics(const CPoint pt1);
	virtual ~Comgraphics();
	double GetArea() const;
	bool ptIn(const CPoint& pt) const;
	bool InRect(const CRect& rc) const;
	void Draw() const;
	void DrawColor();
	CShape* Clone() const;
	CShape& Move(int nOffsetX, int nOffsetY);
	CPoint m_pt1;
	CPoint m_pt2;

};
#endif

CShape.cpp

#include "CShape.h"
#include "graphics.h"
#include <iostream>
using namespace std;
//CShape
CShape::CShape()
{
}
CShape::CShape(const CShape& shape) {
	m_sName = shape.m_sName;
}
CShape::~CShape()
{
}
double CShape::GetArea() const {
	return 0;
}
bool CShape::ptIn(const CPoint& pt) const {
	return false;
}
bool CShape::InRect(const CRect& rc) const {
	return false;
}
void CShape::Draw() const
{
}
void CShape::DrawColor()
{
}
CShape* CShape::Clone() const {
	return new CShape(*this);
}
CShape& CShape::Move(int nOffsetX, int nOffsetY) {
	return *this;
}

//CPoint
CPoint::CPoint(int nPosX, int nPosY) {
	m_nPosX = nPosX;
	m_nPosY = nPosY;
}
CPoint::CPoint(const CPoint& pt) {
	m_nPosX = pt.m_nPosX;
	m_nPosY = pt.m_nPosY;
}
CPoint::~CPoint() {
	//cout << "CPoint::~CPoint()\n";
}
double CPoint::GetArea() const {
	return 0;
}
bool CPoint::ptIn(const CPoint& pt) const {
	return false;
}
bool CPoint::InRect(const CRect& rc) const {
	return rc.ptIn(*this);
}
void CPoint::Draw() const {
	circle(m_nPosX, m_nPosY, 2);
}
void CPoint::DrawColor()
{
}
CPoint* CPoint::Clone() const {
	return new CPoint(*this);
}
CPoint& CPoint::Move(int nOffsetX, int nOffsetY) {
	m_nPosX += nOffsetX;
	m_nPosY += nOffsetY;
	return *this;
}

//CTriangle
CTriangle::CTriangle(const CTriangle& tri) {
	for (int i = 0; i < 3; i++) {
		m_pts[i] = tri.m_pts[i];
	}
}
CTriangle::~CTriangle() {
	//cout << "CTriangle::~CTriangle()\n";
}
CTriangle::CTriangle(const CPoint& pt1, const CPoint& pt2, const CPoint& pt3) {
	m_pts[0] = pt1;
	m_pts[1] = pt2;
	m_pts[2] = pt3;
}
CTriangle::CTriangle(const CPoint& pt)
{
	CPoint* pt1 = new CPoint(pt.m_nPosX + 100, pt.m_nPosY + 90);
	CPoint* pt2 = new CPoint(pt.m_nPosX, pt.m_nPosY + 90);
	m_pts[0] = pt;
	m_pts[1] = *pt1;
	m_pts[2] = *pt2;
}

CShape& CTriangle::Move(int nOffsetX, int nOffsetY) {
	for (int i = 0; i < 3; i++) {
		m_pts[i].Move(nOffsetX, nOffsetY);
	}
	return *this;
}
double CTriangle::GetArea() const {
	int x1, y1, x2, y2, x3, y3;
	x1 = m_pts[0].m_nPosX;
	y1 = m_pts[0].m_nPosY;
	x2 = m_pts[1].m_nPosX;
	y2 = m_pts[1].m_nPosY;
	x3 = m_pts[2].m_nPosX;
	y3 = m_pts[2].m_nPosY;

	double bottomLine = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
	double verticalLine1 = abs((y1 - y2) * x3 - (x1 - x2) * y3 + (x1 - x2) * y2 - (y1 - y2) * x2);
	double verticalLine2 = sqrt(pow(y1 - y2, 2) + pow(x1 - x2, 2));
	double verticalLine = verticalLine1 / verticalLine2;

	return (verticalLine * bottomLine) / 2.0;
}
bool CTriangle::ptIn(const CPoint& pt) const {
	CTriangle c1 = CTriangle(m_pts[0], m_pts[1], pt);
	CTriangle c2 = CTriangle(m_pts[1], m_pts[2], pt);
	CTriangle c3 = CTriangle(m_pts[2], m_pts[0], pt);

	double totalArea = c1.GetArea() + c2.GetArea() + c3.GetArea();

	if (totalArea == this->GetArea())
		return true;
	else
		return false;
}
bool CTriangle::InRect(const CRect& rc) const {
	return rc.ptIn(m_pts[0]) && rc.ptIn(m_pts[1]) && rc.ptIn(m_pts[2]);
}
void CTriangle::Draw() const {
	int poly[8] = { m_pts[0].m_nPosX ,m_pts[0].m_nPosY,m_pts[1].m_nPosX,m_pts[1].m_nPosY,
				m_pts[2].m_nPosX,m_pts[2].m_nPosY, m_pts[0].m_nPosX ,m_pts[0].m_nPosY };
	setfillcolor(EGERGB(0xFF, 0xFF, 0xFF));
	fillpoly(4, poly);
}
void CTriangle::DrawColor() {
	int poly[8] = { m_pts[0].m_nPosX ,m_pts[0].m_nPosY,m_pts[1].m_nPosX,m_pts[1].m_nPosY,
				m_pts[2].m_nPosX,m_pts[2].m_nPosY, m_pts[0].m_nPosX ,m_pts[0].m_nPosY };
	setfillcolor(EGERGB(0xFF, 0xA5, 0x00));
	fillpoly(4, poly);
}
CShape* CTriangle::Clone() const {
	return new CTriangle(*this);
}

//CRect
CRect::CRect(CPoint pt1, CPoint pt2) {
	m_ptLT = CPoint(min(pt1.m_nPosX, pt2.m_nPosX), min(pt1.m_nPosY, pt2.m_nPosY));
	m_ptBR = CPoint(max(pt1.m_nPosX, pt2.m_nPosX), max(pt1.m_nPosY, pt2.m_nPosY));
}
CRect::CRect(const CRect& rc) {
	m_ptLT = rc.m_ptLT;
	m_ptBR = rc.m_ptBR;
}
CRect::CRect(CPoint pt1)
{
	m_ptLT = CPoint(pt1.m_nPosX, pt1.m_nPosY);
	m_ptBR = CPoint(pt1.m_nPosX + 100, pt1.m_nPosY + 100);
}
CRect::~CRect() {
	// cout << "CRect::CRect()\n";
}
double CRect::GetArea() const {
	return (m_ptBR.m_nPosX - m_ptLT.m_nPosX) * (m_ptBR.m_nPosY - m_ptLT.m_nPosY);
}
bool CRect::ptIn(const CPoint& pt) const {
	return (pt.m_nPosX >= m_ptLT.m_nPosX && pt.m_nPosX <= m_ptBR.m_nPosX) &&
		(pt.m_nPosY >= m_ptLT.m_nPosY && pt.m_nPosY <= m_ptBR.m_nPosY);
}
bool CRect::InRect(const CRect& rc) const {
	return rc.ptIn(m_ptLT) && rc.ptIn(m_ptBR);
}
void CRect::Draw() const {
	// Store the x,y coordinates of n vertices
	int pts[10] = { m_ptLT.m_nPosX,m_ptLT.m_nPosY,m_ptBR.m_nPosX,m_ptLT.m_nPosY,
	m_ptBR.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptLT.m_nPosY };
	// To draw a polygon with n vertices, the first parameter must be passed in n+1, pts and the coordinates of the last vertex are the same as the first
	//drawpoly(5, pts);
	setfillcolor(EGERGB(0xFF, 0xFF, 0xFF));
	fillpoly(5, pts);
}
void CRect::DrawColor() {
	int pts[10] = { m_ptLT.m_nPosX,m_ptLT.m_nPosY,m_ptBR.m_nPosX,m_ptLT.m_nPosY,
	m_ptBR.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptLT.m_nPosY };
	// To draw a polygon with n vertices, the first parameter must be passed in n+1, pts and the coordinates of the last vertex are the same as the first
	setfillcolor(EGERGB(0xFF, 0xA5, 0x00));
	fillpoly(5, pts);
}
CShape* CRect::Clone() const {
	return new CRect(*this);
}
CShape& CRect::Move(int nOffsetX, int nOffsetY) {
	m_ptLT.Move(nOffsetX, nOffsetY);
	m_ptBR.Move(nOffsetX, nOffsetY);
	return *this;

}
//Comgraphics
Comgraphics::Comgraphics(const CRect& pt1) {

	m_pt1.m_nPosX = pt1.m_ptBR.m_nPosX;
	m_pt1.m_nPosY = pt1.m_ptLT.m_nPosY + (pt1.m_ptBR.m_nPosY - pt1.m_ptLT.m_nPosY) / 2;
	m_pt2.m_nPosX = pt1.m_ptLT.m_nPosX + (pt1.m_ptBR.m_nPosX - pt1.m_ptLT.m_nPosX) / 2;
	m_pt2.m_nPosY = pt1.m_ptBR.m_nPosY;
	m_ptLT = pt1.m_ptLT;
	m_ptBR = pt1.m_ptBR;

}
Comgraphics::Comgraphics(const Comgraphics& rc) {
	m_pt1 = rc.m_pt1;
	m_pt2 = rc.m_pt2;
	m_ptBR = rc.m_ptBR;
	m_ptLT = rc.m_ptLT;
}
Comgraphics::Comgraphics(const CPoint pt1) {
	m_ptLT = CPoint(pt1.m_nPosX, pt1.m_nPosY);
	m_ptBR = CPoint(pt1.m_nPosX + 60, pt1.m_nPosY + 80);
}
Comgraphics::~Comgraphics() {
	cout << "Comgraphics::~Comgraphics()" << endl;

}
double Comgraphics::GetArea()  const {
	return 0.0;
}
bool Comgraphics::ptIn(const CPoint& pt) const {
	return (pt.m_nPosX >= m_ptLT.m_nPosX && pt.m_nPosX <= m_ptBR.m_nPosX) &&
		(pt.m_nPosY >= m_ptLT.m_nPosY && pt.m_nPosY <= m_ptBR.m_nPosY);
}
bool Comgraphics::InRect(const CRect& rc) const const {
	return rc.ptIn(m_ptLT) && rc.ptIn(m_ptBR);
}
void Comgraphics::Draw() const {
	// Store the x,y coordinates of n vertices
	int pts[10] = { m_ptLT.m_nPosX,m_ptLT.m_nPosY,m_ptBR.m_nPosX,m_ptLT.m_nPosY,
	m_ptBR.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptLT.m_nPosY };
	// To draw a polygon with n vertices, the first parameter must be passed in n+1, pts and the coordinates of the last vertex are the same as the first
	//drawpoly(5, pts);
	setfillcolor(GREEN);
	fillpoly(5, pts);
	line(m_pt1.m_nPosX, m_pt1.m_nPosY, m_pt2.m_nPosX, m_pt2.m_nPosY);
	line(m_ptLT.m_nPosX, m_ptLT.m_nPosY, m_pt2.m_nPosX, m_pt2.m_nPosY);
	line(m_pt1.m_nPosX, m_pt1.m_nPosY, m_ptLT.m_nPosX, m_ptLT.m_nPosY);
}
void Comgraphics::DrawColor() {
	// Store the x,y coordinates of n vertices
	int pts[10] = { m_ptLT.m_nPosX,m_ptLT.m_nPosY,m_ptBR.m_nPosX,m_ptLT.m_nPosY,
	m_ptBR.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptBR.m_nPosY,m_ptLT.m_nPosX,m_ptLT.m_nPosY };
	// To draw a polygon with n vertices, the first parameter must be passed in n+1, pts and the coordinates of the last vertex are the same as the first
	setfillcolor(YELLOW);
	fillpoly(5, pts);
	line(m_pt1.m_nPosX, m_pt1.m_nPosY, m_pt2.m_nPosX, m_pt2.m_nPosY);
	line(m_ptLT.m_nPosX, m_ptLT.m_nPosY, m_pt2.m_nPosX, m_pt2.m_nPosY);
	line(m_pt1.m_nPosX, m_pt1.m_nPosY, m_ptLT.m_nPosX, m_ptLT.m_nPosY);
}
CShape* Comgraphics::Clone() const {
	return new Comgraphics(*(this));
}

CShape& Comgraphics::Move(int nOffsetX, int nOffsetY) {
	m_ptLT.Move(nOffsetX, nOffsetY);
	m_ptBR.Move(nOffsetX, nOffsetY);
	m_pt1.Move(nOffsetX, nOffsetY);
	m_pt2.Move(nOffsetX, nOffsetY);
	return *this;
}

Main.cpp

#include<vector>
#include "graphics.h"
#include<iostream>
#include "CShape.h"
using namespace std;

int main()
{
	//Graphics canvas basic settings
	initgraph(640, 480);
	setbkcolor(WHITE);
	delay_ms(0);
	setcolor(BLACK);
	setfont(20, 0, "Regular script");
	setbkmode(TRANSPARENT);
	//enter + left click -- > new rectangle "");
	//enter + right click -- > New Triangle "");
	//enter + middle of scroll wheel -- > new composite graphics

	//ctrl + left click -- > copy graphic "");
	//ctrl + right click -- > Paste graphic "");

	vector<CShape*>shapes;
	vector<CShape*>shapestmp;

	shapes.push_back(new CTriangle(CPoint(320, 320), CPoint(250, 340), CPoint(340, 450)));
	//shapes.push_back(new CTriangle(CPoint(10, 10), CPoint(150, 10), CPoint(150, 150)));
	shapes.push_back(new CRect(CPoint(200, 200), CPoint(300, 300)));
	shapes.push_back(new Comgraphics(CRect(CPoint(250, 50))));


	//move
	bool move_flag = false;
	bool copy_flag = false;
	bool redraw = true;
	//Record its coordinates when the mouse clicks
	int clickX, clickY;
	int copyX, copyY;
	int checkedid = -1;
	int copyid = -1;

	for (; is_run(); delay_fps(60)) {
		while (mousemsg()) {
			mouse_msg msg = getmouse();

			//Judge the movement of the mouse
			if (msg.is_move()) {
				if (checkedid != -1) {
					if (move_flag) {
						shapes[checkedid]->Move(msg.x - clickX, msg.y - clickY);
					}
				}
				clickX = msg.x;
				clickY = msg.y;
				redraw = true;
			}

			// Left mouse button
			else if (msg.is_left()) {
				// Judge whether the left mouse button is pressed
				if (msg.is_down()) {
					clickX = msg.x;
					clickY = msg.y;

					CPoint pt = CPoint(clickX, clickY);
					int isIn = 0;
					for (int i = 0; i < shapes.size(); i++) {
						if (shapes[i]->ptIn(pt)) {
							isIn = 1;
							//If the mouse is in the graphics area, set the moving flag to true
							move_flag = true;
							checkedid = i;
							redraw = true;
							break;
						}
					}
					if (isIn == 0)
						checkedid = -1;
				}
				else {
					move_flag = false;
				}
			}
		}
		// Redrawn
		if (redraw) {
			redraw = false;
			cleardevice();
			for (int i = 0; i < shapes.size(); i++) {
				if (i == checkedid)
					shapes[i]->DrawColor();
				else
					shapes[i]->Draw();
			}
		}

		while (kbmsg()) {
			key_msg msgk = getkey();
			if (msgk.key == key_enter && msgk.msg == key_msg_down) {
				mouse_msg msgm = getmouse();
				if (msgm.is_left()) {
					// Judge whether the left mouse button is pressed
					if (msgm.is_down()) {
						shapes.push_back(new CRect(CPoint(msgm.x, msgm.y)));
						redraw = true;
					}
				}
				if (msgm.is_right()) {
					// Judge whether the right mouse button is pressed
					if (msgm.is_down()) {
						shapes.push_back(new CTriangle(CPoint(msgm.x, msgm.y)));
						redraw = true;
					}
				}
				if (msgm.is_mid()) {
					CRect r1 = CRect(CPoint(msgm.x, msgm.y));
					// Determine whether the middle mouse button is pressed
					if (msgm.is_down()) {
						shapes.push_back(new Comgraphics(r1));
						redraw = true;
					}
				}
			}
			if (msgk.key == key_control && msgk.msg == key_msg_down) {
				mouse_msg msgm = getmouse();
				if (msgm.is_left()) {
					// Judge whether the left mouse button is pressed
					if (msgm.is_down()) {
						copyX = msgm.x;
						copyY = msgm.y;
						CPoint pt = CPoint(copyX, copyY);
						for (int i = 0; i < shapes.size(); i++) {
							if (shapes[i]->ptIn(pt)) {
								//If the mouse is in the graphics area, set the moving flag to true
								copy_flag = true;
								copyid = i;
								break;
							}
						}
					}
				}
				if (msgm.is_right()) {
					// Judge whether the right mouse button is pressed
					if (msgm.is_down()) {
						if (copy_flag == true) {
							shapes.push_back(&(shapes[copyid]->Clone())->Move(msgm.x - copyX, msgm.y - copyY));
							redraw = true;
						}
					}
				}

			}
		}
	}
	closegraph();
	return 0;
}

Operation screenshot:

Topics: C++