《吃透 C++ 类和对象(中):拷贝构造函数与赋值运算符重载深度解析》

🔥个人主页:@草莓熊Lotso

🎬作者简介:C++研发方向学习者

📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。

前言: 在上篇博客中我们学习了构造函数和析构函数这两个类中的默认成员函数,今天这篇博客我想继续为大家分享拷贝构造函数和赋值运算符重载。主要是先介绍特点再通过举例说明,所以举例中的代码注释是很重要的。


目录

一.拷贝构造函数

拷贝构造的特点:

举例说明:(注意看注释) 

二.赋值运算符重载

运算符重载:

举例说明:(注意注释)

赋值运算符重载:

赋值运算符重载的特点:

举例说明:(注意注释) 


一.拷贝构造函数

如果一个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。

拷贝构造的特点:

  1. 拷贝构造函数是构造函数的⼀个重载。
  2. 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归用。 拷贝构造函数也可以多个参数,但是第一个参数必须是类类型对象引用,后面的参数必须有缺省值。
  3. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成
  4. 若未显式定义拷贝构造,编译器会自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝)(这里和构造与析构不一样,它是会对内置类型有特定处理的),对自定义类型成员变量会调用他的拷贝构造。
  5. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的拷贝构造会调用Stack的拷贝构造,也不需要我们显示实现MyQueue的拷贝构造。这里还有一个小技巧,如果⼀个类显示实现了析构并释放资源(也可以说必须显示实现析构),那么他就需要显示写拷贝构造,否则就不需要。
  6. 传值返回会产生⼀个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使用引用返回是有问题的,这时的引用相当于⼀个野引用,类似⼀个野指针⼀样。传引用返回可以减少拷贝,但是⼀定要确保返回对象,在当前函数结束后还在,才能用引用返回。

--上述特点在举例说明中都会体现出来 

关于无穷递归图示:

举例说明:(注意看注释) 

#include<iostream>
using namespace std;class Date 
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数//Date(Date d),这种写法不行,我们可以拿个func的例子看看,会发现传值要先调用拷贝函数,下面有提到// 但是拷贝函数本身再一直调用拷贝函数本身的话,会出现无限递归的问题,所以要使用传引用传参//加个const可以让下面传实参的选择更多,避免出现权限扩大等问题Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//其实这里用默认生成的也会处理内置类型,进行浅拷贝,在这里是没问题的。这也是和构造和析构的一个区别//用指针实现拷贝,但是这里并不算拷贝函数/*Date(Date* d){_year = d->_year;_month = d->_month;_day = d->_day;}*/void Print(){cout << _year << "/" << _month << "/" << _day << '\n';}
private:int _year;int _month;int _day;
};//自定义类型传值传参和传值返回都会调用拷贝构造完成
//传值传参会调用拷贝函数
void func(Date d)
{}//传值返回也会调用拷贝函数,但是传引用就不会(后续会用Stack继续来讲),这里就不过多介绍了
//Date& func2()
Date func2()//这里最好用传值返回
{Date d(2025,7,31);return d;
}int main()
{Date d1(2025,8,1); Date d2(d1);//func(d1);//我们调试发现,他会先去调用Date里面的拷贝函数再去func//传值返回可以,传引用返回不行,这里不细讲Date ret = func2();//Date ret = (func2());,这样写也是可以的//由这个我们还可以看出,那其实之前的拷贝也可以写出这样//Date d2 = d1;// 这里可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造//Date d2(&d1);d1.Print();d2.Print();return 0;
}

关于对内置类型处理和深浅拷贝的相关示例: (注意注释,用的栈)

