C + + Experiment 2 inheritance and polymorphism

Posted by jd72 on Wed, 03 Nov 2021 06:04:29 +0100

Experimental environment

Operating system: win10
gcc: 8.1.0
Development software: qt5.14.2

Experimental content

1, Inheritance access test
Design member functions or variables of class A with different attributes such as public, protected and private;
Class B inherits A through public, protected, private and other ways, and tests the member function or variable accessing A in the member function of class B;
Add member functions or variables with different attributes such as public, protected and private to class B, and test and access each member function or variable of B externally;
B inherits A in A private way and tries to promote some public members in A to public.
2, Friend class inheritance test
Design Class A contains private variable a, and friends in class A are given to class C;
Design class B inherits a and adds private variable B; Test and access the member variables A and B of class B in class C;
Design Class D to inherit C, test and access the member variables a of class A and a and B of class B in the member function of D.
3, Comprehensive application of polymorphism
General polymorphism function: the input and output parameters are exactly the same, and virtual is added to the parent class;
Special polymorphism function: the input or output parameters are the pointer of the parent class or the reference of the base class in the subclass, and the pointer of the subclass or the reference of the subclass in the subclass;
Polymorphism of destructor;
Multi inheritance, pay attention to what needs virtual inheritance;
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.
4, Job submission method
The above related functions are completed through the design of experiments, and the completion process and experimental results are recorded in the blog, especially the points of attention emphasized in class, problems encountered and corresponding solutions.

Dynamic library and static library

In this experiment, qt framework will be used, and the concept of dynamic library will be introduced and used; In fact, it is not necessary to understand and use the dynamic library in this experiment, but since it is mentioned in the course, it can also be learned and used here.

First, look back to the concept of library. Library is written, existing, mature and reusable code. In reality, every program depends on many basic underlying libraries. It is impossible for everyone's code to start from scratch, so the existence of libraries is of extraordinary significance.

As shown in the figure above, the common process of compiling code into executable program is shown in the figure above; Among them, the link mode difference in the link step is the difference between dynamic and static.

Let's talk about the difference between dynamic and static. Generally speaking, when compiling the code that calls the library; Whether libraries are compiled into programs is the essential difference between them. From the difference of use, the static library has been compiled into the program, so the executable program can run independently; The dynamic library only specifies the directional functions in the library at compile time, so the executable program cannot run independently when used. But look the other way around; From the perspective of software development update and maintenance, the dynamic library is more conducive to use, while once the static library is updated, the whole program needs to be recompiled, which is not conducive to maintenance.

In this experiment, the implementation of vector graph will depend on the corresponding dynamic library.

inherit

In this experiment, inheritance, an important concept of object-oriented programming, will be involved. This concept also exists in java. In C + +, inherited attributes are divided into three types: public, protected and private; They can be called public, protected and private respectively. Inherited classes are often called parent classes (base classes), while classes generated by inheritance are often called subclasses (derived classes).

The following will design a simple small example to illustrate. The function part is not specially designed. It can simply return variables of the corresponding category.

class test1
{
public:
    int public1 = 0;
    void publicfunction();//By calling this function, you can access three properties of test1 variable
protected:
    int protected1 = 0;
    void protectedfunction();//By calling this function, you can also access the three properties of test1 variable
private:
    int private1 = 0;
    void privatefunction();//By calling this function, you can also access the three properties of test1 variable

	friend void fritest1(test1 testfri);//friend function

//In C + +, the function of a class can access all its variables as long as it can be called.
};
//Design test2, test3 and test4 to inherit test1 in three different ways
class test2:public test1	//Inherit test1 in public
{
public:
	void use_public_val();	
	void use_public_function();	
};
class test3:protected test1		//Inherit test1 in a protected manner
{
public:
	void use_public_val();	
	void use_public_function();	
};
class test4:private test1	//Inherit test1 privately
{
public:
	using test1::public1;
	void use_public_val();	
	void use_public_function();	
};
//The secondary design test5 and test6 inherit test3 and test4 in public
class test5:public test3
{
public:
	void test_use_public_function();	
};
class test6:public test4
{
public:
	void test_use_public_function();	
};

It should be noted that the pseudo code given above is the pseudo code briefly written by the author, just for the description of inheritance.

Firstly, the three attributes of public protected private are explained from the example of test1 in one class; Inside the class, all functions and variables are freely accessible. When external references are declared, only public classes can be accessed, that is, only public1 and publicfunction can be used.

