C++ Primer 构造函数再探

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 7.5构造函数再探
    • 构造函数初始值列表
    • 构造函数的初始值有时必不可少
    • 成员初始化的顺序
    • 默认实参和构造函数
    • 委托构造函数
    • 默认构造函数的作用
    • 使用默认构造函数
    • 隐式的类类型转换
      • 只允许一步类类型转换
      • 类类型转换不是总有效
    • 抑制构造函数定义的隐式转换
      • explicit构造函数只能用于直接初始化
      • 为转换显式地使用构造函数
      • 标准库中含有显式构造函数的类
      • 聚合类
    • 字面值常量类
    • constexpr构造函数

7.5构造函数再探

对于任何C++的类来说,构造函数都是其中重要的组成部分。本节继续介绍构造函数的其他功能,并对之前已经介绍的内容进行一些更深入的讨论。

构造函数初始值列表

当我们定义变量时习惯于立即对其进行初始化,而非先定义、再赋值:

string foo=“HelloNorld1“j//定义并初始化
string bar;//默认初始化成空string对象
bar="Hello World" ;//为bar赋一个新值

就对象的数据成员而言,初始化和赋值也有类似的区别。如果没有在构造函数的初始值列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。例如:

//Sales_data构造函数的一种写法,蛟然合法但比较草率:没有使用构造函数初始值
Sales_data::Sales_data(const string&s, unsigned cnt,double price)
bookNo=s;
units_sold=cnt;
revenue=cnt*price;

这段代码和我们在237页的原始定义效果是相同的:当构造函数完成后,数据成员的值相同。区别是原米的版本初始化了它的数据成员,而这个版本是对数据成员执行了赋值操作。这一区别到底会有什么深层次的影响完全依赖于数据成员的类型。

构造函数的初始值有时必不可少

有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。如果成员是const或者是引用的话,必须将其初始化。类似的,当成员属于某种类类型且该类没有定义默认构造函数时,也必须将这个成员初始化。例如:

class ConstRef{
public:
ConstRef(int ii);
private:int i;
const int c;
int& rii;
};

和其他常量对象或者引用一样,成员ci和ri都必须被初始化。因此,如果我们没有为它们提供构造函数初始值的话将引发错误:

//错误:ci和r必须被初始化
ConstRef::ConstRef(inti3)
{ //赋值,
i = ii;  //正确
ci = ii;//错误:不能给const赋值
ri = i;//错误:ri没被初始化
}

随着构造函数体一开始执行,初始化就完成了。我们初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值,因此该构造函数的正确形式应该是:

// 正确:显式地初始化引用和const成员
ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(i){}

如果成员是const、引用,或者属于森种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初值。

建议:使用构造函数初始值

在很多类中,初始化和赎值的区别事关底尿效率闰题;前者直接初始化数据成员,后者则先初始化再赋值。

除了效率问题外更重要的是,一些数据成员必须被初始化。建议读者养成使用构造函数初始值的习惯,这样能避免某些意想不到的编译错误,特别是遇到有的类含有需要构造函数初始值的成员时。

成员初始化的顺序

显然,在构造函数初始值中每个成员只能出现一次。否则,给同一个成员赋两个不同的初始值有什么意义呢?不过让人稍感意外的是,构造函数初始值列表只说明用于初始化成员的值,而不限定初始化的具体执行顺序。成员的初始化顺序与它们在类定义中的出现顺序一致:第一个成员先被初始化,然后第二个,以此类推。构造函数初始值列表中初始值的前后位置关系不会影响实际的初始化顺序。

-般来说,初始化的顺序没什么特别要求。不过如果一个成员是用另一个成员来初始化的,那么这两个成员的初始化顺序就很关键了。举个例子,考虑下面这个类:

