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

【C++】类和对象(下)

目录

前言

一、再探构造函数

二、类型转换 

三、static 成员 

四、友元

五、内部类

六、匿名对象

七、对象拷贝时的编译器优化

总结



前言

        本文主要内容:构造函数的再探--初始化列表、内置类型与自定义类型之间的转换、类的static成员、友元、内部类、匿名对象。最后还会了解一下编译器对类对象拷贝时的优化。


一、再探构造函数

  • 1. 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
  • 2. 注意:每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。

初始化列表:

#include <iostream>
using namespace std;class Date
{
public://初始化列表Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 8, 20);d1.Print();return 0;
}

运行结果:

相当于我们之前在函数体中进行初始化成员变量,现在可以直接在初始化列表中进行初始化成员变量。注意一个成员变量在初始化列表中只能出现一次


  • 3. 必须放在初始化列表位置进行初始化的3类成员变量:
  1. 引用成员变量
  2. const成员变量
  3. 没有默认构造的类类型(自定义类型)成员变量

我们首先理解第三点,什么是没有默认构造的成员变量?

答:我们回顾上一篇文章,默认构造有三种:无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的无参的构造函数。总结一下就是不传实参就可以调用的构造就叫默认构造。自定义类型的成员变量的构造不是默认构造就叫没有默认构造的成员变量

我们观察以下代码:(栈没有默认构造)

#include <iostream>
using namespace std;typedef int STDataType;
class Stack
{
public://需要传参,非默认构造函数Stack(int n){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (_a == nullptr){perror("malloc fail!");exit(1);}_capacity = n;_top = 0;}~Stack(){free(_a);_a = nullptr;_capacity = _top = 0;}private:STDataType* _a;size_t _capacity;size_t _top;
};class MyQueue
{
public://编译器自动生成的无参默认构造会自动调用 Stack 类的构造初始化成员变量//假如 Stack 没有默认构造
private:Stack _pushst;Stack _popst;
};

我们之前写的 Stack 构造函数是有缺省参数的,因此在 MyQueue 类中,我们不用显示的去写构造函数,编译器自动生成的就会去调用Stack的默认构造。但是现在,Stack 类的构造不是默认构造,我们创建一个 MyQueue 类对象就会报错:

这是因为不是默认构造创建对象时就需要传参,而编译器自动为 MyQueue 生成的默认构造是不支持传参的,因此为了 MyQueue 能够正常实例化对象,我们需要用到初始化列表:

解决方法:

class MyQueue
{
public://编译器自动生成的无参默认构造会自动调用 Stack 类的构造初始化成员变量//假如 Stack 没有默认构造MyQueue():_pushst(4),_popst(4){}private:Stack _pushst;Stack _popst;
};

当然也可以写成全缺省的默认构造:

class MyQueue
{
public://编译器自动生成的无参默认构造会自动调用 Stack 类的构造初始化成员变量//假如 Stack 没有默认构造MyQueue(int n = 4):_pushst(n),_popst(n){}private:Stack _pushst;Stack _popst;
};int main()
{MyQueue q1;MyQueue q2(100);return 0;
}

以上就是为什么没有默认构造的类类型成员变量必须走初始化列表的原因。

接下来就是 引用类型成员变量 和 const成员变量:

报错信息:

正确写法:

对于普通对象 _a 来说,在初始化列表或者函数体中初始化都可以。


小结:加深理解为什么这三种成员变量必须走初始化列表:

  1. &引用类型成员变量:我们知道引用在定义时必须初始化的,不然就是类似野指针的野引用
  2. const类型成员变量:const 类型的变量,只有一次修改的机会,就是在第一次赋值时。
  3. 没有默认构造的自定义类型成员变量:没有默认构造的自定义类型成员变量,那么初始化时必须传参

因为这三种有这些特殊需求,因此必须走初始化列表


  • 4. 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
  • 5. C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。

思维导图:

演示:

class Date
{
public:Date(int year = 2, int month = 2, int day = 2):_year(year), _month(month){}private://注意这里不是初始化,这里给的是缺省值,这个缺省值是给初始化列表的//如果初始化列表没有显示初始化,默认就会用这个缺省值初始化int _year = 1;int _month = 1;int _day = 1;// const 类型成员变量也可以在声明的时候给。const int n = 1;
};

 监视窗口:

 

没有默认构造的自定义类型成员变量也可以使用缺省值初始化: 

class Stack
{
public:Stack(int n):_a((STDataType*)malloc(sizeof(STDataType)*n)),_capacity(n),_top(0){}~Stack(){free(_a);_a = nullptr;_capacity = _top = 0;}private:STDataType* _a;size_t _capacity;size_t _top;
};class MyQueue
{
public:private://哪怕 Stack 没有默认构造也可以在定义时给缺省值//这和显示的去写初始化列表效果一样//当然如果显示的写了初始化列表,优先调用初始化列表初始化Stack _pushst = 4;Stack _pophst = 4;
};


  • 6. 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。