#include<iostream>
using namespace std;
typedef int STDataType;class Stack 
{
public:Stack(int n=4){_a = (int*)malloc(n * sizeof(int));if (_a == nullptr){perror("malloc fail!");exit(1);}_top = 0;_capacity = n;}//这样写是错的,因为这里的数组如果像这样写仅仅是浅拷贝,后续析构函数释放空间会释放同一块空间// 画图理解,调试也会报错/*Stack(const Stack& s){_a = s._a;_capacity = s._capacity;_top = s._top;}*///所以我们需要这样写Stack(const Stack& s){_a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);//申请一块同样大小的空间if (_a == nullptr){perror("malloc fail!");exit(1);}//把值再拷贝过去memcpy(_a, s._a, s._top * sizeof(STDataType));//这两个直接这样就可以了_capacity = s._capacity;_top = s._top;}//补充一点,这里也不能不写,用编译器自动生成的默认的拷贝构造函数,因为这个函数虽然会处理内置类型//但是只会是浅拷贝/值拷贝,像Stack这样需要有深拷贝的就不行了//可以这样说,如果一个类必须显示实现析构函数(需要释放资源),那么他就一定也要显示写拷贝构造函数void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");exit(1);}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout<<"~Stack()"<<'\n';if (_a){free(_a);_a = nullptr;}_top = 0;_capacity = 0;}
private://内置类型STDataType* _a;int _top;int _capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}

图示如下:

理解技巧:可以这样说,如果一个类必须显示实现析构函数(需要释放资源),那么他就一定也要显示写拷贝构造函数

借助上面的栈的类(这里就不再写出来了),给大家对比看看传引用返回在这里的弊端,同时也是在说第6个特点 :(注意注释)

int& func1()
{int x = 1;return x;
}//自定义类型传值返回是会调用拷贝函数的,但是传引用返回不会,画图分析。
//它没调拷贝函数的话,在后面函数栈帧销毁,st析构掉了之后。你再通过别名来找,就出问题了,画图
//Stack func2()
Stack& func2()
{Stack st;return st;
}int main()
{int ret1 = func1();cout << ret1 << '\n';//可能是1也可能是随机值,我们之前判断过//但是这个栈就很明显了,我们调试看看Stack ret2 = func2();//这里其实也是拷贝return 0;
}

 --会报realloc fail,但是上面的结构需要改一下(int改size_t),不然不会报这个错误

  再来看看如果是默认生成的拷贝构造函数对自定义类型的处理:(注意注释)

#include<iostream>
using namespace std;
typedef int STDataType;class Stack
{
public:Stack(int n = 4){_a = (int*)malloc(n * sizeof(int));if (_a == nullptr){perror("malloc fail!");exit(1);}_top = 0;_capacity = n;}Stack(const Stack& s){_a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);//申请一块同样大小的空间if (_a == nullptr){perror("malloc fail!");exit(1);}//把值再拷贝过去memcpy(_a, s._a, s._top * sizeof(STDataType));//这两个直接这样就可以了_capacity = s._capacity;_top = s._top;}//补充一点,这里也不能不写,用编译器自动生成的默认的拷贝构造函数,因为这个函数虽然会处理内置类型//但是只会是浅拷贝/值拷贝,像Stack这样需要有深拷贝的就不行了//可以这样说,如果一个类必须显示实现析构函数(需要释放资源),那么他就一点也要显示写拷贝构造函数void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");exit(1);}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout << "~Stack()" << '\n';if (_a){free(_a);_a = nullptr;}_top = 0;_capacity = 0;}
private://内置类型STDataType* _a;int _top;int _capacity;
};class MyQueue
{
public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化//编译器默认生成MyQueue的拷贝构造函数调用了Stack的拷贝构造函数,完成了拷贝//编译器默认生成MyQueue的析构函数调用了Stack的析构函数,释放的Stack内部的资源
private://自定义类型Stack _pushst;Stack _popst;//内置类型,但很奇怪,混在这里它却能处理,这里大家可以自己去试试//int size = 0;
};
int main()
{Stack st1;st1.Push(1);st1.Push(2);// Stack如果不显示实现拷⻉构造,用自动生成的拷⻉构造完成浅拷⻉// 会导致st1和st2里面的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃Stack st2 = st1;MyQueue mq1;// MyQueue自动生成的拷⻉构造,会自动调用Stack拷⻉构造完成pushst/popst的拷⻉。// 只要Stack拷⻉构造自己实现了深拷⻉,这里就没问题MyQueue mq2 = mq1;return 0;
}


二.赋值运算符重载

--在正式学习赋值运算符重载之前我们需要先了解一下运算符重载

运算符重载:

  • 当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
  • 运算符重载是具有特殊名字的函数,他的名字是由operator和后面要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
  • 重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。一元运算符有⼀个参数,二元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第二个参数。
  • 如果一个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个。
  • 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
  • 不能通过连接语法中没有的符号来创建新的操作符:比如operator@。
  • .* (点*),:: ,sizeof ,?:  ,. (点)注意以上5个运算符不能重载。(选择题里面常考,大家要记一下)
  • 重载操作符至少有一个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int operator+(int x, int y)
  • 一个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,但是重载operator+(当日期加日期时)就没有意义(加天数还是可以的)。
  • 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。
  • 重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。

--上述特点在举例说明中大多都会提到,其中最后三个特点会在后续的博客中讲解 

举例说明:(注意注释)

#include<iostream>
using namespace std;class Date 
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//其实这里用默认生成的也会处理内置类型,进行浅拷贝,在这里是没问题的。//这也是和构造和析构的一个区别,构造和析构不会处理内置类型int Getyear(){return _year;}//d1==d2,传d2就行,d1有this指针,但是在实参和形参不能直接写出来,函数体内可以bool operator==(const Date& d2){return this->_year == d2._year&& this->_month == d2._month&& this->_day == d2._day;}void Print(){cout << _year << "/" << _month << "/" << _day << '\n';}
private:int _year;int _month;int _day;
};// 重载为全局的面临对象访问私有成员变量的问题
// 有几种方法可以解决:
// 1、成员放公有--这个最容易,但是不那么好
// 2、Date提供getxxx函数--上面有在类里面展现出来可以自己看看,然后在底下的函数体内需要修改一下
// 3、友元函数--这里先不讲这个
// 4、重载为成员函数--这个我也在类里重载为成员函数了,但是有些需要注意的地方,我最后选取这种
//bool operator==(const Date& d1, const Date& d2)
//{
//	return d1._year == d2._year//如果用了Get**就是这样写的:d1.Getyear() == d2.Getyear()
//		&& d1._month == d2._month
//		&& d1._day == d2._day;
//}int main()
{Date d1(2025,8,1);Date d2(2025,10,1);Date d3(2025, 8, 1);//我们在这里就需要实现运算符重载函数d1 == d2;//运算符重载函数可以显示调用//operator==(d1, d2);//如果成成员函数了,显示调用是这样的//d1.operator==(d2);//只要传一个参d2就行,d1通过this指针,但是不能在实参和形参显示写出来的//再加上运算符重载要求参数个数和运算符作用对象一样多,所以只能传一个//可以具体去看看上面类里面怎么实现的cout << (d1 == d2) << '\n';//这里需要打括号,优先级的问题,0cout << (d1 == d3) << '\n';//1return 0;
}

