C++

C++:使用 CRTP 模式实现静态多态

大家好,今天介绍 CRTP。最近我在捣鼓 Eigen 线代库,发现里面大量使用了这种模式,所以稍微研究一下。CRTP(Curiously Recurring Template Pattern)是 C++ 中的一种设计模式,特点是利用模板和继承,在基类关联派生类模板参数,来实现静态多态性。 为了更好理解,下面通过一个例子来解释 CRTP 的用法。 经典例子:形状的面积计算 假设要计算不同形状的面积,比如圆形和矩形。通过一个基类来实现公共的接口,同时每个形状能够提供自己的计算逻辑。 定义基类 定义一个模板基类 Shape,它接受一个派生类作为模板参数。 1#include <iostream> 2#include <cmath> 3 4template <typename Derived> 5class Shape { 6public: 7 void area() { 8 static_cast<Derived*>(this)->computeArea(); 9 } 10}; 基类中,area 调用了派生类 computeArea 方法。用 static_cast 可以确保在编译时进行类型检查。 定义派生类 定义两个派生类Circle 和 Rectangle。 1class Circle : public Shape<Circle> { 2public: 3 Circle(double radius) : radius(radius) {} 4 5 void computeArea() { 6 double area = M_PI * radius * radius; 7 std::cout << "Circle area: " << area << std::endl; 8 } 9 10private: 11 double radius; 12}; 13 14class Rectangle : public Shape<Rectangle> { 15public: 16 Rectangle(double width, double height) : width(width), height(height) {} 17 18 void computeArea() { 19 double area = width * height; 20 std::cout << "Rectangle area: " << area << std::endl; 21 } 22 23private: 24 double width, height; 25}; 每个派生类实现 computeArea 方法。 Read more...

C++:深度使用 shared_ptr 的一些经验教训

看了网上,大多数都是告诉你什么不要循环引用、树形节点父子用 rc,子父用 weak 这种泛泛而谈。凡是论打破循环引用,总是说加 weak,却不提这样改可能导致的过早失效等问题。 最近在写一个试验性的库,高强度使用了这些指针。所以总结一下经验。 shared_ptr 意味着什么? “共享所有权”一定是第一个跳入你脑海的词汇,因为它的名字就反映了这一点。但我想说的是,有时候我们可以尝试从依赖的视角来分析这个问题。 如果你的结构体或者类含有一个共享指针的话,这意味着: 你的结构体强依赖于它指向的东西 并且他指向的东西也可能被别的东西所强依赖 并且你需要确保它们都依赖的是同一个实例。 这就引申出了一个共享指针的使用原则:依赖关系是如何的? 依赖这个东西看起来简单但是一旦套入到一个实际场景就很容易犯错误。 例如很多人以为是工人离开老板就不能活,但是实际上反而是老板离不开工人。这里构成的依赖关系就是老板依赖于工人。 (图中箭头表示“依赖于”) 很多时候这种东西是下意识的,因此可能会在后面给你带来困扰。 例如当建模一个符号 A 时,A 的值和梯度谁依赖谁?实际上梯度依赖于值,但大家会下意识地将其并列: struct A { TensorPtr value; TensorPtr grad; }; 当然,这种设计也并不能说是错的,万事万物视具体实际情况而定。 原则一:如果A 都没有了那么 B 也无法存在,那么说明 B 依赖于 A 这里实际上就涉及到的生命周期。就是说你所强依赖的东西,他的生命期一定要比你长。是的,虽然我们不写 Rust,C++ 编译器也不会帮你检查,但是你违反这个规则,就会产生隐患。 从而我们可以推导第二个原则。 原则二:如果 A 的生命期要大于 B 的生命期并且 B 强依赖 A,那么建模时,可以考虑 A 作为 B 的一个所有权指针而存在。 例如,一个函数有输入、算符、和输出。一种直觉上的建模方式是: 1struct Fn { 2 Rc<Var> in; 3 Rc<Var> out; 4 5 void apply() { ... } 6} 但是实际上,Fn 依赖于 in,但并不依赖于 out。我们可以考虑让外部负责维护这个 out。 Read more...

Linux 下 Qt 开发环境的搭建

步骤如下: 安装 Qt6 库和开发工具包。 在 Arch Linux 上,可以使用 AUR 包管理器安装 Qt6。打开终端并运行以下命令: yay -S qt6-base qt6-tools qt6-svg qt6-multimedia qt6-webengine 安装的分别是:Qt6 基本库、工具包、SVG 模块、多媒体模块和 WebEngine 模块。 创建 CMake 项目。 创建空目录作为项目目录,创建 CMakeLists.txt: 1 cmake_minimum_required(VERSION 3.16) 2 project(my_qt_app) 3 4 set(CMAKE_AUTOMOC ON) 5 set(CMAKE_AUTORCC ON) 6 set(CMAKE_AUTOUIC ON) 7 set(CMAKE_CXX_STANDARD 17) 8 set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 10 find_package(Qt6 COMPONENTS Widgets REQUIRED) 11 12 add_executable(my_qt_app main.cpp) 13 14 target_link_libraries(my_qt_app Qt6::Widgets) 上面的代码创建一个名为 my_qt_app 的项目,并告诉 CMake 使用 Qt6 Widgets 组件。 Read more...
1 of 1