Class X(
int i;
int j;
public:
//未定义的:士在了之前被初始化
X(int val):j(val), i(j){}

在此例中,从构造函数初始值的形式上来看仿佛是先用val初始化了j,然后再用j初始化i。实际上,i先被初始化,因此这个初始值的效果是试图使用未定义的值j初始化i!有的编译器具备一项比较友好的功能,即当构造函数初始值列表中的数据成员顺序与这些成员声明的顺序不符时会生成一条警告信息。

最好令构造函数初始值的顺序与成员声明的顺序保持一致。而且如果可能的话,尽量避免使用树些成员初始化其他成员。

如果可能的话,最好用构造函数的参数作为成员的初始值,而尽量避免使用同一个对象的其他成员。这样的好处是我们可以不必考虑成员的初始化顺序。例如,X的构造函敬如果写成如下的形式效果会更好:

X(int val);i(val),j(val){}

在这个版本中,i和j初始化的顺序就没什么影响了。

默认实参和构造函数

Sales_data默认构造函数的行为与只接受一个string实参的构造函数差不多。唯一的区别是接受string实参的构造函数使用这个实参初始化bookNo,而默认构造函数(隐式地)使用string的默认构造函数初始化bookNo。我们可以把它们重写成一个使用默认实参的构造函数:

class Sales_data{
public:
//定义默认构造函数,令其与只接变一个string实参的构造函数功能相同
Sales_data(std::string s8=""):bookNo(s){}
//其他构造函数与之前一致
Sales_data(std::string s,unsigned cnt,double rev):
bookNo(s),units_sold(cnt),revenue(revs*cnt){}
Sales_data(std::istream&is){ read(is,*this);}
//其他成员与之前的版本一致
};

当没有给定实参,或者给定了一个string实参时,两个版本的类创建了相同的对象。因为我们不提供实参也能调用上述的构造函数,所以该构造函数实际上为我们的类提供了默认构造函数。

值得注意的是,我们不应该为Sales_data接受三个实参的构造函数提供默认值。因为如果用户为售出书籍的数量提供了一个非零的值,则我们就会期望用户同时提供这些书籍的售出价格。

委托构造函数

C++11新标准扩展了构造函数初始值的功能,使得我们可以定义所谓的委托构造函数围(delegating constructor)。一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些(或者全部)职责委托给了其他构造函数。

和其他构造函数一样,一个委托构造函数也有一个成员初始值的列表和一个函数体。在委托构造函数内,成员初始值列表只有一个晶一的入口,就是类名本身。和其他成员初始值一样,类名后面紧跟圆括号括起来的参数列表,参数列表必须与类中另外一个构造函数匹配。

举个例子,我们使用委托构造函数重写Sales_data类,重写后的形式如下所示:

class Sales_data{
public:
//非委托构造函数使用对应的实参初始化成员
Sales_data(std::string&,unsigned cnt,double price):
bookNo(s),units_sold(cnt),revenue(cnt*price){}
//其余构造函数全都委托给另一个构造函数
Sales_data():Sales_data("",0,0){}
Sales_data(std::string&):Sales_qata(s,0,0){}
Sales_data(std::istream&is):Sales_data()
{
    read(is,*this);
    //其他成员与之前的版本一致
}
}

在这个Sales_data类中,除了一个构造函数外其他的都委托了它们的工作。第一个构造函数接受三个实参,使用这些实参初始化数据成员,然后结束工作。我们定义默认构造函数令其使用三参数的构造函数完成初始化过程,它也无须执行其他任务,这一点从空的构造函数体能看得出来。接受一个stzing的构造函数同样委托给了三参数的版本。

接受istreamg的构造函数也是委托构造函数,它委托给了默认构造函数,默认构造函数又接着委托给三参数构造函数。当这些受委托的构造函数执行完后,接着执行istreamg构造函数体的内容。它的构造函数体调用read函数读取给定的istream。

当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行。在Sales_data类中,受委托的构造函数体恰好是空的。假如函数体包含有代码的话,将先执行这些代码,然后控制权才会交还给委托者的函数体。

默认构造函数的作用

当对象被默认初始化或值初始化时自动执行默认构造函数。默认初始化在以下情况下发生:

  • 当我们在块作用域内不使用任何初始值定义一个非静态变量或者数组时。
  • 当一个类本身含有类类型的成员且使用合成的默认构造函数时。
  • 当类类型的成员没有在构造函数初始值列表中显式地初始化时。

值初始化在以下情况下发生:

  • 在数组初始化的过程中如果我们提供的初始值数量少于数组的大小时。
  • 当我们不使用初始值定义一个局部静态变量时。
  • 当我们通过书写形如“()的表达式显式地请求值初始化时,其中7是类型名(vector的一个构造函数兮接受一个实参用于说明vector大小),它就是使用一个这种形式的实参来对它的元素初始化器进行值初始化。

类必须包含一个默认构造函数以便在上述情况下使用,其中的大多数情况非常容易判断。

不那么明显的一种情况是类的某些数据成员缺少默认构造函数:

class NoDefault{
public:
NoDefault(conststd::string&);
//还有其他成员,但是没有其他构造函数了
}

struct A{
    //黯认情况下mymem是public的
NoDefault my_mem;
};

A a;//错误:不能为合成构造函敏

struct B{
B(){} //错误:b_member没有初始值
NoDefault b_member;
}

在实际中,如果定义了其它构造函敷,那么最好也提供一个默认构造函数

使用默认构造函数

下面的obj的声明可以正常编译通过:

Sales_data obj();                     // 正确:定义了一个函数而非对象
if(obj.isbn()== Primer_5th_ed.isbn()) //错误:obj是一个出数

但当我们试图使用obj时,编译器将报错,提示我们不能对函数使用成员访问运算符。问题在于,尽管我们想声明一个默认初始化的对象,obj实际的含义却是一个不接受任何参数的函数并且其返回值是Sales_data类型的对象。

如果想定义一个使用默认构造函数进行初始化的对象,正确的方法是去掉对象名之后的空的括号对:

正确:obj是个默认初始化的对象
Sales_data obj;

对于C++的新手程序员来说有一种常犯的错误,它们试图以如下的形式声明一个用默认构造函敏初始化的对象:

Sales_data obj();  //错误:声明了一个函数而非对象
Sales_data obj2;   //正确:obj2是一个对象而非函数

隐式的类类型转换

C++语言在内置类型之间定义了几种自动转换规则。同样的,我们也能为类定义隐式转换规则。如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数(converting constructor)。

能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。

在Sales_data类中,接受string的构造函数和接受istream的构造函数分别定义了从这两种类型向Sales_data隐式转换的规则。也就是说,在需要使用Sales_data的地方,我们可以使用string或者istream作为替代:

string null_book ="9~999~99999~9";
//构造一个临肘的Sales_data对象
//该对象的units_sold和revenue等于0,bookNo等于null_book
tem.combine(nul1l_book);

在这里我们用一个string实参调用了Sales_data的combine成员。该调用是合法的,编译器用给定的string自动创建了一个Sales_data对象。新生成的这个(临时)Sales_data对象被传递给combine。因为combine的参数是一个常量引用,所以我们可以给该参数传递一个临时量。

只允许一步类类型转换

编译器只会自动地执行一步类型转换。例如,因为下面的代码隐式地使用了两种转换规则,所以它是错误的:

//错误:需要用户定义的两种转换:
//(1)把“9-999-99999-9“转换成string
//(2)再把这个(临时的)string转换成Sales_data
item.combine("9-999-99999-9");

如果我们想完成上述调用,可以显式地把字符串转换成string或者Sales_data对象:

//正确:显式地转换成string,隐式地转换成Sales_data
tem.combine(string("9-999-99999~9"));
//正确:隐式地转换成string,显式地转换成Sales_data
tem.combine(Sales_data("9~-999~99999~9"));

类类型转换不是总有效

是否需要从string到Sales_data的转换依赖于我们对用户使用该转换的看法。在此例中,这种转换可能是对的。null_book中的string可能表示了一个不存在的ISBN编号。

//另一个是从istream到Sales_data的转换:
// 使用 istream构造函数创建一个函数传递给combine
item.combine(cin);

这段代码隐式地把cin转换成Sales_data,这个转换执行了接受一个istream的Sales_data构造函数.该构造函数通过读取标准输入创建了一个(临时的)Sales_data对象,随后将得到的对象传递给combine。

Sales_data对象是个临时量,一旦combine完成我们就不能再访问它了。实际上,我们构建了一个对象,先将它的值加到item中,随后将其丢弃。

抑制构造函数定义的隐式转换

在要求隐式转换的程序上下文中,我们可以通过将构造函数声明为explicit加以阻止:

class Sales_data{
public:
Sales_data()=default;
Sales_data(conststd::stringg&S,unsigned n,double p):
bookNo(s),units_sold(n),revenue(P*n){}
explicit Sales_data(conststd::string&s):bookNo(s){}
explicit Sales_data(std::istream&);
//其他成员与之前的版本一致

此时,没有任何构造函数能用于隐式地创建Sales_data对象,之前的两种用法都无法通过编译:

item.combine(null_book);//错误:string构造函数是explicit的
item.combine(cin);//错误:istream构造函数是explicit的

关键字explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用执行隐式转换,所以无须将这些构造函数指定为exp1icit的。只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复:

//错误;explicit关键守只允许出现在类内的构造函数声明处
explicit Sales_data::Sales_data(istream& is)
{
read(ts,*this);
}

explicit构造函数只能用于直接初始化

发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时,我们只能使用直接初始化而不能使用exp1icit构造函数:

Sales_data item1(nul1_book);//正确;直接初始化
//错误:不能将explicit构造函数用于指贝形式的初始化过程
Sales_data item2=null_book;

当我们用explicit关键字声明构造函数时,它将只能以直接初始化的形式使用(参见3.2.1节,第76页)。而且,编译器将不会在自动转换过程中使用该构造函数。

为转换显式地使用构造函数

尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数显式地强制进行转换:

//正确:实参是一卜显式构造的Salesdata对象
item.combine(Sales_data(null_book));
//正确:stattc_cast可以使用explicit的构造函数
item.combine(static_cast<Sales_data>(cin));

在第一个调用中,我们直接使用sales_data的构造函数,该调用通过接受string的构造函数创建了一个临时的Sales_data对象。在第二个调用中,我们使用static_cast执行了显式的而非隐式的转换。其中,static_cast使用istream构造丽数创建了一个临时的Sales_data对象。

标准库中含有显式构造函数的类

我们用过的一些标准库中的类含有单参数的构造函数:

  • 接受一个单参数的const char*的string构造函数不是explicit的。
  • 接受一个容量参数的vector构造函数是explicit的。

聚合类

聚合类(aggregate class)使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。当一个类满足如下条件时,我们说它是聚合的:

  • 所有成员都是public的。
  • 没有定义任何构造函数。
  • 没有类内初始值。
  • 没有基类,也没有virtual函数。

例如,下面的类是一个聚合类:

struct Data{
int ival;
string s;
}

我们可以提供一个花括号括起来的成员初始值列表,并用它初始化棣合类的数据成员:

```cpp
//vall.ival=0;val1.s=string(“Rnnan)
Data val1 = {0,"Rnnan"};

初始值的顺序必须与声明的顺序一致,也就是说,第一个成员的初始值要放在第一个,然后是第二个,以此类推。下面的例子是错误的:

//错误:不能使用“Rnna“初始化ival,也不能使用1024初始化s
Data val2={"Rnna",1024}

与初始化数组元素的规则一样,如果初始值列表中的元素个数少于类的成员数量,则靠后的成员被值初始化。初始值列表的元素个数绝对不能超过类的成员数量。值得注意的是,显式地初始化类的对象的成员存在三个明显的缺点:

  • 要求类的所有成员都是public的。
  • 将正确初始化每个对象的每个成员的重任交给了类的用户(而非类的作者)。因为用户很容易忘掉某个初始值,或者提供一个不恰当的初始值,所以这样的初始化过程冗长乏味且容易出错。
  • 添加或删除一个成员之后,所有的初始化语句都需要更新。

字面值常量类

中我们提到过constexpr函数的参数和返回值必须是宇面值类型。除了算术类型、引用和指针外,桅些类也是字面值类型。和其他类不同,字面值类型的类可能含有constexpr函数成员。这样的成员必须符合constexpr函数的所有要求,它们是隐式const的。

数据成员都是字面值类型的聚合类是字面值常量类。如果一个类不是聚合类,但它符合下述要求,则它也是一个字面值常量类:

  • 数据成员都必须是字面值类型。
  • 类必须至少含有一个constexpr构造函数。
  • 如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于桅种类类型,则初始值必须使用成员自己的constexpr构造函数。
  • 类必须使用析构函数的默认定义,该成员负责销毁类的对象。

constexpr构造函数

尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数。事实上,一个字面值常量类必须至少提供一个constexpr构造函数。

constexpr构造函数可以声明成=default的形式。否则,constexpr构造函就必须即符合构造函数的要求(意味着不能包含返回语句),又符合constexpr有的唯一可执行语句就是返回语句。综合这两点可知,constexpr构造函数体一般来说应该是空的。我们通过前置关键字constexpr就可以声明一个constexpr构造函数了:

class Debug{
public:
constexpr Debug(bool b = true):hw(b),io(b),other(b){}
constexpr Debug(boolh,bool,boolo): hw(h),io(i),other(o){}
constexpr bool any(){return hw || io || other;}
void set_io(bool b){ io = b;}
void set_hw(bool b){ hw = b;}
void set_other(bool b){ hw = b;}

private:
bool hw;//硬件错误,而非IO错误
bool io;//IO错误
bool other;//其他错误
}

constexpr构造函数必须初始化所有数据成员,初始值或者使用constexpr构造函数,或者是一条常量表达式。
constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型:
constexpr Debug io_sub(false,true,false);     //调试IO
if(io_sub.any())                              //等价于if(true)
    cerr<<"print appropriate error messages"<<endl;
constexpr Debug prod(false);                  //无调试
if(prod.any())                                //等价于if(false)
    cerr << "print an error message"<<endl;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL 注入攻击详解[基础篇]:Web 应用程序安全漏洞与防御策略

目录 SQL注入的简介 现代 Web 应用程序中的数据库交互与 SQL 注入攻击 数据库管理系统&#xff08;DBMS&#xff09;架构与 SQL 注入 什么是 SQL 注入&#xff1f; SQL 注入的工作原理 SQL 注入的用例与影响 如何预防 SQL 注入&#xff1f; 数据库分类 数据库类型&am…

自制AirTag,支持安卓/鸿蒙/PC/Home Assistant,无需拥有iPhone

苹果的AirTag很贵&#xff0c;虽然某强北有平价代替品&#xff0c;但是仍需要苹果设备才能绑定&#xff0c;才能查看位置。不支持安卓/鸿蒙/PC&#xff0c;也不支持集成到Home Assistant中。 AirTag 的原理 每个AirTag都会发送一个蓝牙信号&#xff0c;其可以被临近的苹果设备…

网络技术变迁:从IPv4走向IPv6

目录 前言 旧时代产物&#xff1a;IPv4 什么是IPv4&#xff1f; IPv4的工作方式 IPv4的缺点 为什么要从IPv4过渡到IPv6&#xff1f; 走向IPv6&#xff1a;新一代互联网协议 IPv6的技术特性 我们需要过渡技术 双栈&#xff08;Dual Stack&#xff09; 隧道技术&#…

uniapp 滚动尺

scale组件代码&#xff08;部分class样式使用到了uview1.0的样式&#xff09; <template><view><view class"scale"><view class"pointer u-flex-col u-col-center"><u-icon name"arrow-down-fill" size"26&qu…

模型量化初始知识

原文网址&#xff1a;知乎原文-量化基础知识 背景 PyTorch对量化的支持目前有如下三种方式&#xff1a; Post Training Dynamic Quantization&#xff0c;模型训练完毕后的动态量化&#xff1b; Post Training Static Quantization&#xff0c;模型训练完毕后的静态量化&…

在项目中调用本地Deepseek(接入本地Deepseek)

前言 之前发表的文章已经讲了如何本地部署Deepseek模型&#xff0c;并且如何给Deepseek模型投喂数据、搭建本地知识库&#xff0c;但大部分人不知道怎么应用&#xff0c;让自己的项目接入AI模型。 文末有彩蛋哦&#xff01;&#xff01;&#xff01; 要接入本地部署的deepsee…

Redis7——基础篇(五)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 基础篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09;Redis&#x…

【爬虫基础】第一部分 网络通讯 P1/3

前言 1.知识点碎片化&#xff1a;每个网站实现的技术相似但是有区别&#xff0c;要求我们根据不同的网站使用不同的应对手段。主要是常用的一些网站爬取技术。 2.学习难度&#xff1a;入门比web简单&#xff0c;但后期难度要比web难&#xff0c;在于爬虫工程师与网站开发及运维…

揭秘区块链隐私黑科技:零知识证明如何改变未来

文章目录 1. 引言&#xff1a;什么是零知识证明&#xff1f;2. 零知识证明的核心概念与三大属性2.1 完备性&#xff08;Completeness&#xff09;2.2 可靠性&#xff08;Soundness&#xff09;2.3 零知识性&#xff08;Zero-Knowledge&#xff09; 3. 零知识证明的工作原理4. 零…

R软件用潜在类别混合模型LCM分析老年人抑郁数据轨迹多变量建模研究

全文链接&#xff1a; tecdat.cn/?p40283 潜在类别混合模型假设总体具有异质性&#xff0c;由 GG 个潜在类别组成。在多变量的情况下&#xff0c;潜在类别是根据 KK 个纵向结果来定义的&#xff0c;从而形成 GG 个组&#xff0c;每个组的特征由 KK 个轨迹均值轮廓集表示&#…

【Rust中级教程】1.11. 生命周期(进阶) Pt.1:回顾、借用检查器、泛型生命周期

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 这篇文章在Rust初级教程的基础上对生命周期这一概念进行了补充&#xff0c;建议先看【Rust自…

【DeepSeek服务器部署全攻略】Linux服务器部署DeepSeek R1模型、实现API调用、搭建Web页面以及专属知识库

DeepSeek R1模型的Linux服务器搭建、API访问及Web页面搭建 1&#xff0c;引言2&#xff0c;安装Ollama工具3&#xff0c;下载DeepSeek R1 模型4&#xff0c;DeepSeek命令行对话5&#xff0c;DeepSeek API接口远程调用6&#xff0c;DeepSeek结合Web-ui实现图形化界面远程访问6.1…

【免费软件分享】Typor1.9.5-x64-CN免费版

到处找pojie软件的朋友&#xff0c;这里给大家提供一个版本&#xff0c;之前也是废了老大的劲才找到&#xff0c;这里分享给大家&#xff0c;希望帮助到需要的朋友&#xff01; Typor1.9.5-x64-CN&#xff1a; 我用夸克网盘分享了「Typor1.9.5-x64-CN.7z」&#xff0c;点击链接…

Python天梯赛刷题-五分题(上)

蓝桥杯题刷的好累&#xff0c;感觉零帧起手、以题带学真的会很吃力&#xff0c;打算重新刷一点天梯的题目巩固一下&#xff0c;我本人在算法非常不精通的情况下&#xff0c;自认为天梯的L1的题是会相对容易一些的&#xff0c;可能有一些没有脑子光靠力气的“硬推”hhhh。 从头…

Python编程之数据分组

有哪些方式可以进行数据分组利用Pandas库进行分组使用itertools库的groupby分组操作构建Python字典方式实现(小规模数据,不适用数量特别大的情况,不需要依赖其它python库)利用NumPy的groupby函数分组操作利用Python的Dask库提供的函数进行分组下面看一个如何去实现坐标数据…

激光雷达YDLIDAR X2 SDK安装

激光雷达YDLIDAR X2 SDK安装 陈拓 2024/12/15-2024/12/19 1. 简介 YDLIDAR X2官方网址https://ydlidar.cn/index.html‌YDLIDAR X2 YDLIDAR X2是一款高性能的激光雷达传感器&#xff0c;具有以下主要特点和规格参数‌&#xff1a; ‌测距频率‌&#xff1a;3000Hz ‌扫描频…

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2) 我们上次已经了解了Paimon的下载及安装&#xff0c;并且了解了主键表的引擎以及changelog-producer的含义 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 今天&#xff0c;我们继续快速了解下最近比…

⭐ Unity 横向滑动列表 首尾相连 轮转图

效果如下&#xff1a; 场景挂载&#xff1a; 代码部分&#xff1a; using DG.Tweening; using System; using System.Collections; using System.Collections.Generic; using System.Drawing.Printing; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine…

大白话实战Sentinel

Sentinel是SpringCloudAlibaba提供的用来做服务保护的框架,而服务保护的常见手段就是限流和熔断降级。在大型分布式系统里面,由于微服务众多,所以服务之间的稳定性需要做特别关注,Sentinel的核心包就提供了从多个维度去保护服务稳定的策略,而且这些保护策略都可以连接上Se…

【C语言】C语言 哈夫曼编码传输(源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 C语言 哈夫曼编码传输&#xff08;源码数据文件&am…