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

C++初学者指南-3.自定义类型(第一部分)-基本自定义类型/类

C++初学者指南-3.自定义类型(第一部分)-基本自定义类型/类

文章目录

  • C++初学者指南-3.自定义类型(第一部分)-基本自定义类型/类
    • 1.类型种类(简化)
    • 2.为什么选择自定义类型?
      • 单向计数器
      • 提升序列
    • 3.限制成员访问
      • 成员函数
      • 公共(public) vs. 私有(private)的可见性
      • const成员函数
      • 成员声明 vs. 定义
      • 操作符成员函数
    • 3.初始化
      • 成员初始化
      • 构造函数
      • 默认构造函数与自定义构造函数
      • 显式构造函数 ↔ 隐式转换
      • 构造函数委托
      • 最令人困扰的解析
    • 4.设计,约定和风格
      • 接口中的数据类型
      • 成员和非成员
      • 避免使用Setter/Getter对!
      • 命名
      • 使用专用类型!
    • 5.示例实现
      • 示例1:单向的计数器
      • 示例2:递增序列

1.类型种类(简化)

基本类型void, bool, char, int, double, …
简单聚合主要目的:数据分组
聚合:可能包含一个或多个基本类型或其他兼容的聚合类型
无法控制组成类型的相互作用。
简单,如果只有(编译器生成)默认构造函数/析构函数/拷贝构造函数/赋值函数
标准的内存布局(所有成员按声明顺序连续排列),如果所有成员都具有相同的访问控制(例如全部是公共的)
更复杂自定义类型主要目的:确保正确性和安全性保证。
自定义不变量和对成员相互作用的控制。
限制成员访问。
成员函数
用户定义的构造函数/成员初始化。
用户自定义析构函数/拷贝构造函数/赋值函数。
可以是多态的(包含虚成员函数)。

2.为什么选择自定义类型?

正确性保证

  • 不变量 = 从不改变的行为和/或数据属性
  • 通过控制/限制对数据成员的访问来避免数据损坏
  • 使用专用类型来限制函数的输入/输出值

可复用的抽象

  • 隐藏低级实现细节的易于使用的接口
  • 不受内部实现变化影响的稳定接口
  • 可重用的常用功能的抽象(例如,动态数组)

资源管理
也被称为 RAII(资源获取即初始化)

  • 在构造对象时获取一些资源(内存、文件句柄、连接等)
  • 当对象被销毁时释放/清理资源(释放内存、关闭连接等)

单向计数器

  • 存储整数
  • 初始化为0
  • 不变性:计数只能增加(不能减少或重置)
monotonous_counter c;   
cout << c.reading();  // prints 0
c.increment();
cout << c.reading();  // prints 1
c.increment();
c.increment();
cout << c.reading();  // prints 3

简单的聚合类型不能保证:

struct frail_counter {int count;
};
frail_counter c;    
cout << c.count; // any value
c.count++;
c.count = 11;
  • 整数成员未自动初始化为 0
  • 可以自由修改聚合类型的任何整数成员
  • ⇒仅使用整数没有任何优势

提升序列

  • 应该存储整数
  • 不变性:元素数量只能增加,即只能插入新元素,而不能移除它们
  • 不变性:元素必须始终按升序排序
    在这里插入图片描述
    简单的聚合不能保证:
struct chaotic_sequence {std::vector<int> nums;
};
chaotic_sequence s;     
s.nums.push_back(8);   8
s.nums.push_back(1);   8  1
s.nums.push_back(4);   8  1  4
s.nums.pop_back(4);    8  1 

可能违反要求

  • 数字不一定按升序排序
  • 我们可以随意操作数字,比如删除数字等
  • 与使用普通std::vector相比没有什么优势

3.限制成员访问

成员函数

class monotonous_counter {int count_;  // ← 数据成员
…void increment () {  // ← 成员函数++count_; }
};
class ascending_sequence {std::vector<int> seq_;  // ← 数据成员
…void insert (int x) {   // ← 成员变量// 在正确的位置将 x 插入到 nums 中}
};

成员函数可用于

