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

news/2024/4/24 5:32:20/文章来源:https://blog.csdn.net/m0_65679465/article/details/128931489

文章目录

  • 📕再谈构造函数
    • 1. 构造函数体赋值
    • 2. 初始化列表
    • 3. explicit 关键字
  • 📕static 成员
    • 1. 概念
    • 2. static 成员变量
    • 3. static 成员函数
  • 📕 友元
    • 1. 友元函数
    • 2. 友元类
  • 📕内部类
  • 📕编译器优化


📕再谈构造函数

1. 构造函数体赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

class Date {
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对 对象中 成员变量 的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

对于上面普通的成员变量,不初始化当然没有关系,但是比如下面的呢?

main 函数中 定义了一个 aa 对象,这是对这个对象的定义,并没有定义其内部的成员变量。
引用、const修饰的成员变量、无默认构造的自定义类型成员。(默认构造:无参、全缺省、编译器自动生成的构造函数) 对于这三类成员变量,前两个都是要在定义的地方初始化,第三个无法传参。所以,必须要有一个 将成员变量初始化 的途径。

对于前两者,当然可以考虑使用缺省值,但是,缺省值是在 C++ 11 才出现的,那么之前是怎么解决的呢?
C++ 使用初始化列表,来完成该功能。

class B
{
public:B(int n):_b(0){cout << "B()" << endl;}private:int _b;
};class A
{
public:private:int _a1=1; // 声明int _a2=2;// 引用 、const修饰 、没有默认构造 的  自定义类型成员const int _x;int& _ref;   B bb;
};int main()
{const int t = 10;  // const 修饰,只能初始化的时候给值A aa;  // 对象的整体定义,每个成员什么时候定义呢?return 0;
}

2. 初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式

如下,A 中给出了初始化列表,可以简单的理解为在构造函数中间加一点"东西"。

按照初始化列表里面从上到下的顺序来看就是:初始化 _x 为 1 、初始化 _ref 为 _a 的引用 、初始化bb 并且传参为 4(自定义类型,调用它的初始化列表)。
然后函数体里面进行 _a1++ 、 _a2-- 操作。

class B
{
public:B(int n):_b(0){cout << "B()" << endl;}private:int _b;
};class A
{
public:A():_x(1), _ref(_a1)  // 初始化成对 _a1 的引用, bb(4)      // B 类的初始化列表有参数,没有默认构造(无参数),需要传参,必须这样写{_a1++;_a2--;}
private:int _a1=1; // 声明int _a2=2;// 引用 、const修饰 、没有默认构造 的  自定义类型成员const int _x;int& _ref;   // 引用也要在定义的地方初始化B bb;
};int main()
{const int t = 10;  // const 修饰A aa;  // 对象的整体定义return 0;
}

成员在初始化列表中的顺序,并不就是它们的初始化顺序。

如下代码,运行结果是:输出 1 随机值 。
因为初始化列表里面,初始化顺序是 声明的顺序,而 不是 初始化列表里面的顺序。(如果是按照初始化列表里的顺序,那么 _a1 先初始化为参数的值,_a2 再初始化为 _a1 的值)

class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}

所以,初始化列表有以下的一些注意点:

    1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)。
    1. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用 成员变量
    • const 成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
    1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
    1. 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。

我们再来体会一下单参数和多参数的区别。

引用 那一篇文章有写到,内置类型 转换成 自定义类型的引用,必须要用 const 修饰,因为是先将内置类型 放到 临时变量里面,而临时变量具有常性,所以为了避免权限放大,要用 const 修饰。比如下面 main 函数里的第一行代码。

但是对于单个参数的构造函数而言,如果也像下面注释一样,先 构造 临时变量、再将 临时变量 拷贝构造给 a2,未免过于麻烦,所以有的编译器就会优化,将这两个过程合二为一,直接当成构造,1 就相当于传过去的参数。

如下,根据输出结果,也可以看出没有调用拷贝构造

请添加图片描述

class A {
public:A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& a){cout << "A(const A& a)" << endl;}private:int _a1;
};int main()
{const A& a3 = 1; // 将 1 放到 A 类型的 临时变量里面。临时变量具有常性,所以要const修饰// 单个参数A a1(1);A a2 = 1; // 隐式类型转换, 将 1 当作参数,调用初始化列表,有了 A 类型的临时变量,将临时变量拷贝给 a2return 0;
}

C++ 一开始是不支持多参数的,后来也支持了,其用法和单参数类似,只不过是使用的时候,要用大的花括号

