当前位置: 首页 > news >正文

Qt_一个由单例引发的崩溃

头图

Qt_一个由单例引发的崩溃

文章目录

  • Qt_一个由单例引发的崩溃
    • 摘要
    • 关于 Q_GLOBAL_STATIC
    • 代码测试
    • 布局管理器源码分析
    • Demo 验证
    • 关于布局管理器析构
    • Qt 类声明周期探索
    • 更新代码获取父类
    • 分析Qt 单例宏源码

关键字: QtQ_GLOBAL_STATIC单例UI崩溃

摘要

今天简直是令人心力交瘁的一天,在公司被一个顽固的Bug纠缠了整整一天。一开始,我对这个问题的认知并不深刻,只是觉得有点小瑕疵,于是比较轻松地着手解决。我开始摸索着定位问题,态度上也没太在意,毕竟在我看来,这只是一场小小的技术挑战。

然而,随着时间的推移,我逐渐意识到问题的严重性。逐渐加深的烦躁和困扰让我开始感到不安。在一度对问题轻描淡写的态度下,我终于被迫正视这个Bug所可能引发的连锁反应。随着这个问题的逐渐显露出其庞大的影响,我仿佛看到了一个漩涡,正在悄然蔓延着,威胁着整个系统的稳定性。

随着深入的调查和排查,我开始逐渐理解这个Bug的神秘本质,而这个认识过程伴随着我的焦虑逐渐升温。我发现这并非是一个简单的技术故障,而是一个可能牵动公司正常运营的重大问题。这时,我终于感受到了来自责任的重压,因为解决这个问题不仅关乎我的个人技术能力,更涉及到公司业务的持续稳健。

最终,当我对问题的了解达到顶峰时,我不禁意识到这一天的崩溃并非仅限于我的个人情绪,更是对整个系统运行稳定性的一次极大考验。这场对抗Bug的战斗让我感受到了技术领域的不可预测性和挑战性,也让我更加深刻地明白在这个领域中的学无止境。

image-20231127214955902

关于 Q_GLOBAL_STATIC

我在创建单例上偷了懒,使用了Qt Q_GLOBAL_STATIC宏来创建单例。关于Q_GLOBAL_STATIC的解释如下:

Q_GLOBAL_STATIC 是 Qt 框架中用于创建全局静态变量的宏。这个宏的目的是确保在多线程环境下安全地初始化和访问静态变量。在 C++ 中,全局静态变量的初始化顺序可能会带来一些问题,特别是在多线程环境下。

使用 Q_GLOBAL_STATIC 宏,Qt 提供了一种线程安全的机制来创建全局静态变量。这个宏可以确保静态变量只在第一次访问时被初始化,而且在初始化过程中是线程安全的。

具体用法如下:

Q_GLOBAL_STATIC(Type, variable)

其中,Type 是要创建的静态变量的类型,variable 是变量的名称。

举例说明:

#include <QGlobalStatic>class MyClass {
public:MyClass() {// 构造函数}// 其他成员函数和数据成员
};Q_GLOBAL_STATIC(MyClass, myGlobalInstance)

在上面的例子中,myGlobalInstance 就是一个全局静态变量,它的类型是 MyClass。使用 Q_GLOBAL_STATIC 宏,Qt 会在需要时确保 myGlobalInstance 被安全地初始化,并在整个程序运行期间保持其存在。

这种机制的好处是,在多线程环境下,多个线程可以同时访问 myGlobalInstance,而不必担心因为初始化顺序而导致的问题。Qt 在内部使用了一些技巧,比如使用双重检查锁定(double-checked locking)等,以确保在多线程环境下的性能和正确性。

代码测试

所以,按照上面的解释,我使用Q_GLOBAL_STATIC来创建一个单例,应该是没有问题的。但是。

下面看下的Demo 代码

头文件:

#ifndef FORM_H
#define FORM_H#include <QWidget>namespace Ui {
class Form;
}class Form : public QWidget
{Q_OBJECTpublic:explicit Form(QWidget *parent = nullptr);static Form* getInstance();~Form();private:Ui::Form *ui;
};#endif // FORM_H

源文件:

#include "form.h"
#include "ui_form.h"
Q_GLOBAL_STATIC(Form,m_Form)
Form::Form(QWidget *parent) :QWidget(parent),ui(new Ui::Form)
{ui->setupUi(this);
}Form *Form::getInstance()
{return m_Form;
}Form::~Form()
{delete ui;
}

调用文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "form.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->verticalLayout->addWidget(Form::getInstance());}MainWindow::~MainWindow()
{delete ui;
}

布局管理器源码分析

看到这里不知道你发现问题所在了没有呢。其实到这里,程序运行什么都都是没有问题的,就是不要退出程序,因为一旦退出程序,就会遇到崩溃问题。那么开始我一度以为是使用了布局管理器导致的这个问题,但是我看了源码也却突然发现,布局管理器是不会对其管理的控件进行资源释放的,部分源码如下,我这里就合并显示了。

QBoxLayoutPrivate::~QBoxLayoutPrivate()
{
}/*!Destroys this box layout.The layout's widgets aren't destroyed.
*/
QHBoxLayout::~QHBoxLayout()
{
}/*!The layout's widgets aren't destroyed.
*/
QVBoxLayout::~QVBoxLayout()
{
}

Demo 验证

保险起见,我还特意谷歌翻译了一下Destroys this box layout.The layout's widgets aren't destroyed.里面明确说明,布局会破坏,但是里面的控件不会释放。所谓问题来,到底是哪里的问题了,我尝试了另一种写法,代码如下,希望你还可以跟得上

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "form.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);Form * temp = Form::getInstance();temp->setParent(this);temp->show();
}MainWindow::~MainWindow()
{delete ui;
}

这样我得到的结果同样是崩溃的析构函数,如下图所示

image-20231127223544817

关于布局管理器析构

正当我一头雾水的手,我随机搜了一下Qt 布局管理器在析构是会析构子控件吗这个问题,得到的回答如下

在 Qt 中,布局管理器在析构时通常不会主动析构其管理的子控件。布局管理器负责管理控件的位置和大小,而控件的生命周期管理通常是由其父控件负责的。

当一个 QWidget 对象(可能是窗口或其他容器控件)被析构时,它会负责删除其子控件。这是因为 QWidget 继承自 QObject,而 QObject 有一个父子对象关系系统,负责自动管理子对象的内存。

布局管理器在 Qt 中主要用于确定子控件的布局,而不是控制它们的生命周期。因此,当布局管理器本身被析构时,它不会主动销毁其管理的子控件。子控件的析构通常是由其父控件来处理。

如果你希望在删除布局管理器的同时也删除其管理的子控件,你需要手动迭代子控件并将其删除,或者使用智能指针等工具来管理控件的生命周期。这可以确保在删除布局管理器时,相关的子控件也会被正确地释放。

总的来说,Qt 的布局管理器并不负责子控件的析构,而是留给控件的父对象来管理。

结合上面对布局管理器源码的分析,可以看到,我们的崩溃不是有布局管理器引起的,而是被父类析构析构了。

Qt 类声明周期探索

本着刨根问题的原则,我有问了另一个问题设置了布局管理器就等于设置了父类吗

在 Qt 中,设置了布局管理器并不等同于设置了父类。虽然布局管理器通常是作为容器控件的子对象来管理的,但它们在概念上是不同的。

  1. 父子关系:

    • 当你将一个控件(例如 QWidget)设置为另一个控件的子控件时,它们建立了父子关系。这样做的话,父控件会负责管理子控件的生命周期,包括在父控件销毁时自动删除子控件。
    • 布局管理器是一种机制,用于控制子控件的布局,但它们通常不会成为父控件。布局管理器可以安排子控件的位置和大小,但并不会负责管理它们的生命周期。
  2. 布局管理器的作用:

    • 布局管理器是用来管理布局的工具。它们通常被设置在容器类控件上,比如 QWidget、QMainWindow、QDialog 等。布局管理器负责决定子控件的排列方式,但不负责管理它们的析构。