Next, the three inheritance methods of public protected private are explained from three examples: test2, test3 and test4. First, test2, test3 and test4 are subclasses that can access the variables and functions of their parent classes, that is, the public and protected properties of test1. The more specific difference lies in the inheritance types of three different inheritance methods, in short.

For public inheritance of test2:

  • Private member of the base class. Subclasses cannot be accessed
  • A protected member of a base class. A subclass can inherit as its own protected member. It can be accessed in a derived class but not externally.
  • The public member of the base class. Subclasses can inherit as their own public members. It can be accessed in a derived class or externally.

For protected inheritance of test3:

  • A public member of a base class, which is inherited as its own protected member in a subclass. It can be accessed in a derived class but not externally
  • Base class protects members. Subclasses inherit as their own protected members. They can be accessed in derived classes and not externally
  • Private members of the base class can not be accessed by subclasses like private members of the base class.

For private inheritance of test4:

  • The public member of the base class is inherited as its own private member in the subclass. It can be accessed in the derived class and not externally.
  • The base class protects members. Subclasses inherit as their own private members, which can be accessed in derived classes and not externally.
  • Private members of the base class cannot be accessed by subclasses

The above three differences can be explained from the differences between test5 and test6. For example, the access in test5 is legal, but not in test6.

using

After the above description, of course, there will be some usage scenarios; If it is necessary to enable some subclasses to access the parent class attributes with cross permissions, use can be used at this time; For example, public 1 can be accessed across permissions in test4.

Friends

Friend is also one of the important concepts in c + +. Specific applications can be divided into friend functions and friend classes; For friend functions, the biggest task can be simply summarized as making the declaring function access the protected and private objects that cannot be accessed outside the class. As the code in test1 pasted below.

class test1
{
public:
    int public1 = 0;
    void publicfunction();//By calling this function, you can access three properties of test1 variable
protected:
    int protected1 = 0;
    void protectedfunction();//By calling this function, you can also access the three properties of test1 variable
private:
    int private1 = 0;
    void privatefunction();//By calling this function, you can also access the three properties of test1 variable

	friend void fritest1(test1 testfri);//friend function

//In C + +, the function of a class can access all its variables as long as it can be called.
};

At this time, the externally defined fritest1 function can access the restricted protected and private objects.

For a friend class, if test7 is defined as a friend class of test1, the instance of test7 can also access all members of test1.

However, two points need to be noted at this time. First, we assume that there are two new classes test8 and test9
test8 inherits from test1 and test9 inherits from test7
be

  • test7 can access all objects inherited from test1 in test8, but test8 itself does not have the special access nature of friends
  • test9 has lost its special access to test1's friends
  • Of course, test9 cannot access test8 friends

polymorphic

What is polymorphism? In the object-oriented method, the so-called polymorphism is that different objects receive the same message and produce different behaviors. In C + + programming, polymorphism refers to defining different functions with one name, and these functions perform different but similar operations, so that functions with different contents can be called with the same function name.

For polymorphism, a simple example is the operator. For example, we can use the operator +. We can realize the addition operation between integer numbers, floating-point numbers and double precision types. These three types of addition operations are actually different from each other and are realized by functions with different contents. This example uses polymorphic features.

Take a simple example

class test1
{
public:
	virtual void showtest()
	{cout << "test1" << endl;}
};

class test2 : public test1
{
public:
	void showtest()
	{cout << "test2" << endl;}
};

void Func(test1 &test)
{
	test.showtest();
}

int main()
{
	test1 a;
	test2 b;

	Func(a);
	Func(b);

	return 0;
}

After the above code is executed, the results should be test1 and test2.
Here comes the application of a virtual keyword, which corresponds to an important concept of polymorphism, virtual function.
Virtual functions refer to those functions that add the virtual keyword before the member function of the class, and the rewriting of virtual functions refers to that there is a virtual function in the derived class that is exactly the same as that of the base class. We call the virtual function of the subclass rewriting the virtual function of the base class. "Exactly the same" means that the function name, parameter and return value are the same. In addition, the rewriting of virtual functions is also called the covering of virtual functions.

It should be noted here that the following two conditions need to be met to form polymorphism:

  • The object calling the function must be a pointer or reference.
  • The called function must be a virtual function and the virtual function has been rewritten.

