CppCon 2017 学习:10 Core Guidelines You Need to Start Using Now

C.45: 不要定义一个仅仅初始化成员变量的默认构造函数,而是使用类内成员初始化器

如果你有一个默认构造函数,它的唯一作用是给成员变量赋默认值(如 1、2、3),这更清晰、简单的方法是直接在成员变量声明时使用类内初始化器(in-class initializers)

C.48: 优先使用类内初始化器,而不是在构造函数中初始化常量初值

解释:

当初始值是固定的(比如总是初始化为 1、2、3),直接放在成员定义里更清楚、更少重复。

✳ 重构你的代码

class Simple {
public:Simple() = default;Simple(int aa, int bb, int cc = -1) : a(aa), b(bb), c(cc) {}Simple(int aa) {a = aa;b = 0;c = 0;}
private:int a{1};int b{2};int c{3};
};

优点:

  • a{1}b{2}c{3} 让默认初值更直观。
  • 默认构造函数 Simple() = default; 是自动生成的,不用你写。
  • 更少代码,更清晰意图。

原代码问题:

Simple() : a(1), b(2), c(3) {}

这只是在做初始化,跟写在类内没区别,应该删掉改为类内初始化器

总结一句话:

如果成员有固定初始值,不要在构造函数里写,直接在类内初始化。构造函数就用来处理变化的参数。这样代码更清晰、更易维护。

你提供的这两段代码展示了**使用类内成员初始化器(in-class initializers)**的好处,结合后面列出的 Benefits(好处),我们来逐条解释。

示例代码含义解析

class Simple {
public:Simple()  {}  // 用户定义的默认构造函数Simple(int aa, int bb, int cc) : a(aa), b(bb), c(cc) {}Simple(int aa) : a(aa) {}
private:int a = -1;int b = -1;int c = -1;
};
class Simple {
public:Simple() = default;  // 编译器自动生成默认构造函数Simple(int aa, int bb, int cc) : a(aa), b(bb), c(cc) {}Simple(int aa) : a(aa) {}
private:int a = -1;int b = -1;int c = -1;
};

在这两个版本中:

  • 类内初始值 int a = -1; 是默认值,会被任何没特别赋值的构造函数所使用。
  • 第二个版本通过 = default,使默认构造函数由编译器自动生成,而不是手写空函数体。

优点(Benefits)详解

No arguing about “equivalent” ways to do it

不再争论各种“等价”的初始化写法。

统一使用类内初始化器可以避免这样的问题:

Simple() : a(-1), b(-1), c(-1) {}   // 和
int a = -1; int b = -1; int c = -1; // 哪个更好?

→ 统一方式更清晰,减少团队代码风格争议。

May prevent some bugs

可以防止某些初始化遗漏的 bug。

比如:

Simple(int aa) : a(aa) {}  // 如果 b 和 c 没在构造函数中初始化

→ 它们就会用类内初始值 -1,不会变成未定义值或垃圾值。

May put you back in “compiler generates constructors” land

有可能让你回到“让编译器自动生成构造函数”的美好世界。

你就可以用 = default 自动生成默认构造函数,不必手写:

Simple() = default;

→ 更少代码,更少出错。

Potentially marginally faster in some circumstances

在某些情况下,这种做法可能更快(边际提升)。

尤其在使用 std::vector<Simple> 等容器时,类内初始化器有时能让构造路径更优化,因为编译器可能内联或避免重复初始化。

总结一句话:

类内初始化器 + 默认构造函数 = 更简单、更安全、更一致的 C++ 代码。

这符合现代 C++(C++11 起)的最佳实践,尤其在构造函数中不重复写相同的默认值。

你提供的是 C++ 核心准则中的一条重要建议:

F.51:如果可以选择,优先使用默认参数而不是函数重载

示例比较

使用重载实现默认行为(冗余):