例如:下面程序的运行结果是什么()

A.输出1 1

B.输出2 2

C.编译报错

D.输出1 随机值

E.输出1 2

F.输出2 1

#include <iostream>
using namespace std;class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2 = 2;int _a1 = 2;
};int main()
{A aa(1);aa.Print();
}

答案:D

解析:

  1. 首先如开头所说:初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关
  2. 因为显示的写了_a1和_a2的初始化列表,因此_a1和_a2的缺省值用不上。
  3. 而在初始化列表中,_a2(_a1)先执行,此时_a1为随机值,因此_a2被赋值为随机值,再执行_a1(a),_a1被赋值为1。
  4. 打印结果:


初始化列表总结:

  1. 无论是否显示写初始化列表,每个构造函数都有初始化列表;
  2. 无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;


二、类型转换 

  • C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
  • 构造函数前面加 explicit 就不再支持隐式类型转换。
  • 类类型的对象之间也可以隐式转换,需要相应的构造函数支持。

演示:

#include <iostream>
using namespace std;class A
{
public://普通构造A(int a1):_a1(a1){cout << "A(int a1)" << endl;}//构造重载A(int a1, int a2):_a1(a1),_a2(a2){cout << "A(int a1, int a2)" << endl;}//拷贝构造A(const A& a):_a1(a._a1), _a2(a._a2){cout << "A(const A& a)" << endl;}void Print() const{cout << _a1 << " " << _a2 << endl;}private:int _a1 = -1;int _a2 = -1;
};int main()
{//1构造⼀个A的临时对象,再用这个临时对象拷贝构造aa1//编译器遇到连续构造+拷贝构造->优化为直接构造A aa1 = 1;aa1.Print();return 0;
}

运行结果:

解释:

  • 上述中,将 1 赋值给 aa1 进行构造的过程就涉及隐式类型转换,理论上是首先将 1 构造成一个 A 类型的临时对象,aa1 再通过这个临时对象调用拷贝构造进行构造。
  • 但是实际过程中,编译器进行了优化,直接将 1 构造A类对象并给到 aa1,省去了拷贝构造环节。
  • 注意:1 调用的是单参数的构造,因此1被赋值到了成员变量_a1中。

这其中 1 构造过程中的临时对象是存在的,并且也具有常性,对其引用需要加 const 修饰。以下r引用的是1的一个临时对象。内置类型直接的转换一样。

以上是单参数隐式类型转换,其实C++还支持多参数隐式类型转换,只要有支持的构造函数即可,多参数转换使用大括号{},如:

如果不想让该类能够实现类型转化,可以在构造函数前加上 explicit 关键字就行了:

#include <iostream>
using namespace std;class A
{
public://普通构造explicit A(int a1):_a1(a1){cout << "A(int a1)" << endl;}//构造重载explicit A(int a1, int a2):_a1(a1),_a2(a2){cout << "A(int a1, int a2)" << endl;}//拷贝构造A(const A& a):_a1(a._a1), _a2(a._a2){cout << "A(const A& a)" << endl;}void Print() const{cout << _a1 << " " << _a2 << endl;}private:int _a1 = -1;int _a2 = -1;
};

这时就不支持内置类型转化为该类类型了:

不同类类型直接也可以相互转化,只要提供了对应的构造函数即可:

#include <iostream>
using namespace std;class A
{
public://普通构造A(int a1):_a1(a1){cout << "A(int a1)" << endl;}//构造重载A(int a1, int a2):_a1(a1),_a2(a2){cout << "A(int a1, int a2)" << endl;}//拷贝构造A(const A& a):_a1(a._a1), _a2(a._a2){cout << "A(const A& a)" << endl;}void Print() const{cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}private:int _a1 = -1;int _a2 = -1;
};class B
{
public://提高对A类的构造B(const A& a):_b(a.Get()){cout << "B(const A& a)" << endl;}private:int _b = 0;
};int main()
{A aa1 = { 1,1 };B bb1 = aa1;return 0;
}

运行结果:


类型转换应用场景:像Stack入栈A类对象就方便很多

#include <iostream>
using namespace std;class A
{
public://普通构造A(int a1):_a1(a1){cout << "A(int a1)" << endl;}//构造重载A(int a1, int a2):_a1(a1),_a2(a2){cout << "A(int a1, int a2)" << endl;}//拷贝构造A(const A& a):_a1(a._a1), _a2(a._a2){cout << "A(const A& a)" << endl;}void Print() const{cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}private:int _a1 = -1;int _a2 = -1;
};class Stack
{
public:Stack(int n = 4):_a((A*)malloc(sizeof(A)* n)), _capacity(n),_top(0){}void Push(const A& a){_a[_top++] = a;}~Stack(){free(_a);_a = nullptr;_capacity = _top = 0;}private:A* _a;size_t _capacity;size_t _top;
};int main()
{Stack st;//Push的参数加上 const 的原因就是传过去的都是临时对象的引用//像这样入栈是不是很方便st.Push({ 1,1 });st.Push({ 2,2 });st.Push({ 3,3 });return 0;
}

