【C++初阶】之类和对象(中)
【C++初阶】之类和对象(中)
- ✍ 类的六个默认成员函数
- ✍ 构造函数
- 🏄 为什么需要构造函数
- 🏄 默认构造函数
- 🏄 为什么编译器能自动调用默认构造函数
- 🏄 自己写的构造函数
- 🏄 构造函数的特性
- ✍ 拷贝构造函数
- 🏄 编译器默认生成的拷贝构造函数
- 🏄 自己写的拷贝构造函数
- 🏄 拷贝构造函数调用的场景
- ✍ 赋值运算符重载(也叫拷贝赋值函数)
- 🏄 运算符重载的引入
- 💘 前置++和后置++重载
- 💘 运算符重载函数的调用
- 🏄 赋值运算符重载
- 💘 编译器默认生成的赋值运算符重载函数
- 💘 自己显示写的赋值运算符重载函数
- ✍ 析构函数
- 🏄 编译器默认生成的析构函数
- 🏄 显式写的析构函数
- 🏄 析构函数的特性
- 🏄 没有深拷贝,导致二次释放同一空间问题
- 💘 问题的引入---拷贝构造函数
- 💘 问题的解决---深拷贝
- 💘 赋值运算符重载函数的浅拷贝问题
- ✍ const成员函数
- 🏄 const对象访问的规则
- 🏄 非const对象访问的规则
- ✍ 对普通对象的取地址运算符重载和对const对象取地址运算符重载
- ✍ C++默认构造函数提供的机制
- 🏄 C++默认构造函数是否提供的情况