class Reactor {
public:double Offset(double a, double b, double ff);double Offset(double a, double b);  // 重载一份只是为了给 ff 默认值
};
double Reactor::Offset(double a, double b, double ff) {// 复杂计算return whatever;
}
double Reactor::Offset(double a, double b) {return Offset(a, b, 1.0);  // 手动添加默认值
}

使用默认参数(简洁):

class Reactor {
public:double Offset(double a, double b, double ff = 1.0);
};
double Reactor::Offset(double a, double b, double ff /* = 1.0*/) {// 复杂计算return whatever;
}

为什么默认参数更好?(Benefits)

No arguing about “equivalent” ways to do it

避免对两种“看似等效”的实现方式的争论。

不用再争论是要重载两个版本,还是默认参数好——直接默认参数就行。

Will not forget to make same change to both copies

修改参数逻辑时,不会忘记同步另一个版本。

例如改成使用 ff = 0.95,只要改一处:

double Offset(double a, double b, double ff = 0.95);

→ 避免因为忘改重载函数导致不一致或 bug。

Difference between the two “versions” is crystal clear

不再出现“两个版本”之间的模糊区别,调用者一目了然。

Offset(10, 20);         // 用默认 ff = 1.0
Offset(10, 20, 0.75);   // 显式给出 ff

相比重载版本:

Offset(10, 20);         // 哪个版本?(看签名)
Offset(10, 20, 0.75);   // 不明显差异

总结一句话:

用默认参数,写得更少,错得更少,读得更清楚

这体现了现代 C++ 的风格倾向:减少重复代码,提升可维护性与表达力

C.47:定义和初始化成员变量时,应按照它们在类中声明的顺序

为什么这很重要?

在 C++ 中,即使你在构造函数的初始化列表中按照你喜欢的顺序写初始化语句,编译器实际上仍然会按成员在类中声明的顺序来初始化

示例:存在隐患的代码

class Wrinkle {
public:Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:int a;int x;int b;
};

初始化顺序实际上是:a → x → b
但你写的是:a → b → x
这样会导致:

  • 成员变量 b 被初始化时,依赖了 i 的值(可能和你想的不一样)
  • 代码看起来正确,但行为会出错或让人困惑
  • 编译器可能发出警告:“warning: field ‘x’ will be initialized after field ‘b’”

更清晰的正确写法

class Wrinkle {
public:Wrinkle(int i) : a(++i), x(++i), b(++i) {}
private:int a;int x;int b;
};

更真实的例子

假设:

class Person {
public:Person(string first, string last) : firstName(first), lastName(last), fullName(first + " " + last) {}
private:string firstName;string lastName;string fullName;
};

fullName 依赖于 firstNamelastName但必须确保它在它们后面声明,否则会使用未初始化的值!

谁可能打乱顺序?

  • **“热心的新人”**试图按字母顺序排列变量
  • 工具可能自动整理字段
  • 有人想按“逻辑分组”整理变量,却不看构造函数顺序