--0表示不相等,1表示相等 

给大家大概看一下 .* 这个符号:(注意注释)

// .*符号普及,了解即可,刚好提到了这个运算符不能重载
#include<iostream>
using namespace std;void func1()
{cout << "void func()" << endl;
}class A
{
public:void func2(){cout << "A::func()" << endl;}
};int main()
{// 普通函数指针void(*pf1)() = func1;(*pf1)();// A类型成员函数的指针void(A::*pf2)() = &A::func2;A aa;(aa.*pf2)();//这里就是使用的.*return 0;
}

赋值运算符重载:

赋值运算符重载是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个对象拷贝初始化给另一个要创建的对象。

赋值运算符重载的特点:

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引用,否则会传值传参会有拷贝(const还可以有效防止权限扩大,让能传的参选择更多)
  2. 有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值的场景。
  3. 没有显式实现时,编译器会自动生成⼀个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的赋值重载函数。
  4. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显⽰实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载,也不需要我们显示实现MyQueue的赋值运算符重载。这里还有⼀个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要(跟拷贝构造函数类似)。

--上述特点大部分会在后续的举例说明中解释

举例说明:(注意注释) 

//赋值运算符重载
#include<iostream>
using namespace std;class Date 
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//传引用返回可以减少拷贝(之前提到过在这里传值返回是自动调用拷贝函数的)//这里能使用是传引用返回是因为第一个参数用this来的,函数栈帧销毁也不会找不到//函数要返回类型是为例更好处理连续赋值的情况(d3=d1=d2),用void不好处理Date& operator=(const Date& d)//const和传引用传参的作用就不再多说了{//自己等于自己就可以不用赋值了if (this != &d){_year = d._year;_month = d._month;_day = d._day;}//比如:d1=d2表达式的返回对象应该为d1,也就是*thisreturn *this;}//赋值运算符重载,但其实在Date类型里面不写也没影响,跟拷贝构造函数处理内置类型原理一样//思考联想方法也一样,不再说了void Print(){cout << _year << "/" << _month << "/" << _day << '\n';}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(2025, 8, 1);d1 = d2;Date d3;d3 = d1 = d2;//从右往左d1.Print();d2.Print();d3.Print();// 需要注意这里是拷⻉构造,不是赋值重载// 要牢牢记住赋值重载完成两个已经存在的对象直接的拷⻉赋值// 而拷⻉构造用于一个对象拷⻉初始化给另⼀个要创建的对象Date d4 = d1;//因为拷贝构造如果写出这样就有点容易混//Date d4(d1);//写成这样的时候不太容易混淆return 0;
}