class A {
public:A(int a,int b):_a1(a){cout << "A(int a)" << endl;}A(const A& a){cout << "A(const A& a)" << endl;}private:int _a1;
};int main()
{// 多个参数A b1(1, 2);A b2 = { 2,2 };  // 多参数这样写const A& b3 = { 4,7 };return 0;
}

3. explicit 关键字

构造函数不仅可以构造 初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值
的构造函数,还具有类型转换的作用,从上面两段代码也可以看出来(将 内置类型转换成了自定义类型)。

但是,如果我们不需要构造函数的类型转换功能,也是可以的,只要用 explicit 修饰构造函数即可。

如下,explicit 修饰之后,依然强制类型转换,会报错。


class A {
public:explicit A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& a){cout << "A(const A& a)" << endl;}private:int _a1;
};int main()
{// 如果不想让内置类型,转换为自定义类型,可以在初始化列表的函数前面用 explicit 修饰A a = 1;  // 报错return 0;
}

📕static 成员

1. 概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数
静态成员变量一定要在类外进行初始化


2. static 成员变量

如果要 实现一个类A,计算程序中实例化了多少个A类型的对象
最先想到的就是,在全局设置一个变量,每一次 构造 或者 拷贝 的时候,该变量的值都 +1 ,这样就可以计算实例化出了多少个对象。

但是,有了 static 成员的知识储备,就可以在类里面实现,这样子能更好地体现“封装”的性质。

如下代码,static 修饰的成员变量 count,不属于某个具体的对象,而是属于整个类。在类里面只是声明,并不是定义,并且在类外定义的时候,是不要加 static 的。
但是如果成员变量在类里面被 private 修饰,那么就不可以访问到。所以必须要用 public 修饰,但是用 public 修饰之后,又可以被任意访问修改。

这样子就又出现了一个问题,如何才可以 不用public 修饰成员变量的情况下,还可以访问到count呢?这里要用static成员函数解决。

class A {
public:A(int a):_a1(a){count++;cout << "A(int a)" << endl;}A(const A& a){count++;cout << "A(const A& a)" << endl;}static int Size()  // 静态成员函数,没有 this 指针,所以无法访问非静态的成员变量{return count;}public:int _a1;static int count; // 声明。此外,这个成员变量不属于某个对象,属于整个类
};int A::count=0;  // 初始化int main()
{// count 需要变成 public 才可以被外部访问到A aa(2);cout << aa.count << endl;cout << A::count << endl;return 0;
}

3. static 成员函数

static 修饰的成员函数,没有 this 指针。所以,该函数内部不可以对 类 里面的非静态成员变量进行访问

如下,static 修饰的 Size() 函数,只能访问静态成员变量 count ,不可以访问非静态成员变量 _a1 ,因为该函数的参数是没有 this 指针的。
从某种程度上来说,静态成员函数就是为了静态成员变量而创造的。

下面的代码,通过 Size() 函数 返回 count 的值,但是却不会对 count 进行任何修改(因为返回值是拷贝进临时变量的)。所以是最完美的结果,“封装”性体现的很好。

class A {
public:A(int a):_a1(a){count++;cout << "A(int a)" << endl;}A(const A& a){count++;cout << "A(const A& a)" << endl;}static int Size()  // 静态成员函数,没有 this 指针,所以无法访问非静态的成员变量{return count;}private:int _a1;static int count; // 声明。此外,这个成员变量不属于某个对象,属于整个类
};int A::count=0;  // 初始化int main()
{// 定义静态成员函数,这个函数返回 count 的值cout << A::Size() << endl;cout << A(1).Size() << endl;  // 匿名对象,生命周期只在这一行return 0;
}

static 成员的一些特性:

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区。
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制。

📕 友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数友元类

1. 友元函数

如下代码,当我们想要在一个类里面对流输出进行重载的时候,使用起来 是 d1<< cout; 和常规使用方式反了。所以,要么在iostream 标准库里面添加重载,要么在外部写流输出重载,前者显然不现实。但是,在类的外部写流输出重载的时候,无法访问类的成员变量,也就无法输出对应的值。

此时就需要友元函数的介入。

class Date {
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 << cout;     ->     d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧ostream& operator<<(ostream& _cout){_cout << _year << "-" << _month << "-" << _day << endl;return _cout;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023,2,8);d1<<cout;
}

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加 friend 关键字。