监视窗口:


三、static 成员 

1.static成员变量

  • 用 static 修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。
  • 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
  • 静态成员也是类的成员,受public、protected、private访问限定符的限制。
  • 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是给构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。

例:统计该类实例化了多少个对象

#include <iostream>
using namespace std;class A
{
public:A(){++_scount;}A(const A& a){++_scount;}int GetAcount(){return _scount;}private://类里面声明static int _scount;
};//类外面初始化
int A::_scount = 0;void Func(A aa)
{}int main()
{A aa1;A aa2;Func(aa1);cout << aa1.GetAcount() << endl;return 0;
}

运行结果:

如果想统计现存的该类实例化对象个数,就在析构中 --_scount:

#include <iostream>
using namespace std;class A
{
public:A(){++_scount;}A(const A& a){++_scount;}~A(){--_scount;}int GetAcount(){return _scount;}private://类里面声明static int _scount;
};//类外面初始化
int A::_scount = 0;void Func(A aa)
{}int main()
{A aa1;A aa2;Func(aa1);cout << aa1.GetAcount() << endl;return 0;
}

运行结果:


2.static成员函数 

  • 用 static 修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
  • 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
  • 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
  • 突破类域就可以访问静态成员,可以通过类名::静态成员或者 对象.静态成员 来访问静态成员变量和静态成员函数。

演示:

#include <iostream>
using namespace std;class A
{
public:A(){++_scount;}A(const A& a){++_scount;}~A(){--_scount;}//静态成员函数static int GetAcount(){return _scount;}//类里面声明static int _scount;
};//类外面初始化
int A::_scount = 0;void Func(A aa)
{}//突破类域就可以访问静态成员函数或变量
void Fxx()
{cout << A::GetAcount() << endl;cout << A::_scount << endl;
}int main()
{A aa1;A aa2;Func(aa1);Fxx();return 0;
}

运行结果:


3.两道练习题

1. 题目链接:求1+2+3+...+n_牛客题霸_牛客网

解析:

  1. 题目要求不能使用乘除法,因此等差数列的公式不能用,接着不能使用循环和分支结构,因此迭代和递归都不能使用。
  2. 解决方法就是刚刚所学的静态成员变量:
  3. 使用构造函数搭配静态成员变量解决。
