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

CppCon 2017 学习:Migrating a C++03 library to C++11 case study

这段内容是在介绍 Wt(发音类似 “witty”) —— 一个用于 C++ 的 Web UI 框架。总结如下:

什么是 Wt?

  • Wt 是一个 用 C++ 编写的 widget(控件)驱动的 Web 框架
  • 类似于桌面 GUI 框架(比如 Qt),但用于生成网页应用。
  • 第一次发布是在 2006 年

核心特点:

  • 组件化设计:使用控件(widgets)构建 UI,而不是直接写 HTML/JS。
  • 技术抽象:你写 C++ 代码,它会根据客户端支持自动选择使用:
    • HTML
    • JavaScript
    • Ajax
    • WebSockets
  • 自动降级支持:技术不支持时会自动退化为可用的技术组合。
  • 灵感来自 Qt:语法风格和理念都受 Qt 框架影响。

用途:

  • 嵌入式系统的前端
  • 需要 Web 界面的 C++ 后端应用
    例如,工业设备、IoT 界面、企业级数据平台等。
    如果你熟悉桌面开发(比如 Qt 或 MFC),那你会发现用 Wt 开发 Web 应用非常“自然”,不需要太多 JavaScript 或 HTML 的知识。

贴出的代码是一个 Wt 应用程序的基本模板,类似于 “Hello World” 框架级的入门示例。以下是详细解释:

Wt 应用结构(版本 3 示例):

#include <Wt/WApplication>
#include <Wt/WContainerWidget>
#include <Wt/WServer>

这些是 Wt 的头文件,分别提供:

  • WApplication:Wt 的核心类,表示一个 Web 应用。
  • WContainerWidget:用于在网页中放置内容(类似 HTML 的 <div>)。
  • WServer:Wt 内部使用的 Web 服务器接口。

应用入口函数:

Wt::WApplication *createApplication(const Wt::WEnvironment &env)
  • 每当有新用户访问这个 Web 应用时,Wt 会调用这个函数创建一个新的 WApplication 实例。
  • WEnvironment 提供关于请求和客户端环境的信息。
  • 你可以在这里自定义界面内容(例如添加按钮、文本等)。
auto app = new Wt::WApplication(env);
Wt::WContainerWidget *root = app->root();
// 在 root 上添加控件,比如按钮、文本标签等
return app;

程序主函数:

int main(int argc, char *argv[])
{return Wt::WRun(argc, argv, &createApplication);
}
  • Wt::WRun 是 Wt 提供的 主循环
  • 它会调用 createApplication 来为每个访问者创建会话。
  • 启动内置 HTTP 或 FastCGI 服务器(取决于配置)。

类比:

框架应用类控件容器主函数
QtQApplicationQWidget / QMainWindowint main()
WtWApplicationWContainerWidgetWRun()

示例补充(展示文本):

root->addWidget(std::make_unique<Wt::WText>("Hello from Wt!"));

这样你就可以在网页上显示文字了。

贴的这段代码是使用 Wt 框架构建 Web 界面的小例子,展示了如何用 C++ 创建按钮、文本并处理点击事件,背后自动映射为 HTML。

C++ 代码详解

Wt::WContainerWidget *root = app->root();
  • 获取网页的根容器,类似于 HTML <body>
Wt::WPushButton *button = new Wt::WPushButton("Click me!");
root->addWidget(button);
  • 创建一个按钮,按钮文字为 Click me!
  • 加到网页的根容器中 → 显示为 HTML <button>
Wt::WText *text = new Wt::WText("Button not clicked yet!");
root->addWidget(text);
  • 创建一个文字控件(文本标签),初始显示 “Button not clicked yet!”。
  • 加入网页中 → 显示为 HTML <span><div>
button->clicked().connect(std::bind([text]{text->setText("Button clicked!");
}));
  • 将按钮的点击事件 clicked() 连接到一个 lambda 函数。
  • lambda 里将 text 文本改为 “Button clicked!”。
  • 使用 std::bind 捕获 text 指针,确保事件发生时仍能访问它。

转换成的 HTML 大致结构:

<div><button>Click me!</button><span>Button not clicked yet!</span>
</div>