  • 操作或查询数据成员
  • 控制/限制对数据成员的访问
  • 隐藏低级实现细节
  • 确保正确性:保持/保证不变量
  • 确保清晰度:为不同类型的用户设计良好结构的接口
  • 确保稳定性:内部数据表示(大部分)独立于接口
  • 避免重复/模板:避免重复/样板:对于潜在的复杂操作,只需调用一次即可

公共(public) vs. 私有(private)的可见性

私有成员只能通过成员函数访问:

class ascending_sequence {
private:std::vector<int> seq_;// … more private members
public:void insert (int x) {}auto size () const { return seq_.size(); }// … more public members
};
int main () {ascending_sequence s;s.insert(8);         //  'insert' 是公共的auto n = s.size();   //  'size' 是公共的auto i = s.seq_[0];  //  编译错误: 'seq_' 是私有的auto m = s.seq_.size(); //  编译错误s.seq_.push_back(1);    //  编译错误
}

struct 与 class – 主要区别在于默认可见性:
在这里插入图片描述

关键字通常用于
struct公共数据的简单聚合
class私有数据、成员函数、不变量……

const成员函数

只有带 const 修饰的成员函数才能被 const 对象调用:

class ascending_sequence {std::vector<int> seq_;
public:void insert {}auto size () const { return seq_.size(); }
};
int main () {ascending_sequence s;s.insert(88);  //  s不是const的auto const& cs = s; cs.insert(5);  //  编译错误: 'insert' 不是const的
}

接受常量(引用)参数的函数不仅承诺不修改它,这个承诺还将被编译器检查并强制执行。

void foo (ascending_sequence const& s) {// 's' is const reference ^^^^^auto n = s.size();  //  'size' 是 consts.insert(5);  //  编译错误: 'insert' 不是 const
}

const成员函数内部的成员是const

class monotonous_counter {int count_;
public:int reading () const { //  编译错误: count_ 是 const:count_ += 2;return count_;}
};
class ascending_sequence {std::vector<int> seq_;
public:auto size () const {  // 'seq_' 是 const//  编译错误: 调用非const的'push_back'seq_.push_back(0);  //  vector的成员 'size()' 是const的return seq_.size();  }
};

成员函数可以通过const进行重载
如果一个成员函数是const-限定的,另一个不是,它们可以有相同的名称(和参数列表)。这样可以清楚地区分只读访问和读写操作。

class interpolation {int t_;public:// 读/写函数对:void threshold (int t)  { if (t > 0) t_ = t; }int  threshold () const { return t_; }// 可写访问一个'node'node& at (int x) {}// 只读访问一个'node'node const& at (int x) const {}
};

成员声明 vs. 定义

class MyType {int n_;// 更多的成员 …
public:// 声明 + 内联定义int count () const { return n_; } // 只声明double foo (int, int);
};
// 独立定义
double MyType::foo (int x, int y) {// lots of stuff …
}
  • 通常复杂的成员函数的定义会放在类外面(放到单独的源文件中)。
  • 然而,像接口适配器函数、获取器(如 count)这样的小成员函数应该嵌入实现,即直接在类体中,以达到最佳性能。
  • 暂时我们会将所有成员函数保持内联,直到我们了解有关分离编译的知识。

操作符成员函数

特殊成员函数

class X { …Y operator [] (int i) { … } 
};

使用下标运算符。

X x;
Y y = x[0];

在这里插入图片描述

3.初始化

成员初始化

1.成员初始化器 (C++11)

class counter {// counter 应该从0开始int count_ = 0;
public:…
};
class Foo {int i_ = 10;double x_ = 3.14;
public:…
};

2.构造函数初始化列表
构造函数(ctor) = 创建对象时执行的特殊成员函数

class counter {int count_;
public:counter(): count_{0} { }…
};
class Foo {int i_;     // 1stdouble x_;  // 2nd
public:    Foo(): i_{10}, x_{3.14} { }// same order: i_ , x_ …
};

提示:确保初始化列表中的成员顺序始终是与成员声明顺序相同!

构造函数

构造函数(ctor) = 创建对象时执行的特殊成员函数

  • 构造函数的 函数名称 = 类名称
  • 没有返回类型
  • 可以通过初始化列表初始化数据成员
  • 可以在第一次使用对象之前执行代码
  • 可用于建立不变量
  • 默认构造函数 = 不带参数的构造函数
    在这里插入图片描述
    构造函数的独立定义
    与其他成员函数的方式相同
class MyType { …
public:MyType ();  // 声明…
};
// 独立定义
MyType::MyType (): … { … }

注意:确保初始化列表中的成员顺序始终是 与成员声明顺序相同!

