项目问题解决记录(C++&Qt)
本人是个菜鸟
本博客主要用于记录项目中遇到的C++和Qt问题
不定时更新
普通知识点
- 在类定义中的定义的函数都是** 内联函数 **,即使没有使用 inline 说明符;
优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效。 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联。
缺点: 滥用内联将导致程序变慢。 内联可能使目标代码量或增或减, 这取决于内联函数的大小。
结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数。
- 继承有public, protected, private三种继承方式:
- public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
- protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected,protected, private
- private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private。
如果继承时不显示声明是 private,protected,public 继承,则默认是 private 继承,在 struct 中默认 public 继承:
-
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。(官方说的有点抽象,后面有实例更好理解)
函数后面加const加语句块
这是把整个函数修饰为const,意思是“函数体内不能对成员数据做任何改动”。如果你声明这个类的一个const实例,那么它就只能调用有const修饰的函数。
QColor textColor() const { return myTextColor; } // 把整个函数修饰为const,后面的花括号是函数实现
QColor itemColor() const { return myItemColor; }
QColor lineColor() const { return myLineColor; }
构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
具有以下特点
- 构造函数的名称与类的名称是完全相同
- 不会返回任何类型,也不会返回 void
- 构造函数可用于为某些成员变量设置初始值
** 实例:**
实例来自菜鸟教程:
https://www.runoob.com/cplusplus/cpp-constructor-destructor.html
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
** 运行结果: **
Object is being created, length = 10
Length of line : 10
Length of line : 6
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
具有以下特点
- 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀
- 它不会返回任何值,也不能带有任何参数。
- 析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
实例:
实例来自菜鸟教程:
https://www.runoob.com/cplusplus/cpp-constructor-destructor.html
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0; // 自动调用析构函数
}
** 运行结果: **
Object is being created
Length of line : 6
Object is being deleted
override
override保留字表示当前函数重写了基类的虚函数
** 目的 **:
- 在函数比较多的情况下可以提示读者某个函数重写了基类虚函数(表示这个虚函数是从基类继承,不是派生类自己定义的);
- 强制编译器检查某个函数是否重写基类虚函数,如果没有则报错。
虚函数
虚函数的作用:用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。
实例:
实例来自百度知道:
https://zhidao.baidu.com/question/1797274966344020227.html
#include<iostream>
usingnamespacestd;
classA
{
public:
voidprint()
{
cout<<"ThisisA"<<endl;
}
};
classB:publicA
{
public:
voidprint()
{
cout<<"ThisisB"<<endl;
}
};
int main()
{
//为了在以后便于区分,我这段main()代码叫做main1
Aa;
Bb;
a.print();
b.print();
return0;
}
输出结果分别是“ThisisA”、“ThisisB”。通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。那现在就把main()处的代码改一改。
intmain()
{
//main2
Aa;
Bb;
A*p1=&a;
A*p2=&b;
p1->print();
p2->print();
return0;
}
运行一下看看结果,哟呵,蓦然回首,结果却是两个This is A。问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数
classA
{
public:
virtualvoidprint(){cout<<"ThisisA"<<endl;}
};
classB:publicA
{
public:
voidprint(){cout<<"ThisisB"<<endl;}
};
毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。
现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。
总结:指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
QGraphicsScene与QGraphicsview与item
QGraphics图形视图框架由3部分组成:
- QGraphics Scene
- Graphics Item
- Graphics View
Scene负责提供图形视图框架的场景,相当于一块画布,管理所有的Items,并具有以下功能:
- 一个管理大量图形项的快速接口。
- 向每个图形项传播事件
- 管理图形项的状态,比如选择,焦点处理等
- 提供无转换的渲染功能,主要用于打印
添加:addItem(item) // scene的坐标
删除:removeItem(item)
设置背景为图片:setBackgroundBrush(QPixmap(""))
场景边界矩形:setSceneRect(QRectF())
View提供一个观察QGraphicsScene的实例窗口。视图窗口是一个可以滚动的区域,提供一个滚动条来浏览大的场景。
放大缩小:QGraphicsView::scale(xScale, yScale); //在分别在x,y方向上缩放xScale,yScale倍。若为1.0倍,则不进行缩放。
view和scene:QGraphicsView::setScene() // view绑定scene,view和scene按照居中对齐的方式进行显示
Item为编写自己的自定义项提供了一个轻量级的基础。这包括定义项目的几何形状,碰撞检测,它的绘画实现和通过事件处理程序的项目交互。
为了方便使用,Qt提供了一些最常用的基础标准图元:
- QGraphicsEllipseItem 提供椭圆对象
- QGraphicsLineItem 提供线对象
- QGraphicsPathItem 提供路径对象
- QGraphicsPixmapItem 提供图像对象
- QGraphicsPolygonItem 提供多边形对象
- QGraphicsRectItem 提供矩形对象
- QGraphicsSimpleTextItem 提供文本标签对象
- QGraphicsTextItem 提供高级文本浏览对象
当这些不满足需求时(例如:需要一些特定形状时),往往需要自定义,通常的做法就是继承 QGraphicsItem(QGraphicsObject)
参考:
https://blog.csdn.net/Bing_Lee/article/details/106106414
https://blog.csdn.net/xiezhongyuan07/article/details/79262573
自定义Item
必须要重写两个虚函数
void paint():以本地坐标绘制 item 的内容
QRectF boundingRect():将 item 的外边界作为矩形返回由 QGraphicsView 调用以确定什么区域需要重绘
除此之外,可能还需要附加其他需求,例如:
- QPainterPath shape() item 的形状 由 contains() 和 collidesWithPath() 用于碰撞检测。如果未实现,则默认为 boundingRect()。
- 使用信号/槽、属性机制:继承 QObject 和 QGraphicsItem(或直接继承 QGraphicsObject)
- 处理鼠标事件:重新实现 mouse···Event()
- 处理键盘事件:重新实现 key···Event()
- 处理拖放事件:重新实现 drag···Event()、dropEvent()等等
下图显示两函数主要设置什么
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cGzESUGz-1633746670045)(D:\Downloads\shape&boundingRect.png)]
参考:
https://www.cnblogs.com/klcf0220/p/10316510.html
限制QGraphicsScene内部Item的移动范围
项目中,item会随着鼠标移动,移动到视图窗口以外的地方去,要限制item的移动范围,查资料显示,QGraphicsItem提供了虚函数
QVariant QGraphicsItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
ItemPositionChange函数用于通知所属QGraphicsItem的位置即将发生变化,而value的值即为QGraphicsItem将来的位置坐标
QVariant Networkitem::itemChange(GraphicsItemChange change, const QVariant& value)
{
if (change == ItemPositionChange && scene()) // 控件是否发生移动
{
for (Arrow* arrow : qAsConst(arrows)) // 更新arrow
arrow->updatePosition();
QPointF newPos = value.toPointF(); //即将要移动的位置
QRectF rect(0, 0, scene()->width(), scene()->height()); // 限制的区域,scene需要提前设定好大小
if (!rect.contains(newPos)) // 是否在区域内
{
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left()))); // 设置限制
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
return newPos;
}
}
return QGraphicsItem::itemChange(change, value); // Qt图形机制,不必深究
}
参考:
https://blog.csdn.net/diaotaoning2896/article/details/102055010