点击按钮后,<span> 的内容变为:

<span>Button clicked!</span>

总结要点:

C++ 控件HTML 渲染标签动作
WPushButton<button>点击触发事件处理
WText<span><div>展示文本内容
addWidget(...)插入子控件构建 HTML DOM 树
clicked().connect(...)JS事件绑定Wt 自动将 C++事件转为 JS 绑定
如果你继续添加其他控件(表单、图片、表格等),Wt 会自动处理 HTML + JS + 状态同步,让你只用 C++ 编程,像写桌面 GUI 一样做 Web 开发。

Wt 4 框架设计目标和一个关键问题 —— 内存模型不明显,我们逐条理解一下:

Wt 4 的目标

  1. 让使用 Wt 更“好玩”(更愉快)
    • 更清晰、更安全的内存模型
    • 更快的编译速度
    • 更符合现代 C++ 编程习惯(C++11/14/17)
  2. 修复旧 API 的问题
    • 改进不直观或容易出错的 API
  3. 兼容性策略
    • 虽然会有破坏性变更(breaking changes)
    • 尽量让它们在编译时爆出错误(静态报错),而不是运行时崩溃

问题 #1:Wt 的内存模型不明显

引用:StackOverflow 讨论

“看了很多 Wt 的例子,里面用了很多 new,但这些对象是怎么被 delete 掉的呢?”

原因:

很多例子会这样写:

auto button = new Wt::WPushButton("Click me!", root());
  • 看起来像裸指针,没有 delete?
  • 但是 Wt 背后是有“所有权转移”机制的:

Wt 的内存管理方式

Wt 组件都继承自 Wt::WObject,而且 WContainerWidget::addWidget() 会接管所有权

Wt::WPushButton* button = new Wt::WPushButton("Click me!");
root->addWidget(button);  // root 会负责 delete button

也就是说你写 new 是为了动态创建,但不需要手动 delete,它会随着父控件生命周期一起销毁。
类似 Qt 的 QObject 层级结构。

Wt 4 的改进方向

为了让这个行为更加明确和安全,Wt 4 有以下改进思路:

改进目标描述
明确的所有权模型可能使用 std::unique_ptr、智能指针或工厂函数代替 new
减少内存泄露风险避免开发者忘记 delete,或者误用裸指针
更符合现代 C++ 习惯比如支持初始化列表、类型推导、更少冗余指针操作

总结:

项目内容
Wt 4 目标更好用、更现代、更安全
内存问题addWidget() 自动管理生命周期,但例子可能让人误解
改进方向智能指针支持、更清晰的资源所有权、更少出错空间

Wt 的内存管理方式 的进一步探讨。我们来系统理解一下 “所有权是否转移(ownership transfer)” 在 Wt 中是怎么回事,以及 unique_ptr vs shared_ptr 在这种框架下的利与弊。

问题背景

在 Wt 中,组件(widgets)通常通过指针动态分配,然后加入某个容器控件中,例如:

Wt::WPushButton *button = new Wt::WPushButton("Click me!");
container->addWidget(button);  // Wt 框架会负责释放 button

但是:

  • 你用了 new,却没有 delete
  • 又没用智能指针
  • 这样代码虽然能跑,却会误导开发者

方案分析

方案 1:std::make_unique(不转移所有权)

auto button = std::make_unique<Wt::WPushButton>("Click me!");
container->addWidget(button.get());
优点:
  • 使用 unique_ptr在异常时不会泄露内存
  • 编译器能检查生命周期
  • 所有权清晰明确(由 button 持有)
缺点:
  • 所有权不转移,Wt 不知道它何时会被销毁
  • ✗ 一旦 button 离开作用域或被手动释放,UI 就悬空或崩溃
  • ✗ 你要自己确保生命周期合理,否则容易 dangling pointer(悬挂指针)

方案 2:std::make_shared(共享所有权)

auto button = std::make_shared<Wt::WPushButton>("Click me!");
container->addWidget(button);  // 可接受 shared_ptr
button->clicked().connect([button] {button->setText("I was clicked!");
});
优点:
  • 遇到异常不会泄漏(RAII)
  • 更适合配合 Lambda 捕获使用
  • Wt 4 已支持 shared_ptraddWidget() 重载