完整源代码: 

cpp-exclusive-warehouse: 【CPP知识学习仓库】 - Gitee.com


往期回顾:

《吃透 C++ 类和对象(上):封装、实例化与 this 指针详解》

《吃透 C++ 类和对象(中):构造函数与析构函数的核心逻辑》

结语:本篇博客就到此结束了,在学完类和对象的这些知识后,虽然还没学完,但博主后续会先更新实现一个完整的日期类的博客,这个还是有点难度的。大家可以看完之后自己去试一下,检验一下自己的学习成果,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。

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

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

相关文章

Python 环境隔离实战:venv、virtualenv 与 conda 的差异与最佳实践

那天把项目部署到测试环境&#xff0c;结果依赖冲突把服务拉崩了——本地能跑&#xff0c;线上不能跑。折腾半天才发现&#xff1a;我和同事用的不是同一套 site-packages&#xff0c;版本差异导致运行时异常。那一刻我彻底明白&#xff1a;虚拟环境不是可选项&#xff0c;它是…

[ 数据结构 ] 时间和空间复杂度

1.算法效率算法效率分析分为两种 : ①时间效率, ②空间效率 时间效率即为 时间复杂度 , 时间复杂度主要衡量一个算法的运行速度空间效率即为 空间复杂度 , 空间复杂度主要衡量一个算法所需要的额外空间2.时间复杂度2.1 时间复杂度的概念定义 : 再计算机科学中 , 算法的时间复杂…

一,设计模式-单例模式

目的设计单例模式的目的是为了解决两个问题&#xff1a;保证一个类只有一个实例这种需求是需要控制某些资源的共享权限&#xff0c;比如文件资源、数据库资源。为该实例提供一个全局访问节点相较于通过全局变量保存重要的共享对象&#xff0c;通过一个封装的类对象&#xff0c;…

AIStarter修复macOS 15兼容问题:跨平台AI项目管理新体验

AIStarter是全网唯一支持Windows、Mac和Linux的AI管理平台&#xff0c;为开发者提供便捷的AI项目管理体验。近期&#xff0c;熊哥在视频中分享了针对macOS 15系统无法打开AIStarter的修复方案&#xff0c;最新版已完美兼容。本文基于视频内容&#xff0c;详解修复细节与使用技巧…

LabVIEW 纺织检测数据传递

基于 LabVIEW 实现纺织检测系统中上位机&#xff08;PC 机&#xff09;与下位机&#xff08;单片机&#xff09;的串口数据传递&#xff0c;成功应用于煮茧机温度测量系统。通过采用特定硬件架构与软件设计&#xff0c;实现了温度数据的高效采集、传输与分析&#xff0c;操作简…

ECCV-2018《Variational Wasserstein Clustering》

核心思想 该论文提出了一个基于最优传输(optimal transportation) 理论的新型聚类方法&#xff0c;称为变分Wasserstein聚类(Variational Wasserstein Clustering, VWC)。其核心思想有三点&#xff1a;建立最优传输与k-means聚类的联系&#xff1a;作者指出k-means聚类问题本质…

部署 Docker 应用详解(MySQL + Tomcat + Nginx + Redis)

文章目录一、MySQL二、Tomcat三、Nginx四、Redis一、MySQL 搜索 MySQL 镜像下载 MySQL 镜像创建 MySQL 容器 docker run -i -t/d -p 3307:3306 --namec_mysql -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 m…

VR全景导览在大型活动中的应用实践:优化观众体验与现场管理

大型演出赛事往往吸引海量观众&#xff0c;但复杂的场馆环境常带来诸多困扰&#xff1a;如何快速找到座位看台区域&#xff1f;停车位如何规划&#xff1f;附近公交地铁站在哪&#xff1f;这些痛点直接影响观众体验与现场秩序。VR全景技术为解决这些问题提供了有效方案。通过在…

OpenJDK 17 JIT编译器堆栈分析

##堆栈(gdb) bt #0 PhaseOutput::safepoint_poll_table (this0x7fffd0bfb950) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/output.hpp:173 #1 0x00007ffff689634e in PhaseOutput::fill_buffer (this0x7fffd0bfb950, cb0x7fffd0bfb970, blk_starts0x7fffb0…