It should be noted here that polymorphic functions satisfying the above conditions and rewriting are also called general polymorphic functions.

Correspondingly, the Func function parameter in the above example uses a reference to the parent class. The differences between the two are as follows.

void Func(test1 &test)
{
	test.showtest();
}

void Func(test1 test)
{
	test.showtest();
}

The difference between the above two is that the former meets the above-mentioned condition 1 of polymorphism; If it is changed to the following form, the output results are test1 and test1. This kind of polymorphic function is called special polymorphic function. The important significance of this process is to ensure that the function also has polymorphism in the parameter transfer process, so that the function can flexibly identify the polymorphic functions of subclasses when calling.

Polymorphic functions also contain a pure virtual function, that is, write = 0 after the virtual function. Classes containing pure virtual functions are called abstract classes (also called interface classes). Abstract classes cannot instantiate objects. A derived class cannot instantiate an object after inheritance. Only by overriding pure virtual functions can derived classes instantiate objects. Pure virtual functions regulate that derived classes must be rewritten. In addition, pure virtual functions embody interface inheritance. When the base class has no practical significance, but the derived classes will be called in a large number, pure virtual functions are often used.

Vector graph

The vector graphics implemented in this experiment are based on the framework of QT, which involves a special QPainter library. QPainter is the library of QT's graphics rendering system. Its specific use and cases need special learning. The author here is only for examples and has not conducted in-depth research. If necessary, please find relevant functions and use methods by yourself.

The project structure is shown in the figure above. Among them, shapell is a dynamic library, which is responsible for the implementation of vector graph; main is qt's own window program.

Due to time reasons, this experiment only realizes simple examples of point and triangle graphics.

shapedll.h

#ifndef SHAPEDLL_H
#define SHAPEDLL_H
#include<string>
#include<math.h>
#include<QPainter>
#include<vector>

using namespace std;

#include "shapedll_global.h"

class SHAPEDLL_EXPORT Shapedll
{
public:
    Shapedll();
};

class CPoint;
class CRect;
class SHAPEDLL_EXPORT 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(QPainter & painter) const;
    virtual CShape* Clone() const;
    virtual CShape& Move(int nOffsetX,int nOffsetY);
    protected:
    string m_sName;
};
class SHAPEDLL_EXPORT CPoint:virtual public CShape
{
public:
    int m_nPosX;
    int m_nPosY;
    CPoint(){}
    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(QPainter & painter) const;
    CPoint* Clone() const;
    CPoint& Move(int nOffsetX,int nOffsetY);
};
class SHAPEDLL_EXPORT CTriangle:virtual public CShape
{
public:
    CTriangle(const CPoint& pt1,const CPoint& pt2,const CPoint& pt3);
    CTriangle(const CTriangle & rc);
    virtual ~CTriangle();
    double GetArea() const;
    bool ptIn(const CPoint& pt) const;
    bool InRect(const CRect& rc) const;
    void Draw(QPainter & painter) const;
    CTriangle* Clone() const;
    CTriangle& Move(int nOffsetX,int nOffsetY);
    CPoint m_pts[3];
};
class ShapeManager
{
public:
	ShapeManager();
	void Add(CShape* pShape);
	void Remove(CShape* pShape);
	void Add(vector<CShape*> shapes);
	void Remove(vector<CShape*> shapes);
	void RemoveAll();
	~ShapeManager();
     CShape * ptIn(const CPoint & pt);
     bool InRect(const CRect & rc, vector<CShape*> &shapesOut);
     void Draw(QPainter & painter, vector<CShape*>&shapes);
     void Draw(QPainter & painter);
	void Clone(vector<CShape*>&shapesIn, vector<CShape*>&shapesOut);
     void Move(int nOffsetX, int nOffsetY, vector<CShape*> &shapes);
 private:
	vector<CShape*> m_pShapes;
};
#endif //SHAPEDLL_H

shapedll.cpp

#include "shapedll.h"
#include<iostream>
using namespace std;
#include<QPaintEvent>
#include<QPainter>

Shapedll::Shapedll()
{
}

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(QPainter & painter) const
{
}
CShape* CShape::Clone() const
{
    return new CShape(*this);
}
CShape& CShape::Move(int nOffsetX, int nOffsetY)
{
    return *this;
}


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() {
}
 
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);
}
//Draw graphics
void CPoint::Draw(QPainter& painter) const
{
    painter.drawPoint(m_nPosX, m_nPosY);
}
CPoint* CPoint::Clone() const {
    return new CPoint(*this);
}
 