📃博客主页: 小镇敲码人
💞热门专栏:C++初阶
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞
✍ 类的六个默认成员函数
当类为空是编译器也不是什么都不生成,而是会生成六大默认成员函数。
我们也可以自己显式把这六个默认成员写出来,这样编译器就会调用我们自己的,而不会调用默认生成的。
✍ 构造函数
🏄 为什么需要构造函数
我们学习C语言的时候,初始化栈操作需要自己写一个
Init
函数,但是这样就很麻烦,因为初始化栈之后需要我们显示的去调用Init
函数,否则就有可能出现野指针的情况,因为如果是链式的栈,要把next
指针初始化为空。
🏄 默认构造函数
我们构造函数就是为了解决这样的问题,在初始化类的时候,
你不需要显示的调用Init函数,编译器会自动的去调用,如果你不去显示的写,
编译器会生成一个默认的构造函数。我们来验证一下。
class Date
{
private:int year;int day;int month;
};
int main()
{Date x;return 0;
}
此时我们写了一个Date类,编译器会给调用它的默认构造函数吗?运行结果:
怎么回事呢?x对象的值没有被初始化呀,那是不是代表编译器没有调用默认构造函数呢?其实不然,C++把类型分为自定义类型和内置类型,默认构造函数要做的是,自定义类型去调用它自己的构造函数(如果有的话),内置类型去给一个随机值,那到底是不是这样呢?我们也可以来验证一下。
class year
{
public:year(){std::cout << "year()" << std::endl;}
};
class Date
{
private:year y;int day;int month;
};
int main()
{Date x;return 0;
}
注意:那个自定义类型的构造函数必须是public的,否则在它自己的类外面就访问不了。
运行结果:
默认构造函数默认成员函数是两个不同的概念,两者不能混淆,不用我们传参数,全缺省构造函数和无参数构造函数、默认生成的构造函数都称作为默认构造函数。
class Date
{
public:Date(){day = 0;month = 0;}Date(int day = 0,int month = 0){}
private:year y;int day;int month;
};
int main()
{Date x;return 0;
}
注意:上面那两个默认构造函数不能同时存在,因为都不需要传参数,会造成歧义,编译器不知道调用哪一个默认构造函数。
🏄 为什么编译器能自动调用默认构造函数
那为什么编译器能在实例化类对象的的时候自动调用它的构造函数呢?
可以认为这是编译器做了特殊的处理,它帮助我们调用了这个函数。我们转到反汇编,可以发现编译器帮助我们调用了。
🏄 自己写的构造函数
我们也可以自己显示的写构造函数,那样编译器就不会去调用默认生成的构造函数了。
class year
{
public:year(){std::cout << "year()" << std::endl;}
};
class Date
{
public:Date(){day = 0;month = 0;}
private:year y;int day;int month;
};
int main()
{Date x;return 0;
}
运行结果:
可以看到,编译器在我们自己写的构造函数进去前,仍然会先去调用自定义类型的构造函数。
🏄 构造函数的特性
1、一次实例化对象只会调用一次,不支持显示调用。
2、构造函数会在实例化对象的时候自动调用,只用于初始化对象的一些成员变量,是初始化对象,而不是给对象开空间。
3、函数名和类名相同,无返回值。
4、支持重载。
✍ 拷贝构造函数
拷贝构造函数是构造函数的一种,主要作用是实现用一个已经存在的类对象,去初始化创建另外一个类对象。
🏄 编译器默认生成的拷贝构造函数
和前面普通的构造函数一样,如果我们不写编译器就会默认生成。
class year
{
public:year(){std::cout << "year()" << std::endl;}
};
class Date
{
public:Date(){day++;month++;}void f(){}
private:year y;int day = 0;int month = 0;
};
int main()
{Date x;Date y(x);return 0;
}
运行结果:
那我们为什么要写呢?这样岂不是浪费时间多次一举吗,编译器都帮助我们写好了,我们有时候的确是不需要写的,比如在没有向堆申请空间的时候,这时不涉及资源的清理,浅拷贝不会出问题,但是一旦我们向堆上申请空间后,不自己写深拷贝的拷贝构造函数,就会造成二次释放相同空间的问题。
🏄 自己写的拷贝构造函数
Date(const Date& x)
{day = x.day;month = x.month;y = x.y;
}
这里加上const是因为我们只是用x去初始化,但不希望改变它的值,至于这里为什么要使用引用,而且必须使用引用否则就会引发无穷递归:
这是因为我们在传参的时候,实参和形参的关系是,形参是实参的拷贝(当两者类型一样时),这不就相当于使用实参去初始化形参吗(也就是一个类去初始化另外一个类),也要调用拷贝构造函数,下一次又是一样的情况,所以会造成无穷递归,但是加了引用,你这个形参就是我实参的别名,不用再去调用拷贝构造,也就不会出现这种问题。
🏄 拷贝构造函数调用的场景
刚刚我们其实已经说了两个场景了。
1、用一个创建好的类初始化另外一个没有初始化的类
2、函数传参(参数为自定义类型)
3、函数返回值(参数为自定义类型),2和3都不能带引用,否则就不会调用拷贝构造函数。
4、赋值运算符重载时,被赋值的类还没有创建。
✍ 赋值运算符重载(也叫拷贝赋值函数)
我们的内置类型可以支持,一个变量赋值给另外一个对象,比如:a = b(都是
int
类型),那我们类(自定义类型)支持吗,答案是肯定的,使用运算符重载就可以解决这个问题。
🏄 运算符重载的引入
在C++中,增加了运算符的重载,这是因为有时候自定义类型也需要做一些类似操作符的操作,引入运算符重载,极大的提升了代码的可读性,它的规则如下:
- 函数名为
operator
后面接需要重载的运算符,注意:不能重载一些莫名奇妙的符号像@。- 函数原型:返回值类型 operator操作符(参数列表)
注意:运算符重载时必须要有一个自定义类型的参数,因为运算符重载就是为类而生的,如果你没有类参数,那就没有意义了。
编译器为了防止你乱搞,会报错的。上面是全局的运算符重载函数。
有时候有的运算符需要两个参数,但是我们在类里面设计的时候只有一个参数,实际上是有两个参数的,第一个参数传的是this指针,编译器给隐藏了
💘 前置++和后置++重载
我们来介绍一下两个特殊的运算符重载,前置++和后置++重载,这两个操作符名字都一样,该如何区分呢?
这里没有办法了,C++对其做了特殊的处理,即给后置++多传一个参数来去区分,并且++操作符重载,最多额外传一个int
参数作区分,也是为了防止用户乱搞。
我们来实现一下Date类的前置++和后置++:
// 前置++运算符
// 该运算符将对象的年份、月份和日期都递增1,并返回递增后的对象的引用
Date& operator++() // 前置++
{ year++; // 递增年份 day++; // 递增日期 month++; // 递增月份 return *this; // 返回当前对象的引用
} Date operator++(int) // 后置++
{ Date tmp(*this); // 创建当前对象的副本 ++(*this); // 递增当前对象(使用前置++) return tmp; // 返回递增前的对象的副本
}
这里实际上我们在++日期的时候要考虑月份和年份的变化,这里我们主要是学习语法就不考虑了。
注意后置++的返回值不能带引用。因为我们返回的是副本,但是副本是临时对象(出了作用域销毁了),所以我们需要返回一个副本的拷贝,而不是副本本身。
- 注意这里即使我们运算符重载函数写成全局的,也能像内置类型那样调用:
using namespace std;
class Date
{
public:Date(int year, int month = 2, int day = 1) ://普通的构造函数year_(year),month_(month),day_(day){cout << "Date(int year, int month = 2, int day = 1)" << endl;}Date(const Date& x) ://拷贝构造函数year_(x.year_),month_(x.month_),day_(x.day_){cout << "Date(const Date& x)" << endl;}Date& operator=(const Date& x)//拷贝赋值函数{year_ = x.year_;month_ = x.month_;day_ = x.day_;cout << "operator=(const Date& x)" << endl;return *this;}~Date()//析构函数{cout << "~Date" << endl;}
public:int year_;int month_;int day_;
};// 前置++运算符
// 该运算符将对象的年份、月份和日期都递增1,并返回递增后的对象的引用
Date& operator++(Date& x) // 前置++
{x.year_++; // 递增年份 x.day_++; // 递增日期 x.month_++; // 递增月份 return x; // 返回当前对象的引用
}Date operator++(Date&x,int) // 后置++
{Date tmp(x); // 创建当前对象的副本 ++x; // 递增当前对象(使用前置++) return tmp; // 返回递增前的对象的副本
}int main()
{Date x(2022);x++;++x;
}
运行结果:
代码正常运行。
如果我们给++运算符重载函数增加其它类型的参数,编译器就会报错:
💘 运算符重载函数的调用
内置类型可以直接
a = b,或者a++
,那我们的自定义类型是否可以这样了,为了可读性和方便,我们的C++支持这样来调用运算符重载函数,我们拿刚刚的前置++、和后置++函数来演示。
int main()
{Date x;x++;//-->operator++(&x,1);++x;//-->operator++(&x);return 0;
}
我们转到反汇编可以发现,确实是调用了对应的函数。
也可以显示调用,注意这里编译器已经帮助我们传了this指针过去,所以这里我们显示调用的是后置++:
🏄 赋值运算符重载
回归正题,我们继续来看我们的赋值运算符重载函数。
💘 编译器默认生成的赋值运算符重载函数
当我们不去显示的写赋值运算符重载函数,编译器会默认生成一个。
但是当我们这样去写,被赋值的y还没有被创建这个时候编译器就会去调用拷贝构造函数,无论你有没有自己显式的写:
💘 自己显示写的赋值运算符重载函数
下面我们来自己显示的写一下,还是会有深拷贝的问题,当我们类的成员变量有在堆上申请空间时,直接赋值会引发二次析构的问题。
// 赋值运算符重载函数
// 将参数x的值赋给当前对象,并返回当前对象的引用
Date& operator=(const Date& x)
{ if (this != &x) // 检查自赋值 { day = x.day; month = x.month; year = x.year; } return *this;
}
现代写法:
这种写法在拷贝构造函数处理好深拷贝问题后,可以很好的实现深拷贝,因为我们这种写法本质是对拷贝构造函数的一个复用。
Date& operator=(const Date& x)
{ // 检查自赋值,避免不必要的操作 if (this != &x) { // 创建一个临时Date对象tmp,并使用参数x来初始化它 Date tmp(x); // 使用std::swap来交换tmp对象的day成员和当前对象的day成员 std::swap(tmp.day, this->day); // 使用std::swap来交换tmp对象的year成员和当前对象的year成员 std::swap(tmp.year, this->year); // 使用std::swap来交换tmp对象的month成员和当前对象的month成员 std::swap(tmp.month, this->month); // 通过上述交换,实际上是将tmp对象(即x的副本)的内容赋给了当前对象 } // 返回当前对象的引用,以支持链式赋值操作 return *this;
}
运行结果:
✍ 析构函数
有初始化资源的函数,就会有清理资源的函数。析构函数和构造函数一样,它是在当前作用域结束后就会自动调用析构函数。它的函数名字不一样,类名前面多了一个
~
。
🏄 编译器默认生成的析构函数
一般情况下编译器也会默认生成一个析构函数,当我们的成员变量都没有申请资源时就不需要显示的写析构函数。
🏄 显式写的析构函数
~Date(){year = 0;day = 0;month = 0;}
🏄 析构函数的特性
1、当前函数作用域结束后自动调用
2、无参数,无返回值
3、函数名是~+类名。
4、功能是清理对象中的资源而不是释放对象的空间。
5、支持显示调用,构造函数不支持。
🏄 没有深拷贝,导致二次释放同一空间问题
💘 问题的引入—拷贝构造函数
前面在讲拷贝构造函数和赋值构造函数,我们就对这个问题做了铺垫,这个问题的本质就和标题一样,内存重复释放,为什么会这样,本质还是万恶的值拷贝!下面我们写一段代码来解释并解决这个问题。
class Stack
{
public:Stack(){_capacity = 4;//假设开始的时候给容量设置为4_top = 0;_a = (int*)malloc(sizeof(int) * _capacity);if (_a == nullptr){std::cout << "malloc Failed" << std::endl;exit(-1);}}~Stack(){std::cout << " ~Stack" << std::endl;_capacity = 0;_top = 0;free(_a);_a = nullptr;}
private:int* _a;int _top; // 栈顶int _capacity; // 容量
};int main()
{Stack st1;Stack st2(st1);return 0;
}
运行结果:
是的,程序在这里崩溃了,我们来调试一下。
可以看到st1中的_a、和st2中的_a保存的地址值是一模一样,释放了两次相同空间的地址。
💘 问题的解决—深拷贝
那么我们如何规避这种情况呢,就要用到深拷贝,我们可以开和st1中_a指向空间一样大小的数组,并把_a指向空间的值赋值给我们的数组。
代码实现:
// Stack类的拷贝构造函数
// 接收一个对Stack类型的常量引用st作为参数,用于复制对象
Stack(const Stack& st)
{ // 为栈的底层数组_a分配内存,大小与源栈st的容量相同 _a = (int*)malloc(sizeof(int) * (st._capacity)); // 检查内存是否分配成功 if (_a == nullptr) { // 如果分配失败,则输出错误信息并退出程序 std::cout << "内存分配失败" << std::endl; exit(-1); } // 使用memcpy函数将源栈st的底层数组内容复制到当前栈的底层数组_a中 memcpy(_a, st._a, sizeof(int) * (st._capacity)); // 复制源栈st的栈顶指针_top到当前栈 _top = st._top; // 复制源栈st的容量_capacity到当前栈 _capacity = st._capacity;
}
运行结果:
此时不再报错。
调试结果:
保存的地址不同(指向的空间不同),但是数组中的值相同,完成了拷贝构造(深拷贝)。
💘 赋值运算符重载函数的浅拷贝问题
使用编译器默认的值拷贝,去赋值,在刚刚的场景也会报错。
int main()
{Stack st1;Stack st2;st2 = st1;return 0;
}
我们可以使用赋值运算符重载函数现代写法来复用刚刚拷贝构造函数写好的深拷贝。
// Stack类的赋值运算符重载
// 接收一个对Stack类型的常量引用st作为参数,用于赋值操作
Stack& operator=(const Stack& st)
{ // 检查是否自赋值,即当前对象与参数对象是否为同一个对象 if (this != &st) { free(_a);//释放之前_a的内存// 创建一个临时Stack对象tmp,并用参数对象st初始化 Stack tmp(st); // 使用std::swap交换临时对象tmp的底层数组与当前对象的底层数组 std::swap(tmp._a, _a); // 使用std::swap交换临时对象tmp的栈顶指针与当前对象的栈顶指针 std::swap(tmp._top, _top); // 使用std::swap交换临时对象tmp的容量与当前对象的容量 std::swap(tmp._capacity, _capacity); } // 返回当前对象的引用,支持链式赋值操作 return *this;
}
运行结果:
调试结果:
与预期一致。
✍ const成员函数
使用
const
关键字修饰的函数(放在函数括号右边)叫做const成员函数,const实际是修饰的this
指针指向的内容,所以其指向的内容不能修改。
请看下面的代码:
class Date
{
public:Date(){year = 2024;month = 1;day = 1;}void f() const{this->year = 1;}
private:int year;int month;int day;
};int main()
{Date x;
}
🏄 const对象访问的规则
1、const对象不能访问非const类型的函数,但是可以构造函数和析构函数例外。这很好理解,因为我们的const
修饰对象,对象的内容不能被修改,如果你能调用非const函数,在这个函数里修改了我们的成员变量,那不就逻辑不自洽了嘛。
const对象访问构造函数。
也不能对析构或者构造函数使用const修饰。
const对象访问析构函数。
const对象访问非const函数是非法的。
2、const成员函数类也不能调用其它的非const的成员函数。
本质上是一样的问题const Date* const this类型不能转变为Date* const this,否则它的能力就扩大了。
- 注意前面的const修饰的是
*this
,表示this指向的内容具有常性,后面的const
修饰this指针,表示this指针的值不能被修改。
🏄 非const对象访问的规则
3、但是非const成员函数内可以调用const成员函数。
程序正常退出。
4、非const对象也可以调用const函数。
3和4总结起来也是一样的,Date* const this
类型可以向const Date* const this
类型转化。
✍ 对普通对象的取地址运算符重载和对const对象取地址运算符重载
这两个函数一般都不需要我们显示的去写,编译器会默认生成的。
我们也可以显示的写出来。
// 非const成员函数的取地址运算符重载
// 返回当前对象的地址
Date* operator&()
{ return this;
}// const成员函数的取地址运算符重载
// 返回当前对象的const地址
const Date* operator&() const
{ return this;
}
但是通常只有我们有一些特殊的需求比如取出某个特定成员变量的地址时,才需要自己显示的去写。
✍ C++默认构造函数提供的机制
部分内容参考博主这篇博客C++默认构造函数提供的机制。
我们都知道,在C++98中,有着这样的几种构造函数:普通构造函数、析构函数、拷贝构造函数、赋值运算符重载函数。
生成这些特殊的成员函数(或者不生成)的规则比较复杂,编译器默认生成的构造函数是有可能被删除的。
🏄 C++默认构造函数是否提供的情况
- 如果自定义了普通构造函数和拷贝构造函数,系统将不再提供默认的无参构造函数。
但是如果定义了一个赋值运算符重载函数,系统还是会提供普通的无参构造函数。
2、如果自定义了一个普通的构造函数,系统还会提供一个拷贝构造函数和赋值运算符重载函数(值拷贝)。
- 如果自定义了一个拷贝构造函数,系统将不再提供默认的拷贝构造函数。但是会生成默认的赋值运算符重载函数。
4、如果自定义了一个赋值运算符重载函数,系统就不会默认生成赋值运算符重载函数了,但是其它函数还是会生成。
这里少打字了,应该是无参的构造函数。
没有生成报错是这样的:
-
如果自定义了一个析构函数,系统也就不会再提供默认的析构函数。
-
如果类里面有没有初始化的非静态const数据成员或者引用类型的数据成员,会导致默认提供的默认构造函数被删除。
当我们使用初始化列表初始化好这两个变量好,发现去调用拷贝构造函数是可以的(编译器默认生成了),但是拷贝赋值函数却被删除了。
6.用户如果自己没有提供一个拷贝构造函数或者拷贝赋值函数,编译器会隐式声明一个。
相关文章:

【C++初阶】之类和对象(中)
【C初阶】之类和对象(中) ✍ 类的六个默认成员函数✍ 构造函数🏄 为什么需要构造函数🏄 默认构造函数🏄 为什么编译器能自动调用默认构造函数🏄 自己写的构造函数🏄 构造函数的特性 ✍ 拷贝构造…...

Vue2(十一):脚手架配置代理、github案例、插槽
一、脚手架配置代理 1.回顾常用的ajax发送方式: (1)xhr 比较麻烦,不常用 (2)jQuery 核心是封装dom操作,所以也不常用 (3)axios 优势:体积小、是promis…...

在宝塔面板中,为自己的云服务器安装SSL证书,为所搭建的网站启用https(主要部分攻略)
前提条件 My HTTP website is running Nginx on Debian 10(或者11) 时间:2024-3-28 16:25:52 你的网站部署在Debain 10(或者11)的 Nginx上 安装单域名证书(默认)(非泛域名…...

学习JavaEE的日子 Day32 线程池
Day32 线程池 1.引入 一个线程完成一项任务所需时间为: 创建线程时间 - Time1线程中执行任务的时间 - Time2销毁线程时间 - Time3 2.为什么需要线程池(重要) 线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time…...
@Transactional 注解使用的注意事项
事务管理 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好的事务管理机制,主要分为编程式事务和声明式事务两种。 编程式事务: 是指在代码中手动的管理事务的提交、回滚等操作,代码侵入比较强。 声明式事务ÿ…...

电商系列之库存
> 插:AI时代,程序员或多或少要了解些人工智能,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家…...

Apache HBase(二)
目录 一、Apache HBase 1、HBase Shell操作 1.1、DDL创建修改表格 1、创建命名空间和表格 2、查看表格 3、修改表 4、删除表 1.2、DML写入读取数据 1、写入数据 2、读取数据 3、删除数据 2、大数据软件启动 一、Apache HBase 1、HBase Shell操作 先启动HBase。再…...
【设计模式】原型模式详解
概述 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象 结构 抽象原型类:规定了具体原型对象必须实现的clone()方法具体原型类:实现抽象原型类的clone()方法,它是可以被复制的对象。访问类&…...

企微侧边栏开发(内部应用内嵌H5)
一、背景 公司的业务需要用企业微信和客户进行沟通,而客户的个人信息基本都存储在内部CRM系统中,对于销售来说需要一边看企微,一边去内部CRM系统查询,比较麻烦,希望能在企微增加一个侧边栏展示客户的详细信息…...
如何确定最优的石油管道位置
如何确定最优的石油管道位置 一、前言二、问题概述三、理解问题的几何性质四、转化为数学问题五、寻找最优解六、算法设计6.1伪代码6.2 C代码七算法效率和实际应用7.1时间效率分析7.2 空间效率分析结论一、前言 当我们面对建设大型输油管道的复杂任务时,确保效率和成本效益是…...

FPGA 图像边缘检测(Canny算子)
1 顶层代码 timescale 1ns / 1ps //边缘检测二阶微分算子:canny算子module image_canny_edge_detect (input clk,input reset, //复位高电平有效input [10:0] img_width,input [ 9:0] img_height,input [ 7:0] low_threshold,input [ 7:0] high_threshold,input va…...

2024.3.28学习笔记
今日学习韩顺平java0200_韩顺平Java_对象机制练习_哔哩哔哩_bilibili 今日学习p286-p294 继承 继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些…...

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查
33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查 关系数据库 关系对象数据库(ORM) 应用偏好数据库 分布式数据库 关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。HarmonyOS关系型…...

寄主机显示器被快递搞坏了怎么办?怎么破?
大家好,我是平泽裕也。 最近,我在社区里看到很多关于开学后弟弟寄来的电脑显示器被快递损坏的帖子。 看到它真的让我感到难过。 如果有人的数码产品被快递损坏了,我会伤心很久。 那么今天就跟大家聊聊寄快递的一些小技巧。 作为一名曾经的…...
python爬虫-bs4
python爬虫-bs4 目录 python爬虫-bs4说明安装导入 基础用法解析对象获取文本Tag对象获取HTML中的标签内容find参数获取标签属性获取所有标签获取标签名嵌套获取子节点和父节点 说明 BeautifulSoup 是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数…...

SpringBoot学习之ElasticSearch下载安装和启动(Mac版)(三十一)
本篇是接上一篇Windows版本,需要Windows版本的请看上一篇,这里我们继续把Elasticsearch简称为ES,以下都是这样。 一、下载 登录Elasticsearch官网,地址是:Download Elasticsearch | Elastic 进入以后,网页会自动识别系统给你提示Mac版本的下载链接按钮 二、安装 下载…...

OC对象 - Block解决循环引用
文章目录 OC对象 - Block解决循环引用前言1. 循环引用示例1.1 分析 2. 解决思路3. ARC下3.1 __weak3.2 __unsafe_unretained3.3 __block 4. MRC下4.1 __unsafe_unretain....4.1 __block 5. 总结5.1 ARC下5.2 MRC下 OC对象 - Block解决循环引用 前言 本章将会通过一个循环引用…...

Java设计模式之装饰器模式
装饰器模式是一种结构型设计模式,它允许动态地将责任附加到对象上。装饰器模式是通过创建一个包装对象,也就是装饰器,来包裹真实对象,从而实现对真实对象的功能增强。装饰器模式可以在不修改原有对象的情况下,动态地添…...
Java基础知识总结(25)
代理模式 什么是代理模式? 代理模式是指,为其他对象提供一种代理以控制这个对象的访问。一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户和目标对象之间起到中介的作用。换句话说,代理模式,是在不修…...
Vue3 实现基于token 用户登录
前后端分离情况下,实现的大致思路 1 第一次登录的时候,前端调用后端的登录接口,发送用户名与密码 2 后端收到请求,验证用户名和密码,验证成功 给前端返回一个token 3 前段拿到token 将token 存储进localStorage 和…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...