如下代码,在 Date 类里面 申明 流输入重载、流输出重载,同时在两个函数的前面都加上 friend 。就代表这两个函数,是 Date 类的友元函数,可以访问 Date 类里面的成员变量

两个函数的函数定义里面也可以看出来,传入的第二个参数是 d 的引用,但是 Date 类里面,成员变量都是 private 修饰的,那么即使是实例化的对象,也无法访问。但是由于两个函数都是Date类的友元函数,所以可以直接访问

class Date {friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}int main()
{Date d;cin >> d;cout << d << endl;return 0;
}

友元函数的一些性质:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数。
  • 友元函数不能用const修饰。
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数。
  • 友元函数的调用与普通函数的调用原理相同。

2. 友元类

联想友元函数的声明以及用法,友元类也差不多。友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

如下,在 Time 类里面声明,Date 类是 Time 类的友元类,那么 Date 类里面的任何一个成员函数,都可以直接访问 Time 类里面的成员变量

但是,这样的关系是单向的—— Time 类 里面的成员函数,不可以直接访问 Date 类的成员变量。

class Time {friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};class Date {
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){
// 直接访问 Time 类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

关于友元类的一些性质:

  • 友元关系是单向的,不具有交换性。
    比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
    访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  • 友元关系不能传递
    如果C是B的友元, B是A的友元,则不能说明C时A的友元。

📕内部类

如果一个类定义在另一个类的内部,这个在内部的类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
内部类其实就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

比如下方的例子,B类在A类的内部,是A类的内部类。所以,B天生就是A的友元类,函数中可以通过A类的对象访问A类的成员变量(比如 foo 函数种,通过 a 访问 h) 。
另外,下方输出结果,sizeof(aa),结果是 8,并没有计算类型B里面的成员变量。这是因为,虽然B是A的内部类,但两个类是独立的,仅仅相当于B被隐藏起来了而已。

class A {
private:static int k;int h;
public:class B { // B天生就是A的友元public:void foo(const A& a){cout << k << endl;//OKcout << a.h << endl;//OK}};privateint _b1;double _b2;
};int A::k = 1;int main()
{A::B b;b.foo(A());A aa;cout<<sizeof(aa)<<endl;return 0;
}

使用内部类的情况也不多,下面是它的特性:

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。

📕编译器优化

在函数传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

如下代码,在构造函数、拷贝构造、赋值重载、析构 这些函数体里面,都使用了流输出,只要调用函数,就会打印相应的内容,调试就可以知道某一行代码具体干了什么

如下,将内置类型的 1 强制转换成A 类型的 aa1,按照之前理解的,要先构造为临时变量,然后临时变量拷贝构造给 aa1 。但是编译器会优化成直接构造,1 就是传的参数。

func(aa1) 要将实参拷贝为形参,所以调用一次拷贝构造。

func1(2); 先将 2 强制类型转换成 A 类型的临时变量,再将临时变量拷贝构造为形参。所以要先构造,再拷贝,但是编译器优化为直接构造。
func1(A(3)); 先以3为参数进行构造成为实参,再拷贝构造传给形参。优化为直接构造。

fuc2() 测试的是传引用,由于传引用不存在把实参拷贝构造给形参,所以没有优化的空间。

class A {
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa) {_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};void func1(A aa)
{}void func2(const A& aa)
{}int main()
{A aa1 = 1;     // 构造+拷贝构造 -》 优化为直接构造func1(aa1);    // 无优化func1(2);      // 构造+拷贝构造 -》 优化为直接构造func1(A(3));   // 构造+拷贝构造 -》 优化为直接构造cout << "----------------------------------" << endl;func2(aa1);  // 无优化func2(2);    // 无优化func2(A(3)); // 无优化return 0;
}

如下,A 依然是上面的类,一摸一样。改变的是两个外部函数,返回值传A类型的,并且函数内部定义A类型的对象。

直接调用 fuc3() ,无疑,先构造 aa ,再拷贝构造出一个临时变量作为返回值。所以调用了构造、拷贝构造。但是没有优化的空间,因为 func3() 函数内部 aa的 定义 和 返回是分开的

A aa1=func3(); 调用 fun3 的过程和上面一样,先构造、再拷贝构造。生成的临时变量再拷贝构造给 aa1 ,所以经历了两次拷贝构造,此时就会被优化成一个拷贝构造。

但是,对于 aa2 而言,aa2 已经定义好了,aa2 = func3(); 是赋值重载,而不是拷贝构造,所以无法优化。

对于 func4(); 它直接构造出一个匿名对象返回,相当于构造和拷贝构造是连续的,所以可以优化为构造。
A aa3 = func4(); 除了调用 func4() ,还多了将 func4() 生成的临时变量,拷贝构造给 aa3 的过程,所以是 构造、拷贝构造、拷贝构造。编译器会优化成 构造。

A func3()
{A aa;return aa;   // 不可以优化,因为分开的
}A func4()
{return A();  // 可以优化
}int main()
{func3();A aa1 = func3(); // 拷贝构造+拷贝构造  -- 优化为一个拷贝构造cout << "****" << endl;A aa2;aa2 = func3();   // 不能优化  , 因为是赋值重载cout << "---------------------------" << endl;func4();         // 构造+拷贝构造 -- 优化为构造A aa3 = func4(); // 构造+拷贝构造+拷贝构造  -- 优化为构造return 0;
}

上面所提到的一些优化,对于A类而言,确实没有什么区别,但是如果对于二叉树那些的呢?多次拷贝开销很大,所以,要学会使用这些小技巧,让程序更加优质!

这里浅浅地总结一下:

  • 如果可以,尽量 return 匿名对象
  • 函数传参,尽量传引用
  • 如果要用一个对象来初始化另一个对象,尽量使用拷贝构造而不是赋值重载。

类和对象部分到这一篇文章就算是完结啦!有什么错误的地方欢迎指正!!

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

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

相关文章

C# 引用DLL 静态字段和非静态字段

再讲一下如何引用dll动态链接库&#xff1a;右键项目----添加 --项目引用----选择你要添加的dll即可。在依赖项这里就可以看到。再在要用的项目那里using一下这个dll的命名空间&#xff1a;using 生成dll;然后就可以使用以下所说的两种方法去调用dll里的函数了。切记&#xff0…

canal 使用详解

第1章 Canal 简介canal [kənl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费工作原理canal 模拟 MySQL slave 的交互协议&#xff0c;伪装自己为 MySQL slave &#xff0c;向 MySQL master 发送 d…

fastadmin后台表单文字过长,限制显示,鼠标悬停显示全部

问题&#xff1a;显示文字区域过长&#xff0c;影响用户体验感 解决措施&#xff1a; 特别注意&#xff1a; return "<span styledisplay: block;overflow: hidden;text-overflow: ellipsis;white-space: nowrap; title" row.contents ">" value …

【物联网平台选型】葵花宝典:盘点开源、SaaS及通用型平台的优劣势和选型适配

随着工业物联网领域和智慧物联领域的发展&#xff0c;大大小小的物联项目和物联场景需求层出不穷&#xff0c;物联网平台作为技术底座型软件&#xff0c;是不可或缺的项目地基。 市场需求下&#xff0c;物联网平台提供商越来越多&#xff0c;“打地基”的方式大体分为开源平台、…

内核数据结构-XArray

内核数据结构-XArrayXArray简介XArray 基本数据结构Xarray结构图API介绍Xarray锁参考链接XArray简介 XArray是一种抽象数据类型&#xff0c;类似于一个大的指针数组&#xff0c;它满足了许多与哈希或常规可调整大小数组相同的需求。由于 xarray 中的数据都是指针&#xff0c;使…

以太网知识-GMII / RGMII接口

今天和海翎光电的小编一起分析MII/RMII/SMII&#xff0c;以及GMII/RGMII/SGMII接口的信号定义&#xff0c;及相关知识&#xff0c;同时小编也对RJ-45接口进行了总结&#xff0c;分析了在10/100模式下和1000M模式下的连接方法。GMII 接口分析GMII接口提供了8位数据通道&#xff…

shell条件测试

文章目录三、shell条件测试3.1条件测试的基本语法3.2 文件测试表达式3.3字符串测试表达式3.4 整数测试表达式3.5 逻辑操作符三、shell条件测试 为了能够正确处理Shell程序运行过程中遇到的各种情况&#xff0c;Linux Shell提供了一组测试运算符。通过这些运算符&#xff0c;Sh…

go语言的并发编程

并发编程是 Go语言的一个重要特性,而 go语言也是基于此而设计出来的。 本文将会介绍如何使用go-gc中的“runtime”方法实现 go语言中的并发编程。 在之前的文章中,我们已经对 runtime方法进行了详细介绍,这次文章将对 runtime方法进行深入分析,并讲解如何在go-gc中使用该方…

智能建筑电力监控自动化的解决方案

引言 安科瑞 李亚俊 壹捌柒贰壹零玖捌柒伍柒 所谓智能建筑就是采用计算机技术和通讯技术对建筑的设备进行自动监控&#xff0c;对信息资源进行管理和为用户提供信息服务等。美国智能建筑研究机构把智能建筑定义为&#xff1a;通过对建筑物的结构、系统、服务和管理四个基本要…

数据库模式(schema)是什么?

在数据库的术语中&#xff0c;模式&#xff08;schema&#xff09;是一个逻辑概念&#xff0c;用于组织数据库中的对象。模式中的对象通常包括表、索引、数据类型、序列、视图、存储过程、主键、外键等等。 模式可以为数据库对象提供逻辑隔离功能&#xff0c;不用应用程序可以…

负载均衡下的webshell上传

负载均衡下的webshell上传1.应用场景2.面临的困难2.1 shell文件上传问题2.2 命令执行时的漂移2.3 大工具投放失败2.4 内网穿透工具失效3.一些解决方案3.1 关机3.2 基于IP判断执行主机3.3 脚本实现web层的流量转发3.3.1 创建antproxy.jsp脚本3.3.2 修改 Shell 配置4.总结1.应用场…

开发必看!三分钟读懂Salesforce SOQL查询和限制

SOQL是支持我们与Salesforce数据库交互的查询语言。开发人员在编写Apex时通常会使用到SOQL&#xff0c;此外&#xff0c;它还允许管理员和开发人员从组织内部检索数据并在导出结果时生成强大的数据报告。 SOQL 查询对于编写代码的开发人员&#xff0c;以及通过使用子句扩展查询…

STM32 复用JLink下载线输出调试信息

编写STM32程序时&#xff0c;要输出调试信息的话&#xff0c;一般是通过一个串口输出&#xff0c;电脑端使用串口调试助手显示调试信息。这样的话&#xff0c;就需要占用一个串口资源。还有一种SEGGER的RTT方式&#xff0c;直接使用JLink下载线输出调试信息&#xff0c;这样可以…

在线支付系列【21】微信支付服务商接入前准备

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 文章目录项目概述接入准备1. 注册服务商号&#xff08;获取服务商mchid&#xff09;2. 注册公众号&#xff08;获取服务商APPID&#xff09;3. 绑定应用ID和服务商ID4. 入驻子商户&#xff08;特约商户进…

使用Jmeter抓取手机APP报文并进行APP接口测试

Jmeter是一个比较常用的接口测试工具&#xff0c;尤其是接口性能测试。当然它也可以用来测试手机APP的HTTP接口&#xff0c;我在Fiddler抓取手机APP报文 和 接口测试代理工具charles mock测试 分别介绍了Fiddler和charles 如何抓取APP报文&#xff0c;本文介绍使用Jmeter来抓取…

内网渗透(十三)之内网信息收集-收集域环境中的基本信息

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

Jmeter之实现参数化的不同方式详解

参数化简介 定义&#xff1a;动态的获取、设置或生成数据&#xff0c;是一种由程序驱动代替人工驱动的数据设计方案&#xff0c;提高脚本的编写效率以及编写质量 适用场景&#xff1a;当提交的数据量较大时&#xff0c;每次修改太麻烦&#xff0c;可以使用参数化 本文介绍实现…

linux yum安装卸载jdk8

1>安装1 yum -y list java* 列出jdk列表2 yum install -y java-1.8.0-openjdk-demo.x86_64&#xff08;安装这个java -version 正常显示&#xff0c;但是javac不能用&#xff0c;因为yum install java 只是安装了java的运行时环境&#xff0c;并不支持编译&#xff0c;安装成…

NLP学习——信息抽取

信息抽取 自动从半结构或无结构的文本中抽取出结构化信息的任务。常见的信息抽取任务有三类&#xff1a;实体抽取、关系抽取、事件抽取。 1、实体抽取 从一段文本中抽取出文本内容并识别为预定义的类别。 实体抽取任务中的复杂问题&#xff1a; 重复嵌套&#xff0c;原文中…

使用openai-whisper 语音转文字

前言&#xff1a;最近由于ChatGPT 的大热&#xff0c;AI 应用领域再次进入大众的视线&#xff0c;今天介绍一款AI应用whisper 可以较为准确的将人声转换为文字&#xff08;支持多国语言&#xff09;一、安装安装有两种方式pip 和源码编译安装&#xff0c;这里介绍pip安装方式安…