//Move point
CPoint& CPoint::Move(int nOffsetX, int nOffsetY) {
    m_nPosX += nOffsetX;
    m_nPosY += nOffsetY;
    return *this;
}
//triangle
CTriangle::CTriangle(const CPoint& pt1, const CPoint& pt2, const CPoint& pt3) {
    m_pts[0] = pt1;
    m_pts[1] = pt2;
    m_pts[2] = pt3;
}
 
//Move triangle
CTriangle& CTriangle::Move(int nOffsetX, int nOffsetY) {
    for (int i = 0; i < 3; i++) {
        m_pts[i].Move(nOffsetX, nOffsetY);
    }
    return *this;
}
 
CTriangle::CTriangle(const CTriangle& tri) {
    for (int i = 0; i < 3; i++) {
        m_pts[i] = tri.m_pts[i];
    }
}
CTriangle::~CTriangle() {
}
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]);
}
 
//Draw a triangle
void CTriangle::Draw(QPainter & painter) const
 {
     painter.drawLine(m_pts[0].m_nPosX, m_pts[0].m_nPosY, m_pts[1].m_nPosX, m_pts[1].m_nPosY);
     painter.drawLine(m_pts[1].m_nPosX, m_pts[1].m_nPosY, m_pts[2].m_nPosX, m_pts[2].m_nPosY);
     painter.drawLine(m_pts[2].m_nPosX, m_pts[2].m_nPosY, m_pts[0].m_nPosX, m_pts[0].m_nPosY);
}
ShapeManager::ShapeManager()
{
 
}
void ShapeManager::Add(CShape *pShape)
{
    m_pShapes.push_back(pShape);
}
void ShapeManager::Remove(CShape *pShape)
{
    for(vector<CShape*>::iterator it=m_pShapes.begin();it!=m_pShapes.end();it++){
        if(*it==pShape)
        {
            delete pShape;
            m_pShapes.erase(it);
        }
    }
}
void ShapeManager::Add(vector<CShape*> shapes)
{
    m_pShapes.insert(m_pShapes.begin(),shapes.begin(),shapes.end());
 
}
void ShapeManager::Remove(vector<CShape*> shapes)
{
     for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++) {
        Remove(*it);
    }
}
void ShapeManager::RemoveAll()
{
    for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++) {
        delete *it;
   }
    m_pShapes.clear();
}
 
ShapeManager::~ShapeManager()
{
    RemoveAll();
}
 
CShape * ShapeManager::ptIn(const CPoint& pt)
{
     for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++) {
        if((*it)->ptIn(pt))
        {
            return *it;
        }
    }
    return  NULL;
}
 
bool ShapeManager::InRect(const CRect& rc,vector<CShape*> &shapesOut)
{
    shapesOut.clear();
     for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++) {
        if((*it)->InRect(rc))
        {
            shapesOut.push_back(*it);
         }
    }
}
 
void ShapeManager::Draw(QPainter& painter, vector<CShape*>& shapes)
{
   for (vector<CShape*>::iterator it = shapes.begin();it!=shapes.end();it++) {
        (*it)->Draw(painter);
    }
}
 
void ShapeManager::Draw(QPainter& painter)
{
    for (vector<CShape*>::iterator it = m_pShapes.begin();it!=m_pShapes.end();it++) {
        (*it)->Draw(painter);
    }
}
 
void ShapeManager::Clone(vector<CShape*>& shapesIn,vector<CShape*>& shapesOut)
{
    shapesOut.clear();
    for (vector<CShape*>::iterator it = shapesIn.begin();it!=shapesIn.end();it++) {
        shapesOut.push_back((*it)->CLone());
   }
}
 
void ShapeManager::Move(int nOffsetX, int nOffsetY, vector<CShape *> &shapes)
{
    for(vector<CShape*>::iterator it=shapes.begin();it!=shapes.end();it++)
    {
        (*it)->Move(nOffsetX,nOffsetY);
    }
}

The specific operation cannot run after the completion of the construction due to the problems in the machine qt configuration used in the experiment; Subsequent operation screenshots will be supplemented after commissioning; At the same time, there are still problems in the code of ShapeManager, which will also be modified later. Theoretically, the above code can be run directly with a few lines of simple demo code without problems.

Topics: C++ Qt