目录
CPoint CTriangle CRect CCircle CircleInRect
一、继承访问权限测试
1、概述
C++面向对象的三大特性为:封装、继承、多态
继承是面向对象程序设计中最重要的概念之一。继承的作用是允许我们依据一个类来定义另外一个类,这就意味着创建和维护一个应用程序变得更容易,创建一个类时,不需要再重新编写新的数据成员和函数,而是直接使用已有的类的成员。这个已有的类称为基类,新建的类为派生类。比如,后面代码中的 Animal类是基类,而其他的动物类是派生类。
我们在设计类的时候,可以把属性和行为(成员变量和函数)放在不同的权限下
访问权限有三种:
- public 公共权限 类内可以访问,类外也可以访问
- protected 保护权限 类内可以访问,类外不可以访问
- private 私有权限 类内可以访问,类外不可以访问
其中
- private是完全私有的,只有自己可以访问,派生类和外部都不可以访问。
- protected是受保护的,只有派生类可以访问,外部不能访问。
同时
- 保护继承(protected):如果继承方式为保护继承,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):如果继承方式为私有继承,基类的公有和保护成员将成为派生类的私有成员。
2、代码
- 设计类A具有public, protected, private等不同属性的成员函数或变量;
- 类B通过public, protected, private等不同方式继承A,在类B的成员函数中测试访问A的成员函数或变量;
- 在类B中添加public, protected, private等不同属性的成员函数或变量,在外部测试访问B的各个成员函数或变量;
- B以private方式继承A,尝试把A中的部分public成员提升为public。
#include<iostream>
using namespace std;
class Person
{
public:
string name; //姓名
protected:
string sex; //性别
private:
int age; //年龄
};
//1.public继承
class Student1 :public Person
{
public:
void test() {
name = "hello world";
sex = "male";
//age=10; age不可访问
}
int weight;//体重
protected:
int height;//身高
private:
int eye;//视力
};
//2.protected继承
class Student2 :protected Person
{
public:
void test() {
name = "hello world";
sex = "male";
//age=10; age不可访问
}
int weight;//体重
protected:
int height;//身高
private:
int eye;//视力
};
//3.private继承
class Student3 :private Person
{
public:
void test() {
name = "hello world";
sex = "male";
//age=10; age不可访问
}
int weight;//体重
protected:
int height;//身高
private:
int eye;//视力
};
void test()
{
Person p;
p.name;
p.sex;
p.age;
Student1 s1;
Student2 s2;
Student3 s3;
s1.name;
s1.weight;
s1.height;
s1.eye;
s2.name;
s2.weight;
s2.height;
s2.eye;
s3.name;
s3.weight;
s3.height;
s3.eye;
}
结果如下图所示
二、友元类继承测试
1、概述
由前面的实验已知,私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。
在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。
2、代码
#include <iostream>
using namespace std;
//设计类A含有私有变量a,在类A中友元给类C;
class A {
private:
int a;
friend class C;
};
//设计类B继承A,添加私有变量b;
class B :public A {
private:
int b;
};
//在类C中测试访问类B的成员变量a, b;
class C {
public:
void Test() {
B b1;
b1.a; //友元 可以成功访问Class A 中的私有变量a
b1.b;
}
};
//设计类D继承C,在D的成员函数中测试访问类A的成员变量a,类B的成员变量a, b。
class D :public C {
public:
void Test() {
A a1;
a1.a;
B b2;
b2.a;
b2.b;
}
};
结果如下图所示
- 当class A友元给class C 时,则C可访问A中所有成员函数和变量。
- 友元关系是单向的,不具有交换性。
三、多态性综合运用
一般多态性函数
输入输出参数完全一样,在父类中添加virtual
#include <iostream>
using namespace std;
class Animal{
public:
void outPut();
};
void Animal::outPut() {
cout << "Animal output!" << endl;
}
class dog : public Animal {
public:
void outPut();
};
void dog::outPut() {
cout << "dog output!" << endl;
}
int main()
{
Animal p;
p.outPut();
dog d;
d.outPut();
Animal* ptr = new dog();
ptr->outPut();
return 0;
}
子类重写父类的非virtual 方法,当父类指针指向子类对象时,通过指针调用该成员函数,调用的是父类的方法
class Animal{
public:
virtual void outPut();
};
当子类重写父类的virtual函数时。在父类的函数声明中,前面加上virtual,父类指针调用的是实际指向对象的output函数
特殊多态性函数
输入或输出参数 在子类中是父类的指针或基类的引用,在子类中对于的是子类的指针或子类的引用
#include <iostream>
using namespace std;
class Animal{
public:
virtual void outPut(){
cout << "Animal output!" << endl;
}
};
class Dog : public Animal {
public:
void outPut(){
cout << "dog output!" << endl;
}
};
class Cat : public Animal {
public:
void outPut(){
cout << "cat output!" << endl;
}
};
void out1(Animal & X){
X.outPut();
};
void out2(Animal * X){
X->outPut();
};
int main()
{
Animal A ;
out1(A);
out2(&A);
Dog d;
out1(d);
out2(&d);
Cat c;
out1(c);
out2(&c);
return 0;
}
子类可能含有一些父类没有的成员变量或者方法函数,但是子类肯定继承了父类所有的成员变量和方法函数。
所以用父类指针指向子类时,是可以的,因为父类有的,子类都有,不会出现非法访问问题。但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法。虽然父类指针可以指向子类,但是其访问范围还是仅仅局限于父类本身有的数据,那些子类的数据,父类指针是无法访问的。
析构函数的多态性
虚析构函数
基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。
#include <iostream>
using namespace std;
class A
{
public:
virtual ~A(){
cout<<"A delete"<<endl;
}
};
class B : public A
{
public:
~B(){
cout<<"B delete"<<endl;
}
};
int main()
{
A* a = new B;
delete a;
return 0;
}
运行结果
多继承
多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员
#include <iostream>
using namespace std;
//间接基类A
class A{
protected:
int m_a;
};
//直接基类B
class B: virtual public A{ //虚继承
protected:
int m_b;
};
//直接基类C
class C: virtual public A{ //虚继承
protected:
int m_c;
};
//派生类D
class D: public B, public C{
public:
void seta(int a){ m_a = a; } //命名冲突
void setb(int b){ m_b = b; } //正确
void setc(int c){ m_c = c; } //正确
void setd(int d){ m_d = d; } //正确
private:
int m_d;
};
int main(){
D d;
return 0;
}
因为类 B 和类 C 中都有成员变量 m_a(从 A 类继承而来),编译器不知道选用哪一个,所以产生了歧义
所以,为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员
设计矢量图(CShape)
CShape.h
#pragma once
#ifndef CSHAPE_H
#define CSHAPE_H
#include<QPainter> https://blog.csdn.net/cpp_learner/article/details/114678143
#include<string>
#include<vector>
#include<math.h>
using namespace std;
class CPoint;
class CRect;
class CShape {
public:
CShape(); //构建无参构造器
CShape(const CShape& shape);
virtual ~CShape(); //虚析构函数
virtual double GetArea() const; //获取图形面积 已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理 https://blog.csdn.net/qq_32739503/article/details/83341222?spm=1001.2101.3001.6661.1&depth_1-
virtual bool ptIn(const CPoint& pt) const; //判断点是否在图形内
virtual bool InRect(const CRect& rc) const;
virtual void Draw(QPainter& painter) const;
//C++中函数名字前的& 或* https://blog.csdn.net/weixin_45525272/article/details/106234285
//https://www.cnblogs.com/fly1988happy/archive/2011/12/14/2286908.html
virtual CShape* CLone() const;
virtual CShape& Move(int nOffsetX, int nOffsetY);
/* 代码区:存放函数体的二进制代码,由操作系统进行管理的
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放, 存放函数的参数值, 局部变量等
堆区:由程序员分配和释放, 若程序员不释放, 程序结束时由操作系统回收*/
protected:
string m_sName;
};
class 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 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 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;
CShape* Clone() const;
CShape& Move(int nOffsetX, int nOffsetY);
CPoint m_ptLT;
CPoint m_ptBR;
};
class CCircle : virtual public CShape
{
public:
CCircle(CPoint ptCenter, double dR);
CCircle(const CCircle & rc);
virtual ~CCircle();
double GetArea() const;
bool ptIn(const CPoint & pt) const;
bool InRect(const CRect & rc) const;
void Draw(QPainter & painter) const;
CCircle * Clone() const;
CCircle & Move(int nOffsetX, int nOffsetY);
CPoint m_ptCenter;
double m_dR;
};
class CircleInRect : public CCircle, public CRect
{
public:
CircleInRect(CPoint ptCenter, double dR);
CircleInRect(const CircleInRect & rc);
virtual ~CircleInRect();
double GetArea() const;
bool ptIn(const CPoint & pt) const;
bool InRect(const CRect & rc) const;
void Draw(QPainter & painter) const;
CircleInRect * Clone() const;
CircleInRect & Move(int nOffsetX, int nOffsetY);
};
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);//get the first selected
bool InRect(const CRect & rc, vector<CShape*> &shapesOut);
void Draw(QPainter & painter, vector<CShape*>&shapes);
void Draw(QPainter & painter);
//auto input m_pShapes after cloned
void Clone(vector<CShape*>&shapesIn, vector<CShape*>&shapesOut);
void Move(int nOffsetX, int nOffsetY, vector<CShape*> &shapes);
private:
vector<CShape*> m_pShapes;
};
};
#endif // !CSHAPE_H
CShape.cpp
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(QPainter & painter) const
{
}
CShape* CShape::CLone() const
{
return new CShape(*this);
}
CShape& CShape::Move(int nOffsetX, int nOffsetY)
{
return *this;
}
CPoint CTriangle CRect CCircle CircleInRect
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(QPainter& painter) const
{
painter.drawPoint(m_nPosX, m_nPosY);
}
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(const CPoint& pt1, const CPoint& pt2, const CPoint& pt3) {
m_pts[0] = pt1;
m_pts[1] = pt2;
m_pts[2] = pt3;
}
//移动三角形
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() {
cout << "CTriangle::~CTriangle()\n";
}
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(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);
}
//矩形
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()
{
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(QPainter &painter) const
{
painter.drawRect(m_ptLT.m_nPosX,m_ptLT.m_nPosY)
}
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);
// https://www.html.cn/softprog/c/11422084099400.html
return *this;
}
CCircle::CCircle(CPoint ptCenter,double dR)
{
m_ptCenter = ptCenter;
m_dR = dR;
}
CCircle::CCircle(const CCircle & rc)
{
m_ptCenter = rc.m_ptCenter;
m_dR = rc.m_dR;
}
CCircle::~CCircle()
{
cout<<"Circle"<<endl;
}
double CCircle::GetArea() const
{
return m_dR*m_dR;
}
bool CCircle::ptIn(const CPoint& pt) const
{
return (pt.m_nPosX - m_ptCenter.m_nPosX)*(pt.m_nPosX - m_ptCenter.m_nPosX)+(pt.m_nPosY- m_ptCenter.m_nPosY)*(pt.m_nPosY- m_ptCenter.m_nPosY)<m_dR*m_dR;
}
bool CCircle::InRect(const CRect& rc) const
{
return ((m_ptCenter.m_nPosX - m_dR)>rc.m_ptLT.m_nPosX)&&
((m_ptCenter.m_nPosY - m_dR)>rc.m_ptLT.m_nPosY)&&
((m_ptCenter.m_nPosX+m_dR)<rc.m_ptBR.m_nPosX)&&
((m_ptCenter.m_nPosY+m_dR)<rc.m_ptBR.m_nPosY);
}
void CCircle::Draw(QPainter & painter) const
{
painter.drawEllipse(m_ptCenter.m_nPosX,m_ptCenter.m_nPosY,m_dR,m_dR);
}
CCircle* CCircle::Clone() const
{
return new CCircle(*this);
}
CCircle& CCircle::Move(int nOffsetX,int nOffsetY)
{
m_ptCenter.m_nPosX+=nOffsetX;
m_ptCenter.m_nPosY+=nOffsetY;
return *this;
}
CircleInRect::CircleInRect(CPoint ptCenter,double dR):
CCircle(ptCenter,dR),CRect(CPoint(ptCenter.m_nPosX - m_dR,ptCenter.m_nPosY - m_dR),CPoint(ptCenter.m_nPosX + m_dR,ptCenter.m_nPosY + m_dR))
{
}
CircleInRect::CircleInRect(const CircleInRect & rc):
CCircle(rc.m_ptCenter,rc.m_dR),CRect(rc.m_ptLT,rc.m_ptBR)
{
}
CircleInRect::~CircleInRect()
{
}
ShapeManager
//ShapeManager
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);
}
}
运行结果
main.cpp
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
//需要画图的内容
//设置画笔颜色
painter.setPen(QPen(QColor(0, 160, 230), 10));
//设置点
CPoint *point1=new CPoint(300,200);
CPoint *point2=new CPoint(150,260);
CPoint *point3=new CPoint(200,140);
//point1->Draw(painter);
//point2->Draw(painter);
//point3->Draw(painter);
//画三角形
CTriangle *triangle;
triangle=new CTriangle(*point1,*point2,*point3);
//triangle->Draw(painter);
//矩形
CRect *rect=new CRect(*point1,*point2);
//rect->Draw(painter);
//圆形
CPoint *center=new CPoint(300,300);
CCircle *circle=new CCircle(*center,50);
//circle->Draw(painter);
CPoint *point4=new CPoint(300,140);
printf("getarea %f\n",triangle->GetArea());
printf("ptIn %d\n",triangle->ptIn(*point4));
triangle->Move(100,100);
triangle->Draw(painter);
//ShapeManager测试
double r=50.0;
ShapeManager *shapemanager=new ShapeManager();
vector<CShape *> shapes;
for (vector<CPoint *>::iterator it=points.begin();it!=points.end();it++)
{
CShape *test = new CCircle(**it,r);
shapes.push_back(test);
}
CShape *shape =new CTriangle(*point1,*point2,*point3);
shapemanager->Add(shape);
shapemanager->Add(shapes);
shapemanager->Draw(painter);
写代码中遇到的问题
C++函数的返回值 : 返回引用类型和非引用类型
引用类型:在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数。
非引用类型:函数的返回值用于初始化在调用函数时创建的临时对象(temporary object),如果返回类型不是引用,在调用函数的地方会将函数返回值复制给临时对象
int& abc(int a, int b, int c, int& result)
{
result = a + b + c;
return result;
}
C++函数的返回值——返回引用类型&非引用类型https://www.cnblogs.com/fly1988happy/archive/2011/12/14/2286908.html
vector
C++ vector使用方法_w3cschoolhttps://www.w3cschool.cn/cpp/cpp-i6da2pq0.html
QT Painter
要想在绘图设备上(一般是窗口部件)上绘图,只需创建一个QPainter,再将指针传到该设备中