缺点:
  • ✗ 共享所有权 → 更难追踪谁应该负责释放
  • 容易造成内存泄漏(特别是 lambda 捕获中形成循环引用)
  • 不需要线程安全,但 shared_ptr 仍带来性能负担

总结比较:

特性原始 newunique_ptrshared_ptr
安全性泄漏风险安全(但不能转移)安全
所有权清晰不清晰明确属于你共享,模糊
生命周期控制框架内部控制明确可能泄漏(循环引用)
易用性但误导性大✗ 麻烦(需小心)Lambda 很方便

推荐实践(Wt 4 中):

  • 首选原始指针 + Wt 所有权管理(addWidget 接管)
  • 手动 new 是安全的,只要立即传给 addWidget()
  • 比如:
    container->addWidget(new Wt::WPushButton("Click me!"));
    
  • 如需保留引用,可用 std::reference_wrapper 或框架控件中提供的方式
  • 如果用 shared_ptr小心捕获 lambda 中形成循环引用
  • 避免 unique_ptr + addWidget(),因为生命周期不一致会导致 bug
    如果你在实际开发中需要更安全的风格,也可以自定义工厂函数封装:
template<typename Widget, typename... Args>
Widget* makeAndAdd(Wt::WContainerWidget* parent, Args&&... args) {auto* w = new Widget(std::forward<Args>(args)...);parent->addWidget(w);return w;
}

这样既安全又易读。

你说的内容是对 Wt 4 中推荐的内存管理方式——使用 std::unique_ptr 并通过 std::move 转移所有权的介绍。下面我们来完整理解这段代码的设计理念、优缺点,以及为什么它在 Wt 4 中被推荐。

代码解析

Wt::WContainerWidget *container = ...;
auto button = std::make_unique<Wt::WPushButton>("Click me!");
container->addWidget(std::move(button));  // 所有权转移给 container
// button == nullptr
auto text = std::make_unique<Wt::WText>("Button not clicked yet!");
container->addWidget(std::move(text));    // 所有权转移
// text == nullptr
button->clicked().connect([t=text.get()] {t->setText("Button clicked!");
});

理解重点

使用 std::unique_ptr 的优势

  • std::unique_ptr 表示独占所有权:不会自动共享、不会无意间多次释放。
  • 当通过 std::move 将 widget 加入到 container->addWidget(...) 后,container 接管了内存管理
  • 在异常发生时(如构造控件失败),unique_ptr 会自动清理资源 → 不泄漏

潜在问题 / 缺点

✗ 可读性稍差

button == nullptr

因为你已经 move 掉了 button,它变成空指针,之后不能再访问或使用它(比如绑定 signal)。
解决方式:

  • 提前使用 get() 获取原始指针:
    auto text = std::make_unique<Wt::WText>("...");
    auto text_raw = text.get();  // 捕获指针
    container->addWidget(std::move(text));
    button->clicked().connect([t = text_raw] {t->setText("Button clicked!");
    });
    

总结:使用 std::unique_ptr + std::move 是 Wt 4 推荐方式

特性说明
安全使用 RAII 保证不泄漏
清晰所有权明确,避免共享带来的模糊
Wt 4 兼容addWidget() 支持接受 unique_ptr
缺点指针被 move 之后不能再直接使用,写法稍显冗长