  • 初始化列表中的不同顺序可能会导致未定义的行为,例如访问未初始化的内存。
  • 这里,在默认构造函数中,我们需要确保只有在min_和max_被初始化之后才能访问v_{min_,max_}。
  • 有些编译器会对此发出警告:例如 g++/clang++ 使用 -Wall 或 -Wreorder 选项,这就是为什么要始终启用并且决不忽略编译器警告的另一个原因!

默认构造函数与自定义构造函数

没有用户定义的构造函数⇒编译器生成一个

class BoringType { public: int i = 0; };
BoringType obj1;     // 正确
BoringType obj2 {};  // 正确

至少有一个特殊构造函数
⇒ 编译器不生成默认构造函数

class SomeType {public:// special constructor:explicit SomeType (int x){}
};
SomeType s1 {1};  //  特殊 (int) 构造函数
SomeType s2;      //  编译错误: 没有默认构造函数!
SomeType s3 {};   //  编译错误: 没有默认构造函数!

TypeName() = default;
⇒ 编译器生成默认构造函数的实现(编译器实现没有参数的构造函数就是默认构造函数)

显式构造函数 ↔ 隐式转换

// 函数有一个 'Counter' 参数
void foo (Counter c) { … }
void bar (Counter const& c) { … }

隐式转换(不好的方式)

class Counter {int count_ = 0;
public:Counter (int initial):count_{initial} {}};
// 从‘2‘创建了'Counter'对象
foo(2);           // 正确
bar(2);           // 正确
foo(Counter{2});  // 正确
bar(Counter{2});  // 正确

显式构造函数(推荐的方式)

class Counter {int count_ = 0;
public:explicitCounter (int initial):count_{initial} {}};
// 没有隐式转换: 
foo(2);  //  编译错误
bar(2);  //  编译错误
foo(Counter{2});  // 正确
bar(Counter{2});  // 正确

注意:默认情况下,让用户定义的构造函数显式!

  • 隐式转换是难以发现的错误的主要来源!
  • 只有在绝对必要且含义明确时,才使用非显式构造函数,如果需要直接从参数类型进行转换。
  • 一些较老的教材和使用 C++98 的人可能会告诉你,只需要关心单参数构造函数的隐式转换。然而自C++11以来,情况已经改变,因为现在你也可以从花括号括起的值列表中隐式地构造对象。

构造函数委托

= 调用初始化列表中的其他构造函数

class Range {int a_;int b_;
public:// 1) 特殊构造函数explicit Range (int a, int b): a_{a}, b_{b} {if (b_ > a_) std::swap(a_,b_);}// 2) 特殊[a,a]构造 - 委托给[a,b]构造函数explicit Range (int a): Range{a,a} {}// 3) default constructor - delegates to [a,a] ctorRange (): Range{0} {}};
Range r1;        // 3) ⇒ r1.a_: 0  r1.b_: 0
Range r2 {3};    // 2) ⇒ r2.a_: 3  r2.b_: 3
Range r3 {4,9};  // 1) ⇒ r3.a_: 4  r3.b_: 9
Range r4 {8,2};  // 1) ⇒ r4.a_: 2  r4.b_: 8

最令人困扰的解析

由于C++语法中的歧义,无法使用空括号进行对象构造:

class A { … };
A a ();  // 声明了没有参数和返回值的函数'a'
A a;     // 构造一个A类型对象
A a {};  // 构造一个A类型对象

4.设计,约定和风格

每种类型都应该有一个目的

  • 因为这样可以减少将来对它的修改可能性。
  • 降低出现新错误的风险
  • 根据您的类型保持代码更加稳定

保持数据成员私有并使用成员函数访问/修改数据

  • 这样用户只能通过稳定的接口与您的类型进行交互。
  • 避免数据损坏 / 允许不变量保证。
  • 如果你改变了类型的内部实现,类型的用户不需要改变他们的代码。

const - 限定所有非修改成员函数

  • 为了清楚地表明对象的内部状态如何以及何时发生改变。
  • 使您更难错误地使用您的类型。
  • 启用编译器可变性检查。
  • 更好地推理正确性,特别是在涉及同时访问对象的情况下,例如来自多个线程。
接口应该易于正确使用,并且难以错误使用。—  Scott Meyers

函数或类型的用户不应该对其目的、参数的意义、先决条件/后置条件和副作用感到困惑。

接口中的数据类型

#include <cstdint>
#include <numeric_limits>
class monotonous_counter {
public:// 公共类型别名using value_type = std::uint64_t;
private:value_type count_ = 0;
public:value_type reading () const { return count_; }…
};
const auto max = std::numeric_limits<monotonous_counter::value_type>::max();

不要泄露实现细节:

