- 博客(128)
- 收藏
- 关注
原创 OpenGL实现摄像机(根据鼠标位置放大缩小视图)
在一个场景中最重要的就是和用户的交互,所以实现一个摄像机对整个程序的影响是很大的,本文旨在实现比较实用的摄像机。主要包括的功能为:按下鼠标左键对整个场景进行移动,按下鼠标右键对整个场景进行旋转操作,根据鼠标位置滚动滚轮进行视图的放大缩小。
2025-03-23 11:00:44
733
原创 OpenGL实现场景编辑器
最近在使用Qt+OpenGL+glew+freeimage实现一个简单的场景编辑器,先看看他的功能是什么。这个是软件的界面,中间的widget是用来显示场景的,左侧上方的dockwidget可以拖动模型到显示场景中,左侧下方是dockwidget是用于显示场景中加载的模型列表,右侧的dockwidget是用于显示当前模型的信息,包括他的位置和缩放比例。选中模型后会显示他的包围盒,左侧上方点击scene Action可以切换模式,是拖动场景还是拖动模型,拖动模型的时候会将所有显示包围盒的模型都进行移动。
2025-03-09 12:11:03
1103
原创 队列和栈的实现
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。队头(Front):允许删除的一端,又称队首。队尾(Rear):允许插入的一端。空队列:不包含任何元素的空表。栈(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。栈顶(Top):线性表允许进行插入删除的那一端。
2024-06-10 16:18:21
1111
原创 设计模式之创建型模式
创建型模式:创建对象的机制,从所需要实例化的对象中解耦。主要分成了五种设计模式,即工厂方法、抽象工厂、生成器、原型、单例。
2024-04-04 23:00:14
1194
原创 Qt实现画笔、擦除、保存功能
使用Qt的QPainter类,我们很容易就可以实现一个类似于画笔的功能,再重载QPaintEvent函数我们就可以使用这个画笔绘制图形到界面上。
2024-04-01 16:12:10
1886
原创 Qt实现无边框圆角窗口
这时候我们可以看到圆角是没有生效的(这是在windows11下,windows10下是有圆角的,但是还是有背景色)。解决方法:我们可以使用另外一个widget覆盖在QDialog窗口上,但是这个widget也需要设置圆角和背景色。我们在使用QDialog的时候许多场景下都不需要默认的标题栏,这时候我们需要设置他的标志位。由于现代的窗口风格,我们一般会设置窗口为圆角边框的样式,我们可以使用qss的方式来进行设置。但是这时候我们的窗口都不见了,因为背景色是透明色。这时候我们可以将背景色设置为透明的。
2024-04-01 15:33:08
1194
原创 Qt之QLabel介绍
QLabel是QT界面中的标签类,它从QFrame下继承,QLabel 类代表标签,它是一个用于显示文本或图像的窗口部件。我们主要介绍一下QLabel的一些简单的使用。
2024-01-30 22:51:57
1193
原创 条款32:确定你的public继承塑模出 is-a 关系
如果你编写类D(“派生类”)public继承类B(“基类”),就是在告诉C++编译器(以及代码的读者)每个类型D的对象都是类型B的对象,但反之则不然。public继承意味着“is-a”。适用于基类的所有内容也必须适用于派生类,因为每个派生类对象都是基类对象。public继承和is-a的等价关系听起来很简单,但有时你的直觉可能会误导你。类Square应该public继承类Rectangle吗?
2024-01-30 21:50:13
256
原创 cmake-find_package链接第三方库
之前我们是使用了绝对路径来链接OpenCV第三方库,但是现在很多库一般会自己写一些cmake文件提供给用户,用户可以直接使用其中的内置变量即可。使用的命令就是find_package。
2024-01-28 11:40:52
989
原创 条款31:将文件间的编译依存关系降至最低
C++在分离接口和实现方面做得不好。类定义不仅指定了类接口,还指定了相当多的实现细节。如果这些头文件发生了任何变化,或者它们依赖的任何头文件发生了变化,则必须重新编译所有包含Person类的文件。
2024-01-28 11:00:55
373
原创 cmake设置Debug版本和Release版本的输出路径
项目背景:指定可执行文件和动态库输出路径都在bin目录文件夹下,由于项目中存在osg插件,然后我在项目中需要重写osg的插件,这时候就会遇到指定输出路径的问题,由于需要输出到osgPlugins-3.6.5文件夹下,所以使用默认的输出方式不行。设置输出路径的属性。
2024-01-27 23:34:44
1142
原创 Qt使用中文字符串乱码的问题
我们在使用qt的时候有时候会遇到打印中文字符串的时候出现中文乱码的问题,主要是由于Qt的QString字符串存储的方式是使用utf-8的编码方式,如果我们本地的文件是使用GBK方式的编码再使用中文字符串的话就会出现打印乱码的情况。
2024-01-27 17:28:25
1738
原创 条款30:透彻了解 inlining 的里里外外
内联函数背后的思想是用它的代码体替换对该函数的每次调用,大多数是编译时的行为。这可能会增加目标代码的大小(如果函数特表小还可能减少目标代码的大小)。它看上去,用起来都是函数,还避免了起函数调用的开销。如果函数过于复杂,编译器会拒绝内联请求(例如,那些包含循环或递归的函数);由于编译时无法确定,所有的虚函数都不允许内联。构造基类部分、构造成员、如果在构造对象的过程中抛出异常,对象中任何已经完全构造好的部分都会自动被销毁。内联是对编译器的请求,而不是命令。构造函数和析构函数通常不适合内联。
2024-01-27 15:47:45
284
原创 条款29:为“异常安全”而努力是值得的
如果Image构造函数抛出异常,输入流的read标记可能被移动了,而这种移动对程序的其他部分来说是可见的状态变化。在changeBackground解决这个问题之前,它只能提供异常安全性的基本承诺。下面的代码改变了bgImage的类型,使用智能指针不但可以防止资源泄漏,还提供了异常安全性;2、强烈保证:如果抛出异常,程序的状态不会改变(要么全部成功,要么就像没发生过)。从异常安全性的角度来看,这个函数很糟糕。异常安全性有两个要求,这里都不满足。1、基本承诺:如果抛出异常,程序中的所有内容都保持有效状态。
2024-01-21 21:53:17
376
1
原创 条款28:避免返回 handles 指向对象的内部成分
即便如此,upperLeft和lowerRight仍然会返回对象内部的句柄,这在其他方面可能会造成问题。特别是,它可能导致悬空(dangling )句柄:指向不再存在的对象的句柄。避免返回指向对象内部的句柄(引用、指针或迭代器)。遵守这个原则将会增加了封装性,帮助const成员函数保持const行为,并可以尽量避免发生悬空句柄的创建。这个设计可以编译,但它是错误的。我们设计的类返回的是一个const修饰的对象,但是我们还是可以修改内部对象的值。
2024-01-21 16:49:38
389
原创 cmake-动态库和静态库及使用OpenCV第三方库
项目中会有单个源文件构建的多个可执行文件的可能。项目中有多个源文件,通常分布在不同子目录中。这种实践有助于项目的源代码结构,而且支持模块化、代码重用和关注点分离。同时,这种分离可以简化并加速项目的重新编译。
2024-01-21 16:30:01
2779
原创 条款27:尽量少做转型动作
前面的方法不允许将指向多种派生类的指针存储在同一个容器中。这样的C++生成的代码既大又慢,而且很脆弱,因为每次Window类层次结构发生变化时,都必须检查所有这些代码以确定是否需要更新。旧式强制转换仍然是合法的,但新风格更可取;在C++中,强制转换是一种需要高度重视的功能。首先回顾一下强制转换的语法,相同的强制转换通常有三种不同的方式。dynamic_cast的开销较大,深层的层次结构或使用多重继承的层次结构更加昂贵。有时,两个指针的值并不相同,需要使用偏移量,在运行时应用于Derived。
2024-01-16 22:36:32
505
原创 条款26:尽可能延后变量定义式出现的时间
尽可能推迟变量定义。它增加了程序的清晰度,提高了程序的效率。未使用的变量是有开销的,所以你应该尽可能避免使用它们。开销:1个构造函数+ 1个析构函数+ n个赋值。开销:n个构造函数+ n个析构函数。
2024-01-16 22:17:22
430
原创 条款25:考虑写出一个不抛异常的swap函数
C++11之前,std::swap是通过标准的交换算法完成的(C++ 11之后用的是std::move,并明确申明为noexcept)。一般来说,重载函数模板是可以的,但std是一个特殊的命名空间。这样的行为是未定义的,虽然大多数情况下可以正常编译。C++允许类模板的偏特化,但不允许函数模板的偏特化。要交换两个Widget对象的值,真正需要做的就是交换它们的pImpl指针,但默认的交换算法无法知道这一点。它试图访问a和b中的pImpl指针,它们是私有的,所以无法通过编译!
2024-01-15 22:06:44
573
原创 条款24:若所有参数皆需类型转换,请为此采用非成员函数
乘法应该是可交换的,但是由于参数的隐式类型转换只发生在参数列表中,所以2 * oneHalf会报错,由于2是int类型的参数。重载函数修改为非成员函数,编译器还会寻找非成员运算符。这时候我们可以将operator。
2024-01-15 21:51:22
395
原创 cmake-将源文件编译为可执行文件
我们创建一个CMakeLists.txt的文件,文件的名称区分大小写,必须命名为CMakeLists.txt,CMake才能够解析。这时候cmake会认为进入了子文件夹中,所以这时候我们来看一下子文件夹中的CMakeLists.txt文件的编写。这个过程类似于厨师炒菜,需要原材料指定给他,他就可以根据这些原材料做出你想要的菜。总而言之,编写cmake最核心的内容就是让厨师可以找到所有的原材料来进行炒菜。首先看一下我们的目录结构,在外层有一个CMakeLists.txt文件。第三行,添加包含的子文件夹。
2024-01-13 17:21:20
1068
原创 条款23:宁以 non-member、non-friend 替换 member 函数
优先选择非成员非友元函数而不是成员函数。这样做可以提高封装性、打包灵活性和功能可扩展性。
2024-01-13 11:04:49
376
原创 条款22:将成员变量声明为 private
更重要的是为了封装!如果你通过函数实现了对数据成员的访问,那么以后可以替换,使用者并不会察觉。将数据成员隐藏在函数接口之后可以提供各种实现灵活性。如何实现averageSoFar函数?
2024-01-10 22:19:10
378
原创 条款21:必须返回对象时,别妄想返回其引用
绝不要返回指针或引用指向一个local stack对象,或返回引用指向一个heap-allocated对象,或返回指针或对象指向一个local static对象而有可能同时需要多个这样的对象。也许你会想到一个基于operator*的实现,它返回一个定义在函数内部的静态Rational对象的引用。这里还是发生了构造和析构,更糟糕的是引用的对象被析构了。结果也是错误的,由于操作的都是同一块内存,所以执行后的结果都是最后一次执行的结果。让我们考虑在堆上构造一个对象并返回它的引用的可能性。
2024-01-10 22:12:35
572
原创 条款20:宁以常量引用传递替换值传递
通过引用传递参数也可以避免切割问题(slicing problem)。当继承类对象作为基类对象(按值)传递时,基类的拷贝构造函数会被调用,而那些派生类对象特有的部分会被“切掉”。调用了Student拷贝构造函数来初始化形参s。当validateStudent返回时,形参s被销毁。值传递的开销可能比较大。// 调用,被切割,而且没有多态。
2024-01-08 22:48:24
477
原创 条款18:让接口容易被正确使用,不易被误用
shared_ptr可以解决“跨DLL问题”。当在一个DLL中使用new创建对象,但在另一个DLL中delete时,会出现此问题。shared_ptr避免了这个问题,因为它的默认删除器来自创建shared_ptr的同一个DLL中。只有12个有效的月份值,Month类型应该反映这一点。一种方法是使用枚举来表示月份,但枚举的类型安全性不高。例如,枚举可以像int一样使用(参见条款2)。这会导致至少两种类型的客户错误:没有删除指针,以及多次删除同一个指针。
2024-01-07 17:05:19
625
原创 条款16:成对使用 new 和 delete 时要采用相同形式
对于喜欢使用宏typedef的人来说,这条规则也值得注意,因为它意味着当使用new来生成typedef类型的对象时,必须说明应该使用哪种形式的delete。内存数组通常包括数组的大小,delete可以知道需要调用多少个析构函数。下面程序的行为是未定义的。至少,stringArray指向的100个string对象中有99个不太可能被正确地析构。使用delete时使用了方括号,delete假定指向的是一个数组。为了避免这种混淆,请避免对数组类型使用typedef,可以使用vector替代。
2024-01-03 22:11:17
453
原创 条款15:在资源管理类中提供对原始资源的访问
经常有API需要访问原始资源,因此每个RAII类都应该提供方法来获取它管理的资源。访问可以通过显式转换或隐式转换。一般来说,显式转换更安全,而隐式转换对客户端来说更方便。
2024-01-02 22:14:38
418
原创 条款14:在资源管理类中小心拷贝行为
3.复制底部资源:有时可以有任意多个资源的副本,需要资源管理类的唯一原因是确保每个副本都能被正确释放。在这种情况下,复制资源管理对象也应该复制它包裹的资源。2、引用计数底层资源:保留资源,直到使用它的最后一个对象被销毁。std::shared_ptr会调用delete,而我们的Lock类,需要的是解锁。4.转移底部资源的所有权:在极少数情况下,需要确保只有一个RAII对象管理资源,当复制RAII对象时,需要转移资源的所有权。1、禁止复制:如果允许复制RAII对象没有意义,这时应该禁止它。
2024-01-01 21:16:22
494
原创 条款13:以对象管理资源
为了防止资源泄漏,使用RAII对象,它们在构造函数中获取资源,并在析构函数中释放资源。两个常用的RAII类是shared_ptr和unique_ptr。shared_ptr通常是更好的选择,因为它在复制时的行为很直观。
2023-12-31 20:09:25
385
原创 条款 12:拷贝对象的所有部分
编译器生成的拷贝函数(拷贝构造函数,拷贝赋值运算符),会拷贝对象的所有数据,当你声明自己的拷贝函数时,就是在告诉编译器,默认实现中有你不喜欢的地方。如果两个拷贝函数有很多重复代码,可以先构造一个,再用另一个调用它吗?构造函数会初始化新对象,但赋值运算符只适用于已经初始化的对象。在调用赋值的时候data数据由于没有进行赋值拷贝,所以在子类的赋值中缺失了该数据。如果没有调用的话进行赋值的时候就会导致基类部分的数据没有赋值过去。出现这个问题的最狡猾的方式之一是通过继承。拷贝时,后期添加的成员被遗忘了。
2023-12-30 21:57:11
394
原创 条款 11:在 operator= 中处理“自我赋值“
当对象被赋值给自身时,确保operator=表现良好。技术途径包括:比较源对象和目标对象的地址、仔细的语句排序、copy-and-swap。确保在两个或多个对象相同(是同一个对象)的情况下,函数的行为仍然正确。
2023-12-24 23:21:58
378
原创 条款 9:绝不在构造和析构过程中调用虚函数
解决这个问题有很多方法。一种是将logTransaction转换为非虚函数,然后要求派生类构造函数将必要的日志信息传递给基类构造函数。检测虚函数在构造或析构期间的调用并不总是那么容易。假设有个类的继承体系,用于建模股票交易,例如买入订单、卖出订单等。此类交易是可审计的,因此每次创建交易对象时,都需要在审计日志中创建适当的条目。
2023-12-24 17:44:20
409
原创 条款 8:别让异常逃离析构函数
析构函数永远不应该吐出(emit)异常。如果在析构函数中调用的函数可能抛出异常,则析构函数应该捕获任何异常,然后将其吞下或终止程序。如果类客户需要能够对操作期间抛出的异常做出反应,则类应该提供执行该操作的普通(即非析构函数)函数。
2023-12-23 22:31:24
414
原创 条款7:为多态基类声明虚析构函数
多态基类应该声明虚析构函数。如果一个类有虚函数,它应该有一个虚析构函数。非基类或非多态使用的类不应该声明虚析构函数。
2023-12-23 17:40:52
394
原创 条款6:若不想使用编译器自动生成的函数,就该明确拒绝
有些场景我们不需要编译器默认实现的构造函数,拷贝构造函数,赋值函数,这时候我们应该明确的告诉编译器,我们不需要,一个可行的方法是将拷贝构造函数和赋值函数声明为private。我们可以使用上面的类定义,编译器将阻止客户端拷贝HomeForSale对象的尝试,如果无意中试图在成员函数或友元函数中这样做,链接器将会报错。要禁用编译器自动提供的功能,请将相应的成员函数声明为private并且不提供任何实现。在c++11的标准中我们可以使用delete来删除函数,这样在编译的时候就会提示错误。
2023-12-20 22:56:05
394
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人