最佳实践建议

  • 推荐总是使用 std::make_unique 创建 widget
  • 通过 std::move 将所有权交给容器(如 WContainerWidget
  • 如果你需要继续使用 widget,比如绑定事件,先缓存 get() 指针
  • 保证生命周期安全(避免 dangling 指针)
    如果你需要,我可以帮你写一个安全封装工具函数,统一管理这些控件的创建与绑定事件,减少冗余代码。是否需要?

你提供的这段是关于 Wt 4 中使用 std::unique_ptr 管理控件所有权(ownership) 的说明。我们来系统地理解它的意义和背后理念。

理解 Wt 4 与 unique_ptr 的使用方式

auto button = std::make_unique<Wt::WPushButton>("Click me!");
container->addWidget(std::move(button));
// button == nullptr

Wt 4 的控件树(Widget Tree)负责控件的生命周期管理。当你将控件传入 addWidget() 时,控件的所有权被 Wt 框架接管,你再也不需要也不应该手动删除它。

为什么使用 unique_ptr

优点(Pros)

  • ** 安全**:不再需要手动 delete,避免内存泄漏。
  • ** 清晰**:所有权转移非常明确,一眼就能看出 widget 生命周期的管理者。
  • ** 与现代 C++ 风格对齐**:使用 unique_ptr 是现代 C++ 推荐的资源管理方式。
  • ** 异常安全**:如果 new 失败或 addWidget 抛异常,unique_ptr 会自动清理。

潜在缺点(Cons)

  • ✗ 稍冗长(verbose)
    • 你必须显式使用 std::make_uniquestd::move
    • 比传统 new 写法更长一点
  • ✗ 控件指针为空(null after move)
    • buttontext 在调用 std::move 后变为 nullptr
    • 你不能再用 button->clicked() 这样调用它们
    • 所以如果还要引用,就得先 auto raw_ptr = button.get();,然后再 move

正确的写法(避免空指针问题)

auto text = std::make_unique<Wt::WText>("Button not clicked yet!");
auto text_ptr = text.get();  // 保存指针用于 lambda
container->addWidget(std::move(text));
button->clicked().connect([t = text_ptr] {t->setText("Button clicked!");
});

总结

特性内容
框架Wt 4 推荐使用 unique_ptr 进行 widget 管理
目的明确控件所有权、避免内存泄漏
建议std::move 之前如果需要后续使用,应先保存原始指针
取代不推荐 newshared_ptr(shared 容易泄漏,管理复杂)

这段内容讲解了 Wt 4 框架中控件生命周期管理的改进方式,特别是:

addWidget() 返回原始指针的机制(用法更方便)

背景

在之前的例子中,我们使用 std::unique_ptr 管理控件对象,然后调用:

container->addWidget(std::move(button));

此后 button 指针就变成 nullptr,如果你还想连接 signal,比如:

button->clicked().connect([...]);

就会变得复杂或不安全。

改进方案:addWidget() 返回控件原始指针

实现方式如下:

template<typename Widget>
Widget* addWidget(std::unique_ptr<Widget> widget) {Widget* result = widget.get();  // 获取原始指针addWidget(std::unique_ptr<Wt::WWidget>(std::move(widget)));  // 调用框架的添加逻辑return result;  // 返回原始指针给用户
}

示例用法变得更简洁清晰:

Wt::WContainerWidget *container = ...;
Wt::WPushButton *button = container->addWidget(std::make_unique<Wt::WPushButton>("Click me!"));
Wt::WText *text = container->addWidget(std::make_unique<Wt::WText>("Button not clicked yet!"));
button->clicked().connect([text] {text->setText("Button clicked!");
});

优点

优点说明
简洁不需要保存 .get(),也不需要先取 raw 指针再 std::move()
安全内存由 unique_ptr 管理,避免泄漏
易用用户可以立即获得原始指针,继续绑定事件或操作控件

use-after-move 风险识别

如何防止“移动后使用”的错误?

使用 clang-tidy 静态分析工具:

clang-tidy -checks=misc-use-after-move use-after-move.cpp -- -std=c++14

或:

run-clang-tidy.py -checks=-*,misc-use-after-move

这可以帮助你检测是否有代码在 std::move() 后还错误使用那个对象(即 Use-After-Move)。

总结

项目内容
改进addWidget 返回原始指针
优点减少手动管理 get() 的繁琐和空指针风险
安全保障unique_ptr 确保生命周期;Clang-Tidy 可检测使用风险
推荐用法auto *ptr = container->addWidget(std::make_unique<...>());

你这一系列内容主要是在介绍 Wt 4 框架的现代化设计变更,特别是围绕内存管理、信号槽系统以及去除 Boost 的优化。

下面是对你贴出的内容的逐点解释和总结,帮助你全面理解:

observing_ptr:弱引用机制用于 signal-slot 生命周期安全

问题:

某个 signal 的发射者(例如按钮)活得比 slot 中用到的对象更久,就可能出现访问已经被销毁对象的风险 —— 导致 悬垂指针(dangling pointer)

解决方案:

引入 Wt::Core::observing_ptrobservable

  • observing_ptr 是一种安全的弱引用。
  • 它不拥有对象,只观察对象的生命周期。
  • 被观察对象析构时,observing_ptr 会自动变为 null,避免悬垂引用。
  • 默认不使用,只有在你明确需要时才用。
    优势: 安全连接 signal-slot,不怕 slot 捕获的对象被销毁。

替换 Boost:减小体积,提升可移植性

Wt 4 去 Boost 改用标准库或轻量实现:

Wt 3Wt 4 替代
Boost.Signals2自己实现(性能更高)
Boost.Anystd::any / 自定义实现
Boost.Threadstd::thread
Boost.Regexstd::regex
Boost.Randomstd::random
Boost.DateTimestd::chrono + date.h
Boost.Asio同名独立版本支持
为什么要替换 Boost?
  • Boost 编译慢(头文件巨大)
  • 在 Windows 上使用 Boost API,需要一起发布 Boost 动态库,麻烦
  • 编译文件更大,打包体积更高

性能与体积对比(Wt 3 vs Wt 4)

项目Wt 3Wt 4
编译时间更长更快
预处理文件大小
安装包体积
安装耗时更长更快
图表数据显示 Wt 4 明显 更快、更小、更清爽

结论总结

内容
所有权模型unique_ptr 明确所有权更安全,尽管稍显繁琐
现代 C++ 支持充分利用 C++17/20 标准库,减少第三方依赖
编译优化去掉 Boost 后,减少了体积,提高了构建效率
安全性增强引入 observing_ptr 提高信号槽机制的鲁棒性
Wt 4是一个结构上更现代、更轻量、更安全的版本,虽然是新发布,还需观察用户长期反馈
如果你是一个用 C++ 写 Web UI 的工程师,或者在嵌入式设备上跑前端,Wt 4 是一个更清晰、现代、可维护的选择
如你想要:
  • 示例代码(比如使用 observing_ptr 防止悬垂槽函数)、
  • 或者迁移 Wt 3 项目的指南,
http://www.lqws.cn/news/450649.html

相关文章:

  • nginx的下载与安装 mac
  • 【前端隐蔽 Bug 深度剖析:SVG 组件复用中的 ID 冲突陷阱】
  • Kafka协议开发总踩坑?3步拆解二进制协议核心
  • 中文Windows系统下程序输出重定向乱码问题解决方案
  • pip install下载安装后的文件所在位置
  • Lombok常用注解总结
  • 4.文件管理(文本、日志、Excel表)
  • 亚马逊云科技中国峰会
  • httpclient实现http连接池
  • 【Python小练习】3D散点图
  • 服务网格安全(Istio)从入门到实践
  • Kotlin 中ArrayList、listOf、arrayListOf 和 mutableListOf区别
  • 电力企业数字化——解读44页电力集团战略实施和集团对标一体化指标体系框架【附全文阅读】
  • Zephyr 系统深入解析:SoC 支持包结构与中断调度器调优实践
  • [设计模式]创建型模式-单例模式
  • Deepseek+墨刀,1min快速生成流程图!
  • iOS APP上架App Store实践:通过自动化流程和辅助工具高效提
  • 传输层协议UDP/TCP
  • Linux运维笔记:在 Ubuntu 工作站上安装 PyCharm 社区版并配置多用户访问
  • 悦数图数据库v5.1原生向量赋能
  • 【时时三省】(C语言基础)善于利用指针
  • 传感器:基于STM32F103/407系AHT20温湿度传感器数据采集
  • C#学习日记
  • STM32学习笔记:深入浅出解析CAN总线
  • 【生活点滴】车辆过户、新车挂牌
  • 基于物联网的智能衣柜系统设计
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(五)
  • 探秘阿里云云数据库Tair:性能、特性与应用全景解析
  • 基于大模型的三叉神经痛预测及治疗方案研究报告
  • [持续集成]