功能测试中常见的面试题-二

二、测试设计与用例编写题解释等价类划分 (Equivalence Partitioning) 和边界值分析 (Boundary Value Analysis)&#xff1f;并举例说明。等价类划分 (EP)&#xff1a; 将输入域划分为若干组&#xff08;等价类&#xff09;&#xff0c;假设同一组内的数据对揭露程序错误具有等…

SOLi-LABS Page-4 (Challenges)--54-65关

sql-54 翻译一下页面&#xff0c;得知我们只有十次机会。id参数是单引号闭合。 ?id-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase()-- 我得到的表名是igsyiz2p7z。&#xff08;每个人得到的应该都不一样&#…

docker代码如何在vscod上修改

基于 docker-compose.yml文件&#xff08;包含 ​​emqx​​&#xff08;MQTT服务&#xff09;、​​backend​​&#xff08;后端服务&#xff09;、​​mysql​​&#xff08;数据库&#xff09;&#xff09;的详细运行、调试、增改删操作说明&#xff0c;结合流程图示意&…

HTML5 CSS3 从入门到精通:构建现代Web的艺术与科学

本文将带你系统地学习掌握现代Web前端的基础与核心&#xff0c;最终能够独立构建语义清晰、布局灵活、交互丰富的专业级网站。 第一章&#xff1a;夯实基础 - HTML5语义化与结构艺术 1.1 告别<div>混沌&#xff1a;语义化标签的力量 <header><h1>网站标题…

C# 微软依赖注入 (Microsoft.Extensions.DependencyInjection) 详解

文章目录 前言 核心原理 三大生命周期 核心接口与类 基础使用示例 关键特性详解 1、构造函数注入 2、作用域管理 3、服务解析方法 4、延迟加载 常见问题解决 问题1:循环依赖 问题2:多实现选择 性能优化技巧 扩展方法示例 前言 微软的依赖注入框架是 .NET Core/5+ 的核心组件…

【车联网kafka】Kafka核心架构与实战经验(第四篇)

一、社团扛把子不为人知的秘密 香港社团里&#xff0c;Kafka 是整个组织的名号&#xff0c;ZooKeeper 就是说一不二的长老团&#xff0c;各个片区的 “话事人” 就是 broker&#xff0c;而能统领所有片区的 “扛把子”&#xff0c;就是 Kafka 里的控制器。​ 1.1 选举的秘密 每…

Scala重点(基础、面向对象、高阶函数、集合、模式匹配)

1. 基础语法1.1. 注释和java一样我是单行注释 /* 我是多行注释 我是多行注释 */ /** * 我是文档注释 * 我是文档注释 */1.2. 语句语句可以不以分号结尾一条语句独占一行 println("Hello World!")多条语句在一行 println("Hello World!"); println("He…

明远智睿T113-i核心板:工业设备制造领域的革新利器

在工业设备制造这片充满挑战与机遇的领域&#xff0c;技术革新如同一股汹涌浪潮&#xff0c;不断重塑着市场竞争的格局。随着技术持续进步&#xff0c;市场竞争愈发激烈&#xff0c;制造商们面临着如何在保证产品卓越性能的同时&#xff0c;有效控制成本这一关键难题。在此背景…

122-基于Flask的校园霸凌数据可视化分析系统

校园霸凌数据可视化分析系统 - 基于Flask的全栈数据分析平台 本文详细介绍了一个基于Flask框架开发的校园霸凌数据可视化分析系统&#xff0c;从技术架构到功能实现&#xff0c;为数据分析项目开发提供参考。 &#x1f4cb; 目录 项目概述技术架构核心功能代码结构技术栈详解核…

Docker 网络设置方式详解

Docker 网络是容器通信的核心基础&#xff0c;它允许容器之间、容器与主机之间以及容器与外部网络之间进行数据交互。Docker 提供了多种网络驱动类型&#xff0c;适用于不同场景&#xff0c;下面详细介绍 Docker 网络的设置方式。一、Docker 网络的基本概念 Docker 网络通过驱动…

export default和export function的作用及export的含义

在 JavaScript 中&#xff0c;export 是一个关键字&#xff0c;用于将模块中的变量、函数、类等导出&#xff0c;以便其他模块可以导入和使用。export default 和 export&#xff08;非默认导出&#xff09;是两种不同的导出方式&#xff0c;它们在使用场景和语义上有明显的区别…