设为首页 加入收藏

TOP

【Qt6】QWidgetAction 的使用(一)
2023-08-26 21:10:53 】 浏览:171
Tags:Qt6 QWidgetAction

在开始主题前,先看一个 C++ 例子:

#include <iostream>

struct Data
{
    int a;
    int b;
};

// 注意这里
struct Data *s;

void doSome()
{
    Data k;
    k.a = 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

int main()
{
    // 先调用了函数
    doSome();
    // 再输出 Data 结构体的内容
    std::cout << "a = " << s->a << '\n';
    std::cout << "b = " << s->b << '\n';
    return 0;
}

不要问这个例子的功能,问就是超能力。其实这个例子没啥功能,纯粹是为了运行后出错而写的。有同学会疑惑:这程序好像没啥问题。嗯,看着是没啥问题,我们预期的情况是:a 的值是 100,b 的值是 300。

遗憾的是,运行结果是这样的:

a = -858993460
b = -858993460

啥玩意儿?下面咱们就扒一下到底哪里出事了。

这个例子先定义了一个结构体叫 Data,里面有两个字段 a、b。然后声明 Data 类型的指针变量,在 doSome 函数中让变量 s 引用了一个 Data 实例的实例。在 main 函数中,先调用 doSome 函数,然后再输出 a、b 的值。这里就出现一个问题了:s 引用的 k 是在 doSome 函数内创建的,而且它的数据分配在栈上,当 doSome 函数执行结束时,k 的生命周期也差不多了。当调用 doSome 函数之后访问 s,此时 s 所指向的对象已经没有了,所以 a、b 输出的是一个“脏”的值。

若是把 k 改为 static,那结果就不一样了。

void doSome()
{
    static Data k;
    k.a = 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

控制台将输出:

a = 100
b = 300

如果你不相信上述现象,也可以把例子改成这样:

#include <iostream>

class Test
{
public:
    Test()
    {
        std::cout << "Test 构造函数 ..." << std::endl;
    }

    ~Test()
    {
        std::cout << "Test 析构函数 ..." << std::endl;
    }
    int a,b;
};

// 注意这里
Test *s;

void doSome()
{
    Test k;
    k.a= 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

int main()
{
    // 先调用了函数
    std::cout << "调用doSome函数前\n";
        doSome();
    std::cout << "调用doSome函数后\n";
    // 再输出a、b的内容
    std::cout << "a = " << s->a << '\n';
    std::cout << "b = " << s->b << '\n';
    return 0;
}

运行上述代码,得到的输出为:

Test 构造函数 ...
Test 析构函数 ...
调用doSome函数后
a = -858993460
b = -858993460

这样就能清楚地知道,s 引用的对象在退出 doSome 函数之前就已经析构了。除了使用 static 关键字外,也可以让 Test 对象分配在堆上。

void doSome()
{
    Test *k = new Test;
    k->a = 100;
    k->b = 300;
    // 复制的是地址,不是对象
    s = k;
}

把 k 赋值给 s,只是把指向的地址复制一遍罢了,对象实例并没有复制。栈上的数据会因变量的生命周期而被回收,但堆上的东西需要 delete。所以,在调用完 doSome 函数后,堆上的东西还在,所以输出的 a、b 值不会“脏”。按理说,s 用完了应该 delete 的,不过,我没写 delete 语句,毕竟这里 main 函数马上就执行完了,程序都结束了,堆上的东西早没了,所以,这里就偷偷懒吧,不必管它。

下面再来看一个 Qt 程序:

#include <QWidget>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>


int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    // 创建两个按钮
    QPushButton btnA("Yes");
    QPushButton btnB("No");
    // 创建顶层窗口
    QWidget window;
    
    // 构建对象树
    btnA.setParent(&window);
    btnB.setParent(&window);
    // 设置按钮在窗口中的位置
    btnA.move(28, 30);
    btnB.move(28, 75);

    // 显示窗口
    window.show();

    return QApplication::exec();
}

上述程序也是一个有问题的程序,但它能运行,只是在关闭窗口时报错。

Unhandled exception at 0x00007FFDD029C1F9 (ntdll.dll) in myapp.exe: 0xC0000374: 堆已损坏。 (parameters: 0x00007FFDD03118A0).

这个问题和第一个例子的有点像但又不完全一样。这个 Qt 程序是一个经典错误,问题出在两个 QPushButton 对象被析构了两次。由于所有变量都是在栈上分配的,上述程序的压入顺序是 btnA - btnB - window。按照后进先出的规则,window 变量是最新定义的,它首先发生析构。由于 btnA、btnB 调用了 setParent 方法设置了对象树关系,当 window 析构时会删除 btnA、btnB。又因变量生命周期的原因,在 window 析构之后,btnA 和 btnB 又发生析构(可刚才 window 让它们析构过了)。

解决方法:1、调整声明变量的顺序,先声明 window 变量,再声明其他变量;2、用指针。

下面代码改为用指针类型。

#include <QWidget>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>


int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    // 创建两个按钮
    QPushButton *btnA = new QPushButton("Yes");
    QPushButton *btnB = new QPushButt
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇linux c++ tcp 下一篇C++项目实战之演讲比赛流程管理系..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目