class Sum {public:Sum() {//每创建一个Sum类对象,就让_ret+=_i一次//随后++_i即可_ret += _i;++_i;}//定义两个静态成员变量static int _ret;static int _i;
};
//初始化为0和1
int Sum::_ret = 0;
int Sum::_i = 1;class Solution {public:int Sum_Solution(int n) {//变长数组一次创建n个Sum类对象Sum arr[n];return Sum::_ret;}
};

2.选择题:

设已经有A,B,C,D 4个类的定义,程序中A,B,C,D构造函数调用顺序为?()

设已经有A,B,C,D 4个类的定义,程序中A,B,C,D析构函数调用顺序为?()

A :D B A C 

B :B A D C

C :C D B A

D :A B D C

E :C A B D

F :C D A B

#include <iostream>
using namespace std;class A
{
public:A(){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
};
class B
{
public:B(){cout << "B()" << endl;}~B(){cout << "~B()" << endl;}
};
class C
{
public:C(){cout << "C()" << endl;}~C(){cout << "~C()" << endl;}
};
class D
{
public:D(){cout << "D()" << endl;}~D(){cout << "~D()" << endl;}
};C c;
int main()
{A a;B b;static D d;return 0;
}

答案:B、E

运行结果:

解释:

  1. 对于构造顺序,全局对象先构造,因为在主函数之前需要确保对象的可用性,主函数中的局部对象 a,b 就按照先后顺序构造,而静态对象d即使是储存在静态区,也是要在定义处才进行构造。
  2. 析构是后定义先析构,而c、d都是储存在静态区的,生命周期比a、b长,则a和b先按照后定义的先析构顺序析构,对于c、d,因为d是局部的静态对象,因此先析构,全局的对象c则最后析构。


四、友元

  • 友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加 friend,并且把友元声明放到一个类的里面。
  • 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
  • 一个函数可以是多个类的友元函数。

例:上一篇中实现 <<和>>的运算符重载就运用了友元

#include <iostream>
using namespace std;class Date
{
public:Date(int year = 1970, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//友元声明friend ostream& operator<<(ostream& cout, const Date& d);friend istream& operator>>(istream& in, Date& d);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{cout << "请输入年月日:>";in >> d._year >> d._month >> d._day;return in;
}int main()
{Date d1, d2;cin >> d1 >> d2;cout << d1 << d2 << endl;return 0;
}
  • 友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
  • 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
  • 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。

友元类:

#include <iostream>
using namespace std;class A
{
public://友元声明(放在A类任意位置都行)friend class B;private:int _a1 = 1;int _a2 = 2;
};class B
{
public:void Func1(const A& a){cout << a._a1 << endl;cout << _b1 << endl;}void Func2(const A& a){cout << a._a2 << endl;cout << _b2 << endl;}private:int _b1 = 3;int _b2 = 4;
};int main()
{B b1;A a1;b1.Func1(a1);b1.Func2(a1);return 0;
}

运行结果:

友元使用注意:

  • 友元有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。


五、内部类

  • 1. 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。

这一点可以通过sizeof计算验证:

#include <iostream>
using namespace std;class A
{
private:static int _k;int _h = 1;public://内部类class B //B默认就是A的友元{public:void foo(const A& a){cout << _k << endl; cout << a._h << endl;}};
};
int A::_k = 1;int main()
{//计算A类大小cout << sizeof(A) << endl;//创建B类对象受到A类域限制A::B b1;return 0;
}

运行结果:

解释:

  1. A类大小为4字节,因为 static 修饰的成员变量不在类对象中,在静态区,而内部类也不在外部类中,内部类实际和全局类一样,只不过受到外部类限制。
  2. 创建内部类对象需要外部类声明,如b1,同时也受到访问限定符限制。
  • 内部类默认是外部类的友元类。因此内部类可以访问外部类的私有成员或函数,但是外部类默认是不能访问内部类的私有成员或函数的,因为友元是单向的。
  • 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到 private/protected 位置,那么A类就是B类的专属内部类,其它地方都用不了。

如:外部类默认不能访问内部类私有成员


适用场景:

  • 内部类使用较少,但在前面一道题中使用内部类相对合理一些

题目链接:求1+2+3+...+n_牛客题霸_牛客网

class Solution {//内部类class Sum {public:Sum() {_ret += _i;++_i;}};static int _ret;static int _i;
public:int Sum_Solution(int n) {//变长数组Sum arr[n];return _ret;}
};
int Solution::_ret = 0;
int Solution::_i = 1;


六、匿名对象

  • 用类型 (实参) 定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名 (实参) 定义出来的叫有名对象
  • 匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。

演示:

#include <iostream>
using namespace std;class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}private:int _a;
};int main()
{//有名对象A aa1(1);A aa2;//匿名对象,其生命周期只有其当前一行A(1);A();//不传参也要有个括号return 0;
}

运行结果:

解释:

  1. 运行结果中,第一个析构就是匿名对象的析构,第二个析构就是第二个匿名对象的析构,因为它们两个生命周期只有一行。

匿名对象的使用:

1.作为参数调用类的成员函数:

class Solution {
public:int Sum_Solution(int n) {//...return n;}
};int main()
{//匿名对象调用类函数cout << Solution().Sum_Solution(1) << endl;return 0;
}

2.作为缺省值:

void Func(A a = A(0))
{//...
}

其余场景因为现在所学较少,不好列举


const 引用匿名对象注意:

int main()
{const A& ra = A(0);return 0;
}
  1. 首先匿名对象也具有常性,因此引用时需要 const 修饰
  2. 此时匿名对象的生命周期会因为引用而延迟,延迟的声明周期与引用一样。

总之,无论const引用的临时对象还是匿名对象,它们的生命周期都会因为const引用而延长,直至const引用结束。


七、对象拷贝时的编译器优化

  • 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少一些传参和传返回值的过程中可以省略的拷贝。
  • 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新更 "激进" 的编译器还会进行跨行跨表达式的合并优化。

简单来说:原本为了达到一个效果需要多条指令,经过编译器优化后,效果一样,但指令大大减少,效率提升。

如:

#include<iostream>
using namespace std;class A
{
public://默认构造A(int a = 0):_a1(a){cout << "A(int a)" << endl;}//拷贝构造A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}//运算符重载A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a1 = aa._a1;}return *this;}//析构~A(){cout << "~A()" << endl;}private:int _a1 = 1;
};A f()
{A aa;return aa;
}int main()
{A aa1 = f();cout << endl;return 0;
}

vs2022运行结果:

我们发现vs2022下,这段代码只调用了默认构造和析构,这就是编译器优化的结果,vs2022的优化还是非常激进的。

我们画图展示理论上这段代码无优化调用的完整流程:

(注:小的临时对象(4/8字节)会存在寄存器中,大的临时对象则是存在main函数的栈帧上的)

VS2019的优化:

(注:由于我没有安装2019版的vs因此这里没有展示2019版的vs运行结果)

直接说结果:在vs2019下,这段代码会调用aa的构造和aa1的拷贝构造和它们两个的析构,比vs2022多了一个拷贝构造和一个析构

我们可以想到vs2022的优化有多么夸张。

我们可以在 Linux 下可以看到无优化的运行结果:指令g++ test.cpp -fno-elide-constructors

我们将无优化结果一一对比原代码,验证临时对象的存在:

我们再看看 VS2022 的优化:

验证VS2022的优化:

验证很简单,只需要证明 f() 函数中的 aa 对象地址与 aa1的地址相同即可:

#include<iostream>
using namespace std;class A
{
public://默认构造A(int a = 0):_a1(a){cout << "A(int a)" << endl;}//拷贝构造A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}//运算符重载A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a1 = aa._a1;}return *this;}//析构~A(){cout << "~A()" << endl;}private:int _a1 = 1;
};A f()
{A aa;cout << &aa << endl;return aa;
}int main()
{A aa1 = f();cout << &aa1 << endl;return 0;
}

运行结果:

成功验证


总结

        以上就是本文的全部内容,感谢支持!

相关文章:

【C++】类和对象(下)

目录 前言 一、再探构造函数 二、类型转换 三、static 成员 四、友元 五、内部类 六、匿名对象 七、对象拷贝时的编译器优化 总结 前言 本文主要内容&#xff1a;构造函数的再探--初始化列表、内置类型与自定义类型之间的转换、类的static成员、友元、内部类、匿名对…...

vue多页面应用集成时权限处理问题

在多页面应用&#xff08;MPA&#xff09;中&#xff0c;权限管理通常会涉及到每个页面的访问控制、身份验证、以及权限校验。以下是几种常见的权限处理方式&#xff1a; 1. 前端路由权限控制 原理&#xff1a;虽然是多页面应用&#xff0c;通常每个页面会独立加载和渲染&…...

输出保留3位小数的浮点数

输出保留3位小数的浮点数 C语言代码C代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 读入一个单精度浮点数&#xff0c;保留3位小数输出这个浮点数。 输入 只有一行&#xff0c;一个单精度浮点数。 输出 也只有一…...

openssl的运用

一、概述 Opssl是一个用于TLS/SSL协议的工具包&#xff0c;也是一个通用密码库。 包含了国密sm2 sm3 sm4&#xff0c;包含了对称加密&#xff0c;非对称加密&#xff0c;单项散列&#xff0c;伪随机、签名&#xff0c;密码交换&#xff0c;证书等一些算法库。 为了深层次的学习…...

C++STL之vector(超详细)

CSTL之vector 1.vector基本介绍2.vector重要接口2.1.构造函数2.2.迭代器2.3.空间2.3.1.resize2.3.2.capacity 2.4.增删查找 3.迭代器失效4.迭代器分类 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f68…...

RabbitMQ消息可靠性保证机制5--消息幂等性处理

RabbitMQ层面有实现“去重机制”来保证“恰好一次”吗&#xff1f;答案是没并没有&#xff0c;而且现在主流的消息中间件都没有实现。 一般解决重复消息的办法是&#xff1a;在消费端让我们消费消息操作具有幂等性。 幂等性问题并不是消息系统独有&#xff0c;而是&#xff0…...

24/12/1 算法笔记<强化学习> 创建Maze交互

我们今天制作一个栅格的游戏。 我们直接上代码教学。 1.载入库和查找相应的函数版本 import numpy as np import time import sysif sys.version_info.major 2:import Tkinter as tk else:import tkinter as tk 2.设置长宽和单元格大小 UNIT 40 MAZE_H 4 MAZE_W 4 3.初始…...

c++:模版 template

一、模版 1.格式&#xff1a; template <typname T> 2.实现 2.1自动推导 模板只对紧跟在后面的第一行代码有效&#xff0c;如果后面还想定义模板函数需要重新定义模板 #include <iostream> #include <string>template <typename T> void Print(T v…...

javascript切换类、删除类、修改类以及增加类

在JavaScript中&#xff0c;操作DOM元素的类&#xff08;class&#xff09;是一个常见的操作。以下是一些基本的方法来切换类、删除类、修改类以及增加内联样式&#xff1a; 切换类&#xff08;Toggle Class&#xff09; 切换类意味着如果类存在则移除它&#xff0c;如果不存…...

区块链学习笔记(2)--区块链的交易模型part1

模型基础 区块链的tx分为两种模型&#xff0c;分别是比特币为代表的UTXO&#xff08;Unspent Transaction Output&#xff09;模型&#xff0c;和以太坊为代表的Account模型。前者适用于货币记账&#xff0c;后者适用于链上应用。 UTXO模型 类似于现金的交易模型 一个tx包含…...

反射知识总结

狂神说 反射的功能&#xff1a; 类加载内存分析 类加载的时候&#xff0c;class对象就形成了。 类无论有多少对象&#xff0c;class对象只有一个。 获取类对象三种方式 反射&#xff0c;就是通过api获取一个类的类对象&#xff1a; 有三种方式&#xff1a; 方法一&#xf…...

selenium部署分布式 UI 自动化测试环境-Docker

一、根据selenium/hub官网的配置信息&#xff0c;进行配置。 How to run this image The Hub and Nodes will be created in the same network and they will recognize each other by their container name. A Docker network⁠ needs to be created as a first step.Create …...

算法刷题Day5: BM52 数组中只出现一次的两个数字

描述&#xff1a; 一个整型数组里除了两个数字只出现一次&#xff0c;其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 要求&#xff1a;空间复杂度 O(1)&#xff0c;时间复杂度O(n)。 题目传送门 is here 思路&#xff1a; 方法一&#xff1a;最简单的思路就…...

使用docker-compose部署搜索引擎ElasticSearch6.8.10

背景 Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;基于 Apache Lucene 构建。它被广泛用于实时数据搜索、日志分析、全文检索等应用场景。 Elasticsearch 支持高效的全文搜索&#xff0c;并提供了强大的聚合功能&#xff0c;可以处理大规模的数据集并进行快速…...

多线程篇-5--线程分类(线程类型,springboot中常见线程类型,异步任务线程)

常见的线程类型包括用户线程&#xff08;User Threads&#xff09;、守护线程&#xff08;Daemon Threads&#xff09;、主线程&#xff08;Main Thread&#xff09;、工作线程&#xff08;Worker Threads&#xff09;和线程池中的线程。 一、用户线程&#xff08;User Thread…...

详解高斯消元

详解高斯消元 好东西,可以求所有一次方程组的解。 \color {red} 好东西,可以求所有一次方程组的解。 好东西,可以求所有一次方程组的解。 前置知识 一般消元法的公理: 两方程互换,解不变; 一方程乘以非零数 k k k,解不变; 一方程乘以数 k k k加上另一方程,解不变。 …...

【Python网络爬虫笔记】5-(Request 带参数的get请求) 爬取豆瓣电影排行信息

目录 1.抓包工具查看网站信息2.代码实现3.运行结果 1.抓包工具查看网站信息 请求路径 url:https://movie.douban.com/typerank请求参数 页面往下拉&#xff0c;出现新的请求结果&#xff0c;参数start更新&#xff0c;每次刷新出20条新的电影数据 2.代码实现 # 使用网络爬…...

泷羽sec- shell编程(8) until循环以及函数基本创建调用 学习笔记

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

Apache Flink从Kafka中消费商品数据,并进行商品分类的数量统计题

使用Apache Flink从Kafka中消费商品数据&#xff0c;并进行商品分类的数量统计是一个典型的流处理任务。以下是一个详细的步骤指南和示例代码&#xff0c;帮助你实现这一功能。 ### 前提条件 1. **安装Flink**&#xff1a;确保你的环境中已经安装了 Apache Flink。 2. **安装…...

Ubuntu 安装 MariaDB

安装 MariaDB具体步骤 1、更新软件包索引&#xff1a; sudo apt update2、安装 MariaDB 服务器&#xff1a; sudo apt install mariadb-server3、启动 MariaDB 服务&#xff08;如果未自动启动&#xff09;&#xff1a; sudo systemctl start mariadb4、设置 MariaDB 开机启…...

GPT打字机效果—— fetchEventSouce进行sse流式请求

EventStream基本用法 与 WebSocket 不同的是&#xff0c;服务器发送事件是单向的。数据消息只能从服务端到发送到客户端&#xff08;如用户的浏览器&#xff09;。这使其成为不需要从客户端往服务器发送消息的情况下的最佳选择。 const evtSource new EventSource(“/api/v1/…...

Leetcode 3373. Maximize the Number of Target Nodes After Connecting Trees II

Leetcode 3373. Maximize the Number of Target Nodes After Connecting Trees II 1. 接替思路2. 代码实现 题目链接&#xff1a;3373. Maximize the Number of Target Nodes After Connecting Trees II 1. 接替思路 这一题和前一题Leetcode 3372其实整体思路上并没有啥太大…...

JS的魔法三角:constructor、prototype与__proto__

在JavaScript中&#xff0c;constructor、prototype和__proto__是与对象创建和继承机制紧密相关的三个概念。理解它们之间的关系对于掌握JavaScript的面向对象编程至关重要。下面将详细介绍这个魔法三角&#xff1a; 1. constructor 定义&#xff1a;constructor是一个函数&am…...

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…...

Leetcode打卡:N皇后

执行结果&#xff1a;通过 题目&#xff1a;51 N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#…...

位运算在嵌入式系统开发中的应用

目录 一、数据存储与节省 “绝技” 1.1. 传感器数据存储挑战 1.2. 位运算解决方案 1.2.1. 数据整合 1.2.2. 数据提取 1.3. 收益分析 二、硬件控制 “精准操纵术” 2.1. 位运算操控硬件寄存器的实例 2.2. 位运算在硬件控制中的优势 2.3. 电机驱动芯片寄存器控制示例 …...

livekit 服务部署

本地起 1. 拉取生成文件镜像 sudo docker pull livekit/generate 2. 生成配置文件 sudo docker run --rm -v $PWD:/output livekit/generate --local (记住输出信息) 3. 拉取livekit/livekit-server sudo docker pull livekit/livekit-server 4. 开始运行服务 sudo docker run…...

笔记:visual studio2022编译 和 运行 VTK9.4.0

一、下载源码 VTK官网下载对应源码。 Download | VTKhttps://vtk.org/download/ 二、编译动态库&#xff08;基于Win11 24h&#xff09; 1. 用VS打开VTK源码的CMakeLists.txt&#xff0c;等待项目配置完成。 生成完毕如图 2.生成动态库&#xff0c;点击全部生成&#xff0c…...

【Linux | 计网】TCP协议深度解析:从连接管理到流量控制与滑动窗口

目录 前言&#xff1a; 1、三次握手和四次挥手的联系&#xff1a; 为什么挥手必须要将ACK和FIN分开呢&#xff1f; 2.理解 CLOSE_WAIT 状态 CLOSE_WAIT状态的特点 3.FIN_WAIT状态讲解 3.1、FIN_WAIT_1状态 3.2、FIN_WAIT_2状态 3.3、FIN_WAIT状态的作用与意义 4.理解…...

Qt Sensors 传感器控制介绍篇

文章目录 Qt Sensors 模块介绍前言 什么是 Qt Sensors&#xff1f;主要特点&#xff1a; 支持的传感器类型Qt Sensors 的核心组件应用场景优势总结 Qt Sensors 模块介绍 前言 随着现代硬件设备的不断发展&#xff0c;传感器已成为许多设备&#xff08;如智能手机、平板电脑和…...

探索3D世界:使用 lib3ds 读取和解析 3DS 文件

在3D图形开发中&#xff0c;读取和解析3DS文件是创建和渲染3D场景的第一步。3DS&#xff08;3D Studio&#xff09;文件格式是一种广泛使用的3D模型文件格式&#xff0c;它包含了多种类型的数据&#xff0c;用于描述3D场景中的物体、材质、相机、灯光和动画等。lib3ds 是一个开…...

【开源免费】基于SpringBoot+Vue.JS服装生产管理系统(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 066 &#xff0c;文末自助获取源码 \color{red}{T066&#xff0c;文末自助获取源码} T066&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…...

计算机技术:冯·诺伊曼架构

冯诺伊曼架构&#xff08;Von Neumann Architecture&#xff09;是由著名数学家和计算机科学家约翰冯诺伊曼&#xff08;John von Neumann&#xff09;在20世纪40年代提出的一种计算机设计模型。它奠定了现代通用计算机设计的基础。 1. 核心思想 冯诺伊曼架构的核心思想是存储…...

在Window10或11系统中同时安装 JDK8 和 JDK11

在Window10或11系统中同时安装 JDK8 和 JDK11 最近写项目&#xff0c;之前的项目是用Java8环境开发的&#xff0c;在二次迭代中&#xff0c;但是新开发的项目采用Java11环境来开发&#xff0c;所以需要同时安装JDK8和JDK11环境&#xff0c;但是两个环境是不能同时使用的&#…...

Hbase2.2.7集群部署

环境说明 准备三台服务器&#xff0c;分别为&#xff1a;bigdata141&#xff08;作为Hbase主节点&#xff09;、bigdata142、bigdata143确保hadoop和zookeeper集群都先启动好我这边的hadoop版本为3.2.0&#xff0c;zookeeper版本为3.5.8 下载安装包 下载链接&#xff1a;In…...

Java学习,数据结构

Java 提供了丰富的数据结构来处理和组织数据&#xff0c;Java的 java.util 包中提供了许多这些数据结构的实现&#xff0c;可以根据需要选择合适的类。 List&#xff08;列表&#xff09; ArrayList&#xff1a;基于动态数组的数据结构&#xff0c;允许包含重复元素和 null。 …...

微众银行前端面试题及参考答案

使用的协议是 HTTP 还是 HTTPS&#xff0c;为什么没用 HTTPS&#xff1f; 在前端开发中&#xff0c;有些网站使用 HTTP 协议&#xff0c;有些使用 HTTPS 协议。 使用 HTTP 协议的情况可能是因为网站对安全性的要求不是极高&#xff0c;或者处于开发的早期阶段&#xff0c;还没有…...

spark同步mysql数据到sqlserver

使用Apache Spark将数据从MySQL同步到SQL Server是一个常见的ETL&#xff08;Extract, Transform, Load&#xff09;任务。这里提供一个基本的步骤指南&#xff0c;以及一些代码示例来帮助你完成这项工作。 ### 前提条件 1. **安装Spark**&#xff1a;确保你的环境中已经安装了…...

使用 useMemo 和 React.memo 优化 React 组件渲染

在 React 中&#xff0c;性能优化是一个重要的主题&#xff0c;特别是在复杂的组件树中。本文将演示如何在同一个父组件中使用 useMemo 和 React.memo 来优化子组件的渲染。 1. 组件结构 创建一个父组件&#xff0c;包含两个子组件&#xff1a; MemoChild&#xff1a;使用 R…...

像素流送api ue多人访问需要什么显卡服务器

关于像素流送UE推流&#xff0c;在之前的文章里其实小芹和大家聊过很多&#xff0c;不过今天偶然搜索发现还是有很多小伙伴&#xff0c;在搜索像素流送相关的问题&#xff0c;搜索引擎给的提示有这些。当然这些都是比较短的词汇&#xff0c;可能每个人真正遇到的问题和想获取的…...

Bert+CRF的NER实战

CRF&#xff08;条件随机场-Conditional Random Field&#xff09; 原始本文&#xff1a;我在北京吃炸酱面 标注示例&#xff1a; 我O在O北B-PLA京I-PLA吃O炸B-FOOD酱I-FOOD面I-FOOD CRF&#xff1a; 目的&#xff1a;提出一些不可能出现的预测组合&#xff08;例如I-PLA不能…...

python学习笔记8-函数2

参数传递 传不可变对象 & 传可变对象 def func(b):print(id(a), a) #140737041872600 234print(id(b), b) #140737041872600 234a 234 func(a)def func(b):print(id(a), a) #1413554098560 [343]print(id(b), b) #1413554098560 [343]a [343] func(a)def func(b):b.appe…...

【linux学习指南】详解Linux进程信号保存

文章目录 &#x1f4dd;保存信号&#x1f320; 信号其他相关常⻅概念&#x1f309;在内核中的表⽰ &#x1f320; sigset_t&#x1f320;信号集操作函数&#x1f309;sigprocmask&#x1f309;sigpending &#x1f6a9;总结 &#x1f4dd;保存信号 &#x1f320; 信号其他相关常…...

Postman设置接口关联,实现参数化

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman设置接口关联 在实际的接口测试中&#xff0c;后一个接口经常需要用到前一个接口返回的结果&#xff0c; 从而让后一个接口能正常执行&#xff0c;这…...

ORACLE之DBA常用数据库查询

数据库信息 数据库概要select a.name "DB Name", e.global_name "Global Name", c.host_name "Host Name", c.instance_name "Instance Name" , DECODE(c.logins,RESTRICTED,YES,NO) "Restricted Mo…...

C++之C++11新特性(三)--- 智能指针

目录 一、智能指针 1.1 为什么需要智能指针 1.2 内存泄漏 1.2.1 内存泄漏的基本概念 1.2.2 内存泄漏的分类 1.2.3 如何避免内存泄漏 1.3 智能指针的使用及其原理 1.3.1 RAII 1.3.2 智能指针的基本原理 1.3.3 auto_ptr 1.3.4 unique_ptr 1.3.5 shared_ptr 1.3.6 sha…...

12 设计模式之工厂方法模式

一、什么是工厂方法模式&#xff1f; 1.定义 在软件开发中&#xff0c;设计模式 是解决常见软件设计问题的最佳实践。而 工厂方法模式&#xff08;Factory Method Pattern&#xff09; 作为创建型设计模式之一&#xff0c;常常被用来解决对象创建问题。它通过将对象的创建交给…...

Python 自动化办公的 10 大脚本

大家好&#xff0c;我是你们的 Python 讲师&#xff01;今天我们将讨论 10 个实用的 Python 自动化办公脚本。这些脚本可以帮助你简化日常工作&#xff0c;提高效率。无论是处理 Excel 文件、发送邮件&#xff0c;还是自动化网页操作&#xff0c;Python 都能派上用场。 1. 批量…...

十一、快速入门go语言之接口和反射

文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 &#x1f4c5; 2024年5月9日 &#x1f4e6; 使用版本为1.21.5 接口 十、Java类的封装和继承、多态 - 七点半的菜市…...

Python中字符串和正则表达式

Python中字符串和正则表达式 在Python编程中&#xff0c;字符串是最常用的数据类型之一。字符串用于表示文本数据&#xff0c;而正则表达式则是一种强大的工具&#xff0c;用于处理和匹配字符串中的模式。本文将介绍Python中的字符串操作、字符串格式化以及如何使用正则表达式…...