建议(总结)

  • 始终按类中声明的顺序编写构造函数初始化列表
  • 不要依赖初始化顺序之外的副作用(比如 ++i
  • 如果成员间存在初始化依赖,应在声明顺序上表达清晰的意图

好处

  • 避免初始化顺序 bug
  • 不需要每个开发者都记住 C++ 的这个“怪癖”
  • 鼓励你重新思考类的设计,减少成员之间的耦合依赖

一句话总结:

在初始化列表中改变顺序没用 —— 编译器会按声明顺序来初始化。为了安全和可读性,让你的初始化列表和成员声明保持一致的顺序。

你提到的内容是 C++ 核心准则中的一条设计建议:

I.23: Keep the number of function arguments low

I.23:尽量减少函数参数数量

举例说明:

糟糕的设计(太多参数):
int area(int x1, int y1, int x2, int y2);
int a = area(1, 1, 11, 21);
  • 参数太多,难记忆,容易出错。
  • 没有抽象,含义不清晰(哪个是左上角?哪个是右下角?)。
更好的设计(引入抽象):
int area(Point p1, Point p2);
int a = area({1, 1}, {11, 21});
  • 使用 Point 类型,更清晰地表达意图。
  • 减少调用者负担,不需要记位置。
  • 抽象可复用。

进一步示例:构造 Customer

糟糕设计:
Customer(string pfirst, string plast, string pph,string sfirst, string slast, string sph, string sid);
  • 多达 7 个字符串参数,难维护。
  • 非常容易传错。
改进设计:
class Customer {Person details;Salesrep rep;
public:Customer(Person p, Salesrep s);
};
  • 将数据封装进合适的结构(如 Person, Salesrep)。
  • 调用清晰,代码更易维护。

核心好处

优点说明
更低的认知负担用户不用记住那么多参数顺序
更清晰的意图表达结构化参数让含义更明确
抽象可以复用Point, Person 可以在别处使用
降低未来代码变更影响只需改结构体,函数签名不动

小结一句话:

函数参数越少越好,如果超过 3-4 个,应该考虑把它们组合进结构体或类里。

ES.50: Don’t cast away const

不要去除 const 限定符(不要“cast away const”)

背景问题

我们有一个 Stuff 类,其中包含一个缓存机制 cachedValue,希望在 getValue() 中使用它。
getValue()const 函数,不能修改任何成员变量。

错误做法:
int Stuff::getValue() const {if (!cacheValid) {cachedValue = LongComplicatedCalculation();  //  编译错误,修改了成员cacheValid = true;}return cachedValue;
}

如果你想让它编译,有人可能会写:

int Stuff::getValue() const {auto self = const_cast<Stuff*>(this);  //  cast away const!if (!self->cacheValid) {self->cachedValue = LongComplicatedCalculation();self->cacheValid = true;}return self->cachedValue;
}

这是不推荐的做法!

为什么不能去掉 const

  • 违反接口契约getValue() 承诺不修改对象状态,却偷偷修改了。
  • 让头文件成为谎言:你声称不变,其实在改。
  • 代码难以维护:别人调用你的 const 函数,会以为它是线程安全的、无副作用的,但其实不是。
  • 容易出 bug,尤其是涉及优化、多线程、拷贝等。

正确做法:使用 mutable

class Stuff {
private:int number1;double number2;int LongComplicatedCalculation() const;mutable int cachedValue;mutable bool cacheValid;
public:Stuff(int n1, double n2): number1(n1), number2(n2), cachedValue(0), cacheValid(false) {}bool Service1(int x);bool Service2(int y);int getValue() const;
};
int Stuff::getValue() const {if (!cacheValid) {cachedValue = LongComplicatedCalculation();cacheValid = true;}return cachedValue;
}

为什么 mutable 是好的解决方案?

优点说明
保持 const 函数语义只有缓存变量能被修改,接口保持诚实
可读性好一眼能看出哪些成员可能在 const 函数中被改
编译器优化友好保留 const 语义,有助于优化和静态分析
更安全避免了错误地修改非缓存成员的风险

小结:

当你需要在 const 函数中修改内部缓存状态时,请使用 mutable,而不是 const_cast。

永远不要 cast away const 除非你非常清楚代价,并且这是最后手段。
mutable 是安全地修改缓存的标准做法。
如果你还想探讨 mutable 的使用边界或缓存设计模式,可以继续问我。

你提到的是 C++ 核心准则中非常重要的一条资源管理原则:

I.11: Never transfer ownership by a raw pointer (T*)

永远不要用裸指针(T*)传递资源所有权

违反规则的错误示例:

Policy* SetupAndPrice(args) {Policy* p = new Policy{...};  // 手动分配内存// ...return p;                     // 通过裸指针传递所有权
}
  • 🔺 谁来 delete? 不清楚。
  • 🔺 极易造成 内存泄漏
  • 🔺 调用方不知道是否需要释放。
  • 🔺 所有权不明确,违反了现代 C++ 的资源管理理念。

更安全的替代方案:

1. 返回值传递(by value)

Policy SetupAndPrice(args);  // 编译器可优化掉复制(RVO/NRVO)
  • 简洁。
  • 现代编译器通常会 自动省略拷贝
  • 如果不担心复制代价,这是首选方式。

2. 使用非 const 引用传入已有对象

void SetupAndPrice(Policy& policy);  // 调用方自己拥有 policy
  • 函数不会创建资源,只修改它。
  • 最适用于:调用前就已有对象。

3. 返回智能指针(推荐!)

std::unique_ptr<Policy> SetupAndPrice(args);
  • 明确表示“我拥有这个对象”。
  • 调用方拿到智能指针后,对象会自动销毁。
  • 避免忘记 delete。

4. 使用 gsl::owner<T*>

gsl::owner<Policy*> SetupAndPrice(args);
  • 并不自动管理内存。
  • 作用是标记:“这个裸指针的所有权转移了”
  • 更易被工具分析/被人理解。
template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
using owner = T;

小结:

做法安全性所有权是否清晰
裸指针返回 T*高风险不明确
返回值 T安全明确
智能指针 unique_ptr<T>安全明确
引用参数 T&安全明确
gsl::owner<T*>辅助作用明确但不自动

核心观点:

内存管理太重要,不能只靠记忆。

不要手动管理内存,应该:

  • 用值语义(复制或移动)
  • 用智能指针管理所有权
  • 或至少用 owner<T*> 明确所有权

F.21: To return multiple “out” values, prefer returning a tuple or struct

返回多个“输出”值时,优先返回 tuplestruct,不要用输出参数(out-params)

传统写法:输出参数

int foo(int inValue, int& outValue) {outValue = inValue * 2;return inValue * 3;
}
int main() {int number = 4;int answer = foo(5, number);return 0;
}

问题

  • number 是隐含的输出值,看起来像输入。
  • 函数返回值和“副作用输出”分开,阅读困难。
  • 不符合现代 C++ 倡导的值语义风格。

更好方式 1:自定义 struct

struct twoNumbers {int value1;int value2;
};
twoNumbers fooStruct(int inValue) {return twoNumbers{ inValue * 2, inValue * 3 };
}
int main() {twoNumbers result = fooStruct(6);int number = result.value1;int answer = result.value2;return 0;
}

优点

  • 清晰表达含义(用字段名说明含义)
  • 编译器可优化拷贝(RVO)
  • 接口干净,没有输出引用

更好方式 2:返回 std::tuple

std::tuple<int, int> fooTwo(int inValue) {return std::make_tuple(inValue * 2, inValue * 3);
}
使用 std::tie 拆解:
int number, answer;
std::tie(answer, number) = fooTwo(9);
使用结构化绑定(C++17 起):
auto [answer, number] = fooTwo(9);

优点

  • 表达力强,代码简洁
  • 结构化绑定让读取 tuple 更方便
    但 tuple 缺点是:字段无名称,不够语义化。如果语义重要,还是 struct 更好。

可选方式:std::optional<T>

如果你只是返回一个对象和一个“是否有效”的布尔值,可以用 std::optional<T>

std::optional<int> maybeDivide(int a, int b) {if (b == 0) return std::nullopt;return a / b;
}
int main() {auto result = maybeDivide(10, 0);if (result)std::cout << "Result: " << *result << '\n';elsestd::cout << "Division failed.\n";
}

为什么要避免输出参数?

问题原因
不清晰输出参数隐藏在函数签名中
副作用函数修改了外部变量,阅读成本高
可读性差调用者必须准备变量来传入修改
无法组合输出参数难以用于链式表达式或惰性计算

总结:建议优先顺序

目的推荐做法
返回多个有语义的值自定义 struct
返回多个简单值std::tuple + 结构化绑定
返回可选单值std::optional<T>
不建议用引用参数 (int& out) 作为输出

你提到的是现代 C++ 中推荐使用的 enum class(作用域枚举),这是 C++11 引入的一项重要特性。下面逐条解释你贴出的内容,并说明其意义。

使用 enum class 的好处

enum class Error { OK, FileNotFound, OutOfMemory };
enum class Ratings { Terrible, OK, Terrific };
enum oldStyle { OH, OK, OR };

传统的 enum(如 oldStyle)的问题:

oldStyle Oklahoma = OK;
  • 你可以直接写 OK,没有作用域前缀。
  • 名字冲突:多个枚举如果都有 OK,只能有一个能叫 OK。
  • 自动转换为 int,可能造成隐式错误:
int x = OK;  // 自动转 int,危险

enum class 的优势

Error result = Error::OK;
Ratings stars = Ratings::OK;
int r = static_cast<int>(result);
  • 名字必须加作用域限定,例如 Error::OK,防止冲突。
  • 不会自动转换为 int,必须显式转换:
int r = static_cast<int>(result);
  • 可以在不同枚举里重复名字(每个都有自己的作用域)

更强类型、更安全

特性enumenum class
作用域限定
隐式转为 int
可以重名(如都叫 OK)
类型安全(可当作独立类型)
推荐旧风格强烈推荐

可指定底层类型(C++11 起)

enum class Error : uint8_t { OK, FileNotFound, OutOfMemory };
  • 默认底层类型是 int,但可以用更小(或大)的类型。
  • 适用于节省空间或做序列化通信协议。

实践建议

  • 永远使用 enum class,除非你明确需要与 C API 兼容。
  • 避免老式的 enum,尤其是放在头文件里的(容易污染命名空间)。
  • 使用 static_cast<int>(e) 明确转为整型。

示例总结:

enum class Error { OK, FileNotFound, OutOfMemory };
enum class Ratings { Terrible, OK, Terrific };
Error result = Error::OK;
Ratings stars = Ratings::OK;
// Cannot do this:
// int x = result;   错误
// Must be explicit:
int x = static_cast<int>(result);  // 

你提供的内容出自 C++ Core Guidelines(由 Kate Gregory 和 Bjarne Stroustrup 等人推动),主题是提高代码的安全性、可读性和意图表达。我们逐条来解释并 翻译理解这些条目。

I.12: 使用 not_null 明确指针不能为空

Service s(1);
Service* ps = &s;
i = ps->DoSomething();
ps = nullptr;          //  潜在空指针异常
i = ps->DoSomething(); // 崩溃

使用 GSL(Guidelines Support Library)中的 not_null

#include <gsl/gsl>
gsl::not_null<Service*> ps = &s;
ps = nullptr; //  编译失败或运行时断言

好处

  • 防止空指针解引用
  • 提升性能(不需要反复检查指针是否为 nullptr
  • 表达意图:这个指针不能为 null,不是“可能为 null”

避免不安全的类型转换(ES.46)

C++ 中隐式转换可能丢失信息,例如:

int x = 300;
char c = x; //  隐式缩窄,char 只有 8 位,丢失数据

使用 GSL 中的 narrownarrow_cast

#include <gsl/gsl>
int x = 300;
char c = gsl::narrow<char>(x);       //  抛异常(值改变了)
char c2 = gsl::narrow_cast<char>(x); //  允许丢失数据,但开发者明确知道这事

narrow vs narrow_cast 区别

功能narrow<T>(x)narrow_cast<T>(x)
类型转换
运行时检查抛出异常不检查
有信息丢失时报错安静执行
使用目的安全性第一(调试优先)性能优先,但我知道后果

总结:为什么要用这些工具

工具目的帮助
gsl::not_null<T*>明确一个指针绝不能是 null编译或运行时强制检查
gsl::narrow<T>类型转换必须安全运行时防止隐式精度丢失
gsl::narrow_cast<T>允许转换但表达开发者意图编译时不报错,清晰表达风险

最终目标

  • 编译器和工具 帮你发现错误
  • 表达清晰的意图,让别人 看得懂你的代码
  • 提前发现 bug,减少运行时崩溃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85307.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java并发编程实战 Day 28:虚拟线程与Project Loom

【Java并发编程实战 Day 28】虚拟线程与Project Loom 文章内容 在“Java并发编程实战”系列的第28天&#xff0c;我们将聚焦于**虚拟线程&#xff08;Virtual Threads&#xff09;**和 Project Loom&#xff0c;这是 Java 在高并发场景下的一次重大革新。随着现代应用对性能和…

Linux系统移植⑦:uboot启动流程详解-board_init_r执行过程

Linux系统移植⑦&#xff1a;uboot启动流程详解-board_init_r执行过程 在uboot中&#xff0c;board_init_r 是启动流程中的一个关键函数&#xff0c;负责完成板级&#xff08;board-specific&#xff09;的后期初始化工作。以下是关于该函数的详细说明&#xff1a; 1. 函数作…

OpenStack入门体验

1.1云计算概述 相信大家都听到很多的阿里云、腾讯云、百度云等等这些词,那到底什么是云计算?云 计算又能做什么? 1.1.1什么是云计算 云计算(cloud computing)是一种基于网络的超级计算模式,基于用户的不同需求,提供所需的资源,包括计算资源、存储资源、网络资源等。云计算…

RK 安卓10/11平台 HDMI-IN 调试

这篇文章我们介绍一下在安卓9、10、11的版本上&#xff0c;rk平台的hdmi-in功能是如何实现的&#xff0c;下篇文章我们再介绍安卓12之后的版本有了什么变化。希望对在rk平台调试hdmi-in功能的朋友有一些帮助。 目录 &#xff08;1&#xff09;概述 &#xff08;2&#xff09;…

MongoDB学习记录(快速入门)

MongoDB核心 基础概念 数据库 数据库是按照数据结构来组织、存储和管理数据的仓库。在内存中运行的&#xff0c;一旦程序运行结束或者计算机断电&#xff0c;程序运行中的数据都会丢失。我们需要将一些程序运行的数据持久化到硬盘之中&#xff0c;以确保数据的安全性。数据库…

阿里一面:微服务拆分需要考虑什么因素?

要拆分微服务&#xff0c;首先我们要了解微服务拆了会有什么问题&#xff1f;怎么合理拆服务&#xff1f; 拆分服务会带来什么问题&#xff1f; 举个电商系统下单扣库存的例子。 对于单体应用&#xff0c;通讯在进程内部进行&#xff0c;下单方法调用扣库存方法&#xff0c;…

3D高斯泼溅和4D高斯

1.高斯函数 想象你往平静的湖水里扔一块石头&#xff0c;水波会以石头落点为中心向外扩散&#xff0c;形成一个逐渐衰减的圆形波纹。高斯函数的形状就和这个波纹类似&#xff1a; 中心最高&#xff08;石头落点&#xff0c;波峰最强&#xff09;。越往外&#xff0c;高度&…

comfyui插件和comfyui mac安装

mac comfyui安装包 ComfyUI.zip&#xff0c;官方最新0.3.40&#xff0c;如果后续官方有迭代&#xff0c;可以直接通过git更新源码升级 comfyui插件下载&#xff0c;解压放到custom_nodes目录下&#xff0c;包含 comfyui-animatediff-evolved&#xff08;视频插件&#xff09; 和…

面试题SpringCloud

SpringCloud有哪些特征&#xff1f; 分布式/版本化配置服务注册与发现路由服务到服务的调用负载均衡断路器领导选举和集群状态分布式消息传递 SpringCloud核心组件&#xff1f; Eureka 注册中心Ribbon 客户端负载均衡Hystrix&#xff1a; 服务容错处理Feign:声明式Rest客户端Zu…

ASR-PRO语音识别可能出现的问题

ASR-PRO语音识别可能出现的问题 4月份有一天刷到牢大/爱丽丝语音自开关灯设备&#xff0c;心血来潮&#xff0c;博主也是浅尝了一下&#xff0c;由此也总结一下&#xff0c;实现此项目会出现的问题。 在实现爱丽丝开关灯模块时ASRPRO语音识别可能出现的问题如下&#xff1a; …

苍穹外卖--缓存菜品Spring Cache

Spring Cache是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;例如&#xff1a; ①EHCache ②Caffeine ③Redis 常用注解…

个人简历制作MarkDown模板

MarkDown制作个人简历的模板放在了github上&#xff0c;大家如有需求&#xff0c;请自取&#xff1a; https://github.com/QQQQQQBY/ResumeTemplate 介绍一下此模板的特点&#xff1a; &#x1f338;个人面试期间使用的、整理的简历格式&#xff0c;现在分享给大家。 ⭐简历采…

【MySQL数据库 | 第五篇】DDL操作2

文章目录 当前数据库student的数据数据表操作 - 修改&删除&#x1f4d6;修改操作增加字段&#x1f44f;案例&#xff1a;向数据表student中添加字段 id修改字段的数据类型【只能修改字段的属性】&#x1f44f;案例&#xff1a;将student表中字段age的属性由tinyint unsigne…

【浏览器插件】如何开发一个Chrome浏览器插件

这篇文章来介绍一下,如何开发一个自己的Chrome浏览器插件程序。 Chrome浏览器插件,其实是让浏览器替我们执行我们自己写的代码,既然要让浏览器执行代码,那么首先,就需要定义一个规范,也就是说,需要让Chrome浏览器知道,你写的程序是一个插件。 这就需要介绍一下插件中…

详细讲解Redis为什么被设计成单线程

Redis 被设计成单线程的原因主要有以下几点&#xff0c;这些原因涉及性能优化、复杂性控制、数据一致性以及适用场景等多个方面&#xff1a; 1. 简化设计与实现 避免锁竞争&#xff1a;多线程环境下&#xff0c;多个线程访问共享资源时需要加锁来保证数据一致性。锁的使用会增…

Hive 逻辑优化器

Optimizer PointLookupOptimizer 作用&#xff1a;把符合条件的 OR 表达式转为 IN。 参数hive.optimize.point.lookup 设置是否开启 PointLookupOptimizer&#xff0c;默认为 true. 参数 hive.optimize.point.lookup.min 控制多少个 OR 表达式转为 IN&#xff0c;默认 31。 例…

ZYNQ Petalinux实战:PCIe直通NVMe固态硬盘,解锁存储性能新极限!

突破SD卡和SATA的速度枷锁!本文将手把手教你如何在ZYNQ平台上通过PCIe接口驱动NVMe固态硬盘。从硬件设计、Linux内核配置到创新性的DMA零拷贝优化,实现2000MB/s+ 的存储性能飞跃,附完整代码解析和性能实测对比。 一、为什么选择PCIe NVMe?存储性能革命 ZYNQ传统存储方案面…

05-mcp-server案例分享-用豆包大模型 1.6 手搓文生图视频 MCP-server发布到PyPI官网

1前言 上期给大家介绍过mcp-server案例分享-用豆包大模型 1.6 手搓文生图视频 MCP-server。当时部署的方式使用了一个私有云SSE的部署。当时缺少一个本地部署的方式&#xff0c;有的小伙伴给我留言能不能有一个本地话部署方式了。今天就给大家带来一个本地化部署的方案。 话不…

MCP Parameters 增加描述

场景&#xff1a;本地MCP开发完后是否发现CLINE上显示的Parameters 显示No description 方法1 &#xff1a;使用参数元数据 (Annotated) 可以使用 Pydantic 的with 类提供有关参数的其他元数据Annotated。这种方法更受欢迎&#xff0c;因为它更现代&#xff0c;并且将类型提示…

STM32 GPIO 寄存器开发

&#x1f527; ​一、核心寄存器概览​ ​寄存器​​功能​​位宽​​关键位域​​GPIOx_CRL/CRH​配置引脚模式&#xff08;输入/输出/复用/模拟&#xff09;和输出参数32位每4位控制1个引脚&#xff1a;CNF[1:0]&#xff08;模式&#xff09; MODE[1:0]&#xff08;速度&am…