下面是一个简单的例子,演示了如何在 QWidget 中使用布局管理器:

#include <QHBoxLayout>
#include <QPushButton>
#include <QWidget>int main(int argc, char *argv[]) {QApplication app(argc, argv);QWidget *mainWidget = new QWidget;QHBoxLayout *layout = new QHBoxLayout(mainWidget);QPushButton *button1 = new QPushButton("Button 1");QPushButton *button2 = new QPushButton("Button 2");layout->addWidget(button1);layout->addWidget(button2);mainWidget->setLayout(layout);mainWidget->show();return app.exec();
}

在这个例子中,mainWidget 是一个 QWidget,而 layout 是一个 QHBoxLayout。布局管理器 layout 被设置为 mainWidget 的布局管理器。然而,mainWidget 仍然是这两个按钮的父控件,而不是布局管理器。

总的来说,在设置布局管理器时,确保理解父子关系的变化,并注意生命周期管理的责任。

更新代码获取父类

虽然回答的不是那么一回事,但是还是启发了,所以我对我的代码做了一点更新,如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "form.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);Form * temp = Form::getInstance();ui->verticalLayout->addWidget(temp);qDebug()<< "temp->parent()->" << temp->parent() << endl<< "temp->parent()->parent()->" << temp->parent()->parent() << endl<< "this->"<< this << endl<< "this->parent()->" <<this->parent() << endl<< "ui->verticalLayout->parent()" << ui->verticalLayout->parent();
}MainWindow::~MainWindow()
{delete ui;
}

得到打印信息如下

temp->parent()-> QWidget(0x948920, name = "centralwidget") 
temp->parent()->parent()-> MainWindow(0x8ffd20, name = "MainWindow") 
this-> MainWindow(0x8ffd20, name="MainWindow") 
this->parent()-> QObject(0x0) 
ui->verticalLayout->parent() QWidget(0x948920, name = "centralwidget")

这下基本明白了,其实对象的声明周期在Qt里面还是受气父控件的控制,而并非是布局管理器,今天我之所碰巧解决了这个Bug,仅仅是因为当我们吧控件添加到布局管理器时。不管理器把他的费空间做了传递、现在终于明白了。

分析Qt 单例宏源码

那么还需要解决另一个问题,就是我的单例宏做了什么。

#define Q_GLOBAL_STATIC(TYPE, NAME)                                         \Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)                         \namespace { namespace Q_QGS_ ## NAME {                                  \typedef TYPE Type;                                                  \QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \Q_GLOBAL_STATIC_INTERNAL(ARGS)                                      \} }                                                                     \static QGlobalStatic<TYPE,                                              \Q_QGS_ ## NAME::innerFunction,                     \Q_QGS_ ## NAME::guard> NAME;
#if defined(Q_COMPILER_CONSTEXPR)
#  define Q_BASIC_ATOMIC_INITIALIZER(a)     { a }
#else
#  define Q_BASIC_ATOMIC_INITIALIZER(a)     { ATOMIC_VAR_INIT(a) }
#endif
#define Q_GLOBAL_STATIC_INTERNAL(ARGS)                          \Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction()   \{                                                           \struct HolderBase {                                     \~HolderBase() noexcept                        \{ if (guard.loadRelaxed() == QtGlobalStatic::Initialized)  \guard.storeRelaxed(QtGlobalStatic::Destroyed); }     \};                                                      \static struct Holder : public HolderBase {              \Type value;                                         \Holder()                                            \noexcept(noexcept(Type ARGS))       \: value ARGS                                    \{ guard.storeRelaxed(QtGlobalStatic::Initialized); }       \} holder;                                               \return &holder.value;                                   \}
#else
// We don't know if this compiler supports thread-safe global statics
// so use our own locked implementationQT_END_NAMESPACE
#include <QtCore/qmutex.h>
#include <mutex>
QT_BEGIN_NAMESPACE#define Q_GLOBAL_STATIC_INTERNAL(ARGS)                                  \Q_DECL_HIDDEN inline Type *innerFunction()                          \{                                                                   \static Type *d;                                                 \static QBasicMutex mutex;                                       \int x = guard.loadAcquire();                                    \if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) {           \const std::lock_guard<QBasicMutex> locker(mutex);           \if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {        \d = new Type ARGS;                                      \static struct Cleanup {                                 \~Cleanup() {                                        \delete d;                                       \guard.storeRelaxed(QtGlobalStatic::Destroyed);         \}                                                   \} cleanup;                                              \guard.storeRelease(QtGlobalStatic::Initialized);        \}                                                           \}                                                               \return d;                                                       \}
#endif