  • 只有当别名类型在您的类的公共接口中使用时,即作为公共成员函数的返回类型或参数时,才将类型别名公开。
  • 如果别名类型只在私有成员函数中使用或用于私有数据成员,请不要将类型别名公开。

成员和非成员

如何实现一个特性/添加新功能?

  • 只需要访问公共数据(例如通过成员函数访问)⇒ 实现为独立函数
  • 需要访问私有数据⇒作为成员函数实现

示例:间隔类型 gap类
如何实现一个函数,使新的间隔对象的两个边界都移动相同的量?

class gap {int a_; int b_;
public:explicit gap (int a, int b): a_{a}, b_{b} {}int a () const { return a_; }int b () const { return b_; }
};

推荐的独立式函数实现

gap shifted (gap const& g, int x) {return gap{g.a()+x, g.b()+x};
}
  • 实现仅依赖于gap的公共接口
  • 我们没有更改类型 gap 本身 ⇒ 依赖它的其他代码不需要重新编译

不推荐的成员函数实现

class gap { …gap shifted (int x) const {return gap{a_+x, b_+x};}
};
  • gap的其他用户可能想要一个具有不同语义的移位函数,但他们现在只能使用我们的函数了。
  • 所有其他代码(取决于 gap)都需要重新编译。

避免使用Setter/Getter对!

  • 使用动作/动词函数而不是仅仅使用设置器(Setter)。
  • 通常可以更好地对问题进行建模。
  • 更精细的控制。
  • 更好的代码可读性/意图表达。

推荐的描述性操作:

class Account { …void deposit (Money const&);Money try_withdraw (Money const&);Money const& balance () const;
};

不推荐的Setter/Getter对:

class Account { …void set_balance (Money const&);Money const& balance () const;
};

命名

名称应反映类型/函数的用途
推荐的:可理解的

class IPv6_Address {…};
class ThreadPool {…};
class cuboid {…};
double volume (cuboid const&) {…}

不推荐的:太笼统了

class Manager {…};
class Starter {…};
class Pool {…};
int get_number (Pool const&) {…}

不要在类型、变量、函数、私有数据成员等名称中使用前导下划线或双下划线!

  • 以下划线开头和/或包含双下划线的名称是保留给标准库和/或编译器生成的实体的。
  • 使用具有前置下划线或双下划线的名称可能会引发未定义行为!
  • 一个常见且没有问题的约定是在私有数据成员后面加下划线。
    在这里插入图片描述

使用专用类型!

  • 限制输入参数值
  • 确保中间结果的有效性
  • 保证返回值有效性

⇒编译器作为正确性检查器,如果它能编译通过,它应该是正确的

// 明确的接口:
double volume (Cuboid const&);
// 输入保证:角度以弧度为单位
Square make_rotated (Square const&, Radians angle);
// 只接受有效数量(例如:> 0)
Gadget duplicate (Gadget const& original,  Quantity times);
// 结果保证:向量已被规范化。
UnitVector3d dominant_direction (WindField const&);
//避免混淆,使用一个好的单位库。
si::kg mass (EllipsoidShell const&, si::g_cm3 density);
bool has_cycles (DirectedGraph const&);
// 易于理解的控制流程和逻辑:
Taxon species1 = classify(image1);
Taxon species2 = classify(image2);
Taxon lca = taxonomy.lowest_common_ancestor(species1, species2);

5.示例实现

示例1:单向的计数器

  • 新计数器从 0 开始
  • 只能往上数,不能往下数。
  • 对当前计数值的只读访问
#include <iostream>   // std::cout
#include <cstdint>    // std::uint64_t
class monotonous_counter {
public:using value_type = std::uint64_t;
private:value_type count_ = 0;  // initial
public:monotonous_counter () = default;explicit monotonous_counter (value_type init) noexcept: count_{init} {}void increment () noexcept { ++count_; }[[nodiscard]] value_type reading () const noexcept { return count_; }
};
int main () {monotonous_counter c;c.increment();std::cout << c.reading();  // prints 1c.increment();c.increment();std::cout << c.reading();  // prints 3
}

运行示例

示例2:递增序列

  • 存储整数
  • 通过索引对存储元素进行只读访问
  • 只能插入新元素,但不能删除它们
  • 元素始终按升序排序
  • 只能通过公共接口修改内容

‘insert’ 操作的实现以及 ‘begin’ 和 ‘end’ 成员函数的作用在我们学习了迭代器和标准库中的算法后会变得更加清晰。

#include <iostream>   // std::cout
#include <vector>     // std::vector
#include <algorithm>  // std::lower_bound
class ascending_sequence {
public:using value_type = int;
private:using storage_t = std::vector<value_type>;storage_t seq_;
public:using size_type = storage_t::size_type;void insert (value_type x) {// use binary search to find insert positionseq_.insert(std::lower_bound(seq_.begin(), seq_.end(), x), x);}[[nodiscard]] value_type operator [] (size_type idx) const noexcept { return seq_[idx]; }[[nodiscard]] size_type size () const noexcept { return seq_.size(); }// enable range based iteration[[nodiscard]] auto begin () const noexcept { return seq_.begin(); }[[nodiscard]] auto end ()   const noexcept { return seq_.end(); }
};
int main () {ascending_sequence s;  // s.seq_:  s.insert(7);           // s.seq_: 7s.insert(2);           // s.seq_: 27s.insert(4);           // s.seq_: 247s.insert(9);           // s.seq_: 2479s.insert(5);           // s.seq_: 24579std::cout << s[3];     // prints 7for (auto x : s) {std::cout << x <<' ';  // 2 4 5 7 9}// use type aliasesascending_sequence::value_type x = 1;ascending_sequence::size_type  n = 2;
}

运行示例

附上原文地址
如果文章对您有用,请随手点个赞,谢谢!^_^

相关文章:

C++初学者指南-3.自定义类型(第一部分)-基本自定义类型/类

C初学者指南-3.自定义类型(第一部分)-基本自定义类型/类 文章目录 C初学者指南-3.自定义类型(第一部分)-基本自定义类型/类1.类型种类&#xff08;简化&#xff09;2.为什么选择自定义类型&#xff1f;单向计数器提升序列 3.限制成员访问成员函数公共(public) vs. 私有(private…...

iOS之如何创建.framework静态库

番外&#xff1a;想要查看如何创建.a静态库可前往看我iOS之如何创建.a静态库-CSDN博客这篇文章。 一、创建framework项目 创建framework工程要选择iOS --> Cocoa Touch Framework输入项目名称PrintFramework也是编译生成的framework的名称。framework的名称也可以以后在项目…...

C程序设计谭浩强第五版

程序习题 第一章1、第5题2、第6题 第三章1、第2题2、第2题3、第3题4、第4题Tips 第一章 1、第5题 编写一个C程序,运行时输出以下图形: #include <stdio.h> int main() {for (int i 0; i < 4; i) // 输出4行循环控制{for (int j 0; j < i; j) //第几行就输出几…...

石油化工厂为什么要用专业防爆手机?

防爆手机之所以必须使用专业设计的产品&#xff0c;主要是出于安全考虑&#xff0c;以防止在易燃易爆环境中因手机使用不当引发爆炸事故。以下几点详细解释了使用专业化工防爆手机的必要性&#xff1a; 本质安全设计&#xff1a;顶坚专业防爆手机采用了本质安全&#xff08;本安…...

文本生成sql模型(PipableAI/pip-sql-1.3b)

安装环境 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers 代码 question "What are the email address, town and county of the customers who are of the least common gender?"sc…...

机器学习中的数学底蕴与设计模式

在说机器学习设计模式之前&#xff0c;想多说几句&#xff0c;在进入软件行业最初的10年&#xff0c;那时候耳熟能详的基本就是多线程编程&#xff0c;互斥同步锁&#xff0c;设计模式&#xff0c;OOA&#xff0c;OOP&#xff0c;常规数组&#xff0c;tree&#xff0c;图的数据…...

【Android面试八股文】性能优化相关面试题:如何查找CPU占用?

文章目录 一、 如何查找CPU的占用问题二、TraceView的使用关于TraceView和Android Studio的Profiler第一步、通过Android studio 打开`Android profiler`第二步、使用步骤第三步、技术说明第四步、CPU占用相关指标说明扩展阅读一、 如何查找CPU的占用问题 在Android开发中,如…...

面试框架一些小结

springcloud的⼯作原理 springcloud由以下⼏个核⼼组件构成&#xff1a; Eureka&#xff1a;各个服务启动时&#xff0c;Eureka Client都会将服务注册到Eureka Server&#xff0c;并且Eureka Client还可以反过来从Eureka Server拉取注册表&#xff0c; 从⽽知道其他服务在哪⾥ …...

c# 往window注册表写入数据后,未写入指定的路径

c# 往window注册表写入数据后&#xff0c;未写入指定的路径 最近在用c#开发一个往注册表写入数据的一个项目&#xff0c;发现将输入写入 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell这个路径时&#xff0c;数据并没写入到这个…...

树莓派4B_OpenCv学习笔记13:OpenCv颜色追踪_程序手动调试HSV色彩空间_检测圆

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; OpenCv颜色追踪_程序手动调试HSV色彩空间_检测灰度图中的…...

Golang | Leetcode Golang题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; func rob(nums []int) int {if len(nums) 0 {return 0}if len(nums) 1 {return nums[0]}first : nums[0]second : max(nums[0], nums[1])for i : 2; i < len(nums); i {first, second second, max(first nums[i], second)}return se…...

基于ruoyi-app的手机短信登录(uniapp)

本篇用于记录h5的框架搭建 组件地址:短信验证码登陆&#xff0c;手机号&#xff0c;验证码倒计时 - DCloud 插件市场 调整后的表单组件代码: <template><view class"login-view"><!-- <input type"tel" confirm-type"确认"…...

机器学习环境搭建

前言 个人笔记&#xff0c;记录框架和小问题&#xff0c;没有太详细记载。。 1、Anaconda安装 下载地址&#xff1a; Free Download | Anaconda &#xff08;慢&#xff09; ​ 国内镜像&#xff1a;https://link.csdn.net/?targethttp%3A%2F%2Fitcxy.xyz%2F241.html 下载…...

2095.删除链表的中间节点

给你一个链表的头节点 head 。删除链表的中间节点 &#xff0c;并返回修改后的链表的头节点 head。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于 x 的最大整数。 对于 n 1、2、3、4 和…...

Qt QML 坑

Qt QML 坑 QML Listview 1、不定高item 导致item重叠 ListView {id: _cityListViewproperty var _cityArray: [{ type:"A",cityArray:[]},{ type:"B",cityArray:[]},{ type:"C",cityArray:[]},{ type:"D",cityArray:[]}]model: List…...

Chrome浏览器web调试(js调试、css调试、篡改前置)

目录 1. 打开开发者工具(Dev Tool) 2. 打开命令菜单 截图 3. 面板介绍 4. CSS调试 右键检查快速到达元素处 查找DOM数 利用面板Console查找DOM节点 内置函数查找上一个选择点击的元素 5. 调试JS代码(Javascript调试) 日志调试 选择查看日志等级 眼睛观测变量 …...

【Java】Logbook优化接口调用日志输出,优雅!

logbook 简介 很多人可能没有接触过 logbook&#xff0c;但它的确是一个很好用的日志框架。引用官网的介绍 Logbook 是一个可扩展的 Java 库&#xff0c;可以为不同的客户端和服务器端技术启用完整的请求和响应日志记录。它通过以下方式满足了特殊需求&#xff1a; 允许 Web 应…...

LabVIEW电压电流实时监测系统

开发了一种基于LabVIEW和研华&#xff08;Advantech&#xff09;数据采集卡的电压电流实时监测系统&#xff0c;通过高效的数据采集和处理&#xff0c;为工业和科研用户提供高精度、实时的电压电流监测解决方案。系统采用研华USB-4711A数据采集卡&#xff0c;结合LabVIEW编程环…...

骁龙相机拍照流程分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 1.deliverInputEvent 拍照点击事件处理 2.submitRequestList Camera 提交拍照请求 3.createCaptureRequest 拍照请求帧数 骁龙相机通过binder 数据传输…...

sql-语句

文章目录 SQL语句的学习sql是什么sql的内置命令sql的种类sql mode库&#xff0c;表属性介绍&#xff1a;字符集&#xff0c;存储引擎列的数据类型&#xff1a;数字&#xff0c;字符串&#xff0c;时间列的约束DDL: 数据定义语言库表 Online DDL(ALGORITHM) *DML :数据操纵语言资…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...

工厂方法模式和抽象工厂方法模式的battle

1.案例直接上手 在这个案例里面&#xff0c;我们会实现这个普通的工厂方法&#xff0c;并且对比这个普通工厂方法和我们直接创建对象的差别在哪里&#xff0c;为什么需要一个工厂&#xff1a; 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类&#xff1a; 两个发…...

Python异步编程:深入理解协程的原理与实践指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…...