让我们稍作翻译

这段宏定义了一个模板化的全局静态变量创建机制,通过 Q_GLOBAL_STATIC_INTERNAL 宏可以方便地创建一个全局静态变量。这个机制使用了 C++11 的特性,包括 noexcept 说明符和内存模型的一些操作。

让我们逐步解释这个宏的各个部分:

  1. Q_GLOBAL_STATIC_INTERNAL(ARGS): 这是主要的宏定义,用于创建全局静态变量。ARGS 是传递给 Type 类型构造函数的参数。

  2. Q_GLOBAL_STATIC_INTERNAL_DECORATION: 这是一个在宏中使用的修饰符,可能是空的,取决于编译器对 C++11 特性的支持情况。

  3. Type *innerFunction(): 这是一个内部的静态函数,负责实际创建和返回全局静态变量的指针。它使用了一个内部的 Holder 类,以确保在全局静态变量的析构期间正确地管理生命周期。

  4. struct HolderBase: 这是一个基础结构,其中的析构函数负责在全局静态变量被销毁时,检查并更新一个状态标志。这个标志在构造时被设置为 Initialized,而在析构时被设置为 Destroyed。

  5. struct Holder : public HolderBase: 这是一个派生自 HolderBase 的结构。它包含了具体的 Type 类型的实例(value),并在构造函数中将状态标志设置为 Initialized。这确保在全局静态变量的生命周期内,HolderBase 的析构函数将被正确调用,以更新状态标志。

整个机制的目的是确保在多线程环境下,全局静态变量的创建是线程安全的。通过在 HolderBase 的析构函数中检查状态标志,可以防止在全局静态变量析构时重复析构。

这是一个比较复杂的实现,主要是为了保证全局静态变量的正确创建和销毁,并且在多线程环境下能够安全使用。

所以现在应该明白了,这就是资源双重释放了,未来的你,加油。


博客签名2021

相关文章:

Qt_一个由单例引发的崩溃

Qt_一个由单例引发的崩溃 文章目录 Qt_一个由单例引发的崩溃摘要关于 Q_GLOBAL_STATIC代码测试布局管理器源码分析Demo 验证关于布局管理器析构Qt 类声明周期探索更新代码获取父类分析Qt 单例宏源码 关键字&#xff1a; Qt、 Q_GLOBAL_STATIC、 单例、 UI、 崩溃 摘要 今…...

P8A004-系统加固-磁盘访问权限

【预备知识】 访问权限&#xff0c;根据在各种预定义的组中用户的身份标识及其成员身份来限制访问某些信息项或某些控制的机制。访问控制通常由系统管理员用来控制用户访问网络资源&#xff08;如服务器、目录和文件&#xff09;的访问&#xff0c;并且通常通过向用户和组授予…...

数智赋能 锦江汽车携手苏州金龙打造高质量盛会服务

作为一家老牌客运公司&#xff0c;成立于1956年的上海锦江汽车服务有限公司&#xff08;以下简称锦江汽车&#xff09;&#xff0c;拥有1200多辆大巴和5000多辆轿车&#xff0c;是上海乃至长三角地区规模最大的专业旅游客运公司。面对客运市场的持续萎缩&#xff0c;锦江汽车坚…...

kolla-ansible 部署OpenStack云计算平台

目录 一、环境 二、安装及部署 三、测试 一、环境 官方文档&#xff1a;https://docs.openstack.org/kolla-ansible/yoga/user/quickstart.html rhel8.6 网络设置&#xff1a; 修改网卡名称 网络IP&#xff1a; 主机名&#xff1a; 网络时间协议 配置软件仓库 vim docke…...

wireshark 抓包提示

[TCP Previous segment not captured] 在TCP的传输阶段&#xff0c;同一台主机发出的数据段应该是连续的&#xff0c;即后一个包的Seq等于前一个包的SeqLen&#xff08;三次握手和四次挥手是个例外&#xff09;。如果wireshark发现后一个包的Seq号大于前一个包的SeqLen&#xf…...

Redis未授权访问-CNVD-2019-21763复现

Redis未授权访问-CNVD-2019-21763复现 利用项目&#xff1a; https://github.com/vulhub/redis-rogue-getshell 解压后先进入到 RedisModulesSDK目录里面的exp目录下&#xff0c;make编译一下才会产生exp.so文件&#xff0c;后面再利用这个exp.so文件进行远程代码执行 需要p…...

汇编:常用的输入与输出

1.字符输出 使用int 21h中断的02h号功能可以在屏幕输出一个字符&#xff0c;dl中存放要输出字符的ascii码。 如下代码将在屏幕输出一个字符“a”&#xff1a; mov ah,02hmov dl,aint 21h 2.字符输入 使用int 21h中断的01h号功能可以接受一个字符&#xff0c;al存放输…...

MYSQL基础之【正则表达式,事务处理】

文章目录 前言MySQL 正则表达式MySQL 事务事务控制语句事务处理方法PHP中使用事务实例 后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不…...

Mysql并发时常见的死锁及解决方法

使用数据库时&#xff0c;有时会出现死锁。对于实际应用来说&#xff0c;就是出现系统卡顿。 死锁是指两个或两个以上的事务在执行过程中&#xff0c;因争夺资源而造成的一种互相等待的现象。就是所谓的锁资源请求产生了回路现象&#xff0c;即死循环&#xff0c;此时称系统处于…...

二十九、微服务案例完善(数据聚合、自动补全、数据同步)

目录 一、定义 二、分类 1、桶(Bucket)聚合: 2、度量(Metric&#xff09;聚合: 3、管道聚合&#xff08;Pipeline Aggregation&#xff09;&#xff1a; 4、注意&#xff1a; 参与聚合的字段类型必须是: 三、使用DSL实现聚合 聚合所必须的三要素&#xff1a; 聚合可配…...

vue 目录树的展开与关闭

目录 1、翻页方法中控制目录树节点的展开与关闭2、搜索目录树节点名称控制节点的展开与关闭 <el-tree:data"data_option"ref"tree":props"defaultProps"node-click"handleNodeClick":default-expanded-keys"needExpandedKeys&…...

【Docker】python flask 项目如何打包成 Docker images镜像 上传至阿里云ACR私有(共有)镜像仓库 集成Drone CI

一、Python环境编译 1、处理好venv环境 要生成正常的 requirements.txt 文件&#xff0c;我们就需要先将虚拟环境处理好 创建虚拟环境&#xff08;可选&#xff09;&#xff1a; 在项目目录中&#xff0c;你可以选择使用虚拟环境&#xff0c;这样你的项目依赖将被隔离在一个…...

力扣labuladong——一刷day55

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣951. 翻转等价二叉树二、力扣124. 二叉树中的最大路径和三、力扣112. 路径总和&#xff08;遍历&#xff09;四、力扣112. 路径总和&#xff08;分解&a…...

springboot实现验证码功能

转载自 : www.javaman.cn 1、编写工具类生成4位随机数 该工具类主要生成从0-9&#xff0c;a-z&#xff0c;A-Z范围内产生的4位随机数 /*** 产生4位随机字符串*/public static String getCheckCode() {String base "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn…...

内测分发平台是否支持应用的微服务化部署

内测分发平台的微服务化部署支持是现代应用开发和部署的一个重要特性。首先我们得知道什么是微服务化部署都有哪些关键功能&#xff0c;如何实施微服务化的部署。下文以我自己理解总结了几点。 图片来源:news.gulufenfa.com 微服务是一种基于独立运行的小型服务来构建应用程序…...

1140. 最短网络,prim算法,模板题

1140. 最短网络 - AcWing题库 农夫约翰被选为他们镇的镇长&#xff01; 他其中一个竞选承诺就是在镇上建立起互联网&#xff0c;并连接到所有的农场。 约翰已经给他的农场安排了一条高速的网络线路&#xff0c;他想把这条线路共享给其他农场。 约翰的农场的编号是1&#xf…...

升级jdk17过程中,原来的jdk8下的webservice客户端怎样处理

背景&#xff1a;之前jdk8环境下&#xff0c;使用的cxf框架&#xff0c;而且是动态加载解析作为客户端。大家一直相处的很愉快。但是最近升级jdk17&#xff0c;发现cxf不好用了。网上百度&#xff0c;大部分都是说升级cxf版本&#xff0c;并且添加jaxb的相关依赖就可以了。但是…...

Verilog基本语法概述

一、概述 Verilog 是一种用于数字逻辑电路设计的硬件描述语言&#xff0c;可以用来进行数字电路的仿真验证、时序分析、逻辑综合。 既是一种 行为级&#xff08;可用于电路的功能描述&#xff09; 描述语言又是一种 结构性&#xff08;可用于元器件及其之间的连接&#xff09…...

论文阅读:C2VIR-SLAM: Centralized Collaborative Visual-Inertial-Range SLAM

前言 论文全程为C2VIR-SLAM: Centralized Collaborative Visual-Inertial-Range Simultaneous Localization and Mapping&#xff0c;是发表在MDPI drones&#xff08;二区&#xff0c;IF4.8&#xff09;上的一篇论文。这篇文章使用单目相机、惯性测量单元( IMU )和UWB设备作为…...

蓝桥杯刷题day01——字符串中的单词反转

题目描述 你在与一位习惯从右往左阅读的朋友发消息&#xff0c;他发出的文字顺序都与正常相反但单词内容正确&#xff0c;为了和他顺利交流你决定写一个转换程序&#xff0c;把他所发的消息 message 转换为正常语序。 注意&#xff1a;输入字符串 message 中可能会存在前导空…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi&#xff0c;还有多少人在用&#xff1f; 去年&#xff0c;月之暗面创始人杨植麟别提有多风光了。90后清华学霸&#xff0c;国产大模型六小虎之一&#xff0c;手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水&#xff0c;单月光是投流就花费2个亿。 疯…...

前端工具库lodash与lodash-es区别详解

lodash 和 lodash-es 是同一工具库的两个不同版本&#xff0c;核心功能完全一致&#xff0c;主要区别在于模块化格式和优化方式&#xff0c;适合不同的开发环境。以下是详细对比&#xff1a; 1. 模块化格式 lodash 使用 CommonJS 模块格式&#xff08;require/module.exports&a…...

Vue 实例的数据对象详解

Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

初级程序员入门指南

初级程序员入门指南 在数字化浪潮中&#xff0c;编程已然成为极具价值的技能。对于渴望踏入程序员行列的新手而言&#xff0c;明晰入门路径与必备知识是开启征程的关键。本文将为初级程序员提供全面的入门指引。 一、明确学习方向 &#xff08;一&#xff09;编程语言抉择 编…...