C++——类与对象(二)

目录

引言

类的默认成员函数

构造函数

1.构造函数的概念

2.注意事项

初始化列表

1.初始化列表的概念

2.注意事项

析构函数

1.析构函数的概念

2.注意事项

拷贝构造函数

1.拷贝构造函数的概念

2.注意事项

运算符重载

1.运算符重载的概念

2.注意事项

赋值运算符重载

1.赋值运算符重载的定义

2.注意事项

取地址运算符重载

1.const成员函数

2.取地址运算符重载

结束语


引言

在C++——类与对象(一) 我们学习类与对象的一些基础知识,接下来我们接着学习。

类的默认成员函数

在C++中,当你定义一个类时,即使没有显式地声明某些成员函数,编译器也会为该类自动生成一些默认的成员函数。

⼀个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最 后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后还会增加两个默认成员函数, 移动构造和移动赋值,这个我们后面再讲解。默认成员函数很重要,也比较复杂,我们要从两个方面去学习:

第一:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求

第二:编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现?

构造函数

1.构造函数的概念

构造函数是一种特殊的方法,主要用于在创建对象时初始化对象。在面向对象编程(OOP)中,构造函数是类的一个组成部分,它当创建类的新实例时自动调用。构造函数的主要目的是为新创建的对象分配初始值或执行必要的启动操作。

其特点如下:

1.函数名与类名相同。

2. 无返回值。(返回值都不需要给,也不需要写void,不要纠结,C++规定如此)

3. 对象实例化时系统会自动调用对应的构造函数。

4. 构造函数可以重载。

下面是一个日期类的构造函数: 

class Date
{
public:Date(int year, int month, int day){_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,9,1);//自动调用d1.Print();return 0;
}

输出结果如下:

构造函数的功能就相当于初始化函数。构造函数通过其自动调用的特性,在提升代码的容错率和可维护性方面发挥了重要作用。它们确保了每个对象在创建时都能以预期的方式被初始化,减少了因未初始化或错误初始化而导致的错误。

2.注意事项

(1)如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

来看个简单的例子:

class Date
{
public:/*Date(int year, int month, int day){_year = year;_month = month;_day = day;}*/// 编译器自动生成一个无参的默认构造函数void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};

(2)无参构造函数、全缺省构造函数以及编译器默认生成的构造函数,统称为默认构造函数,因为它们都允许在不传递实参的情况下进行对象初始化。在同一个类中,无参构造函数和全缺省构造函数不能同时存在,因为它们之间构成函数重载但调用时会引发歧义。简而言之,默认构造指的是无需显式提供实参即可调用的构造函数。

比如如下代码:

class Date
{
public:Date()//无参{_year = 1900;_month = 1;_day = 1;}Date(int year = 2024, int month = 9, 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 d;	// 引起混淆return 0;
}

如下所示:

在C++中,如果一个类定义了多个构造函数,一旦我们对对象进行实例化,那么编译器在尝试调用这些构造函数时就会遇到歧义,因为它不知道应该选择哪一个。

(3)编译器默认生成的构造函数对内置类型成员变量不进行显式初始化(初始化不确定),但会尝试调用自定义类型成员变量的默认构造函数进行初始化

举个简单的例子,看下面的代码:

class A
{
public:A(){cout << "hello world" << endl;}
private:int _a;
};
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:A a;int _year;int _month;int _day;
};
int main()
{Date d;d.Print();return 0;
}

输出结果为:

Date 类的成员变量 _year、_month 和 _day 没有被初始化,这将导致 Print() 方法输出不确定的值。

我们可以得知:编译器自动生成的默认构造函数只对自定义类型进行初始化。

到后来,C++11 中针对内置类型成员不初始化的缺陷,进行了优化,即:内置类型成员变量在类中声明时可以给默认值

如下所示:

class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 1;	//缺省值int _month = 1;	//缺省值int _day = 1;	//缺省值
};
int main()
{Date d;d.Print();return 0;
}

输出结果如下:

初始化列表

1.初始化列表的概念

初始化列表是C++中构造函数的一个特性,它允许在构造函数体执行之前,直接初始化对象的成员变量或基类。使用初始化列表可以提高效率,特别是对于那些需要复杂构造的成员变量或基类,因为它避免了先调用默认构造函数然后再进行赋值的开销。

初始化列表位于构造函数参数列表之后,构造函数体的大括号 {} 之前,由冒号:引导。

举个简单的例子:

class Date
{
public:Date(int year, int month, int day)// 使用初始化列表初始化各个成员变量: _year(year), _month(month), _day(day){// 构造函数体可以为空// 因为所有成员变量的初始化已经在初始化列表中完成  // 这里可以添加一些额外的初始化代码}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d(2024, 9, 1);d.Print();return 0;
}

输出结果为:

2.注意事项

(1)成员初始化顺序

成员变量的初始化顺序是由它们在类中的声明顺序决定的,而不是在初始化列表中的顺序。如果尝试在初始化列表中改变这个顺序,可能会导致未定义行为。

像这样:

class MyClass 
{  
public:  MyClass() : b(a), a(10) {    } // 错误:a 在 b 之后初始化,尽管在初始化列表中 b 写在前面  
private:  int a;  int b; // b 依赖于 a 的值,但 a 还未初始化  
};

(2)每个成员变量只能初始化一次

(3)必须放在初始化列表中的成员

类中包含以下成员,必须放在初始化列表位置进行初始化:const成员变量,引用成员变量,自定义类型成员(且该类没有默认构造函数时)。因为这些变量都需要在定义时初始化。

const成员变量:const成员变量必须在构造时初始化,且之后不能修改,因此必须放在初始化列表中。

引用成员变量:引用必须在定义时初始化,并且之后不能重新指向另一个对象,因此也必须放在初始化列表中。

没有默认构造函数的自定义类型成员:如果类的成员是自定义类型,且该类型没有默认构造函数(或默认构造函数被声明为delete),则必须在初始化列表中显式地初始化该成员。

像这样:

class A
{
public:A(int a):_a(a){// ...}
private:int _a;
};
class B
{
public:B(int a, int ret):_b(a), _ret(ret), _n(3){// ...}
private:A _b;			// 没有默认构造函数的自定义类型成员int& _ret;		// 引用const int _n;	// const常量
};

(3)尽量使用初始化列表初始化,通常使用初始化列表,对于自定义类型成员变量,会先使用初始化列表初始化。

如下所示:

#include <iostream>
using namespace std;class A
{
public:A(int a) : _a(a)  // 使用初始化列表初始化成员变量{cout << "A(int a)" << endl;}
private:int _a;
};class B
{
public:B(int a, int b): _a(b), _b(a)  // 先初始化 _a 然后初始化 _b{cout << "B(int a, int b)" << endl;}
private:A _a;  // A 的构造函数会先于 B 的构造函数执行int _b;
};int main()
{B b(2, 3);return 0;
}

输出结果:

析构函数

1.析构函数的概念

析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,比如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作

其特点为:

1.析构函数名是在类名前加上字符 ~。
2.无参数无返回值。(这里跟构造类似,也不需要加void)
3.一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4.对象生命周期结束时,系统会自动调用析构函数。

下面是一个简单的示例,同样是日期类:

class Date
{
public:Date(int year = 2024, int month = 9, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//析构函数~Date(){_year = _month = _day = 0;}
private:int _year;int _month;int _day;
};

析构函数相当于C语言中的销毁函数,确保了在对象销毁时,所有由该对象使用的资源都能得到妥善处理,从而避免资源泄露和其他潜在问题。

并且它是自动调用的,可以大大提高代码的容错率。

2.注意事项

(1)如果类中没有显式定义析构函数,则C++编译器会自动生成一个析构函数,一旦用户显式定义编译器将不再生成。

class Date
{
public:Date(int year = 2024, int month = 9, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//析构函数/*~Date(){_year = _month = _day = 0;}*/// 编译器会自动生成一个析构函数
private:int _year;int _month;int _day;
};

(2)默认的析构函数对于内置类型(如int、float等)的成员变量不做任何处理,因为内置类型的生命周期是自动管理的,对于自定义类型调用其析构函数。

class A
{
public:~A(){cout << "~A()" << endl;}
private:int _a;
};
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:A a;int _year;int _month;int _day;
};
int main()
{Date date(2024, 9, 1);date.Print();// 当 date 离开作用域时,其析构函数将被调用return 0;
}

输出结果为:

拷贝构造函数

1.拷贝构造函数的概念

拷贝构造函数是一种特殊的构造函数,只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),用于创建一个对象作为另一个同类型对象的副本。

其特点如下:

拷贝构造函数是构造函数的一个重载

举个例子:

class Date
{
public:Date(int year = 1900, 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;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 9, 1);// 使用拷贝构造函数创建d1的副本d2Date d2(d1);	//拷贝构造// 使用拷贝初始化(也是拷贝构造的一种形式)创建d1的副本d3Date d3 = d1;	//拷贝构造d1.Print();d2.Print();d3.Print();return 0;
}

输出结果为:

2.注意事项

(1)拷贝构造函数的参数只有⼀个且必须是类类型对象的引用,使⽤传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用

错误代码如下:

Date(const Date d)
{_year = d._year;_month = d._month;_day = d._day;
}

(2)如果类没有显式定义拷贝构造函数,编译器会生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会按照对象的内存布局逐字节地复制数据(即浅拷贝或值拷贝)。这意味着对于类中的基本数据类型成员和指针成员,它们的值(或地址)会被直接复制到新创建的对象中。

如下所示:

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 1900, 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;}*/void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 9, 1);// 使用拷贝构造函数创建d1的副本d2Date d2(d1);	//拷贝构造// 使用拷贝初始化(也是拷贝构造的一种形式)创建d1的副本d3Date d3 = d1;	//拷贝构造d1.Print();d2.Print();d3.Print();return 0;
}

输出结果如下:

(3)编译器默认生成的拷贝构造函数执行的是值拷贝(也称为浅拷贝),它会逐字节地复制对象的数据。在大多数情况下,这对于只包含基本数据类型(如整数、浮点数等)的对象来说是足够的。但是在某些场景会出错。

来看个例子:

class A 
{
public:A() : data(new int(10)) {} // 构造函数中动态分配内存  ~A() { delete data; } // 析构函数中释放内存  // 注意:这里没有显式定义拷贝构造函数  void Print() const {std::cout << *data << std::endl;}private:int* data; // 指向动态分配内存的指针  
};int main() 
{A a1; // 创建第一个对象,分配内存  A a2 = a1; // 拷贝构造第二个对象,但这里使用的是默认拷贝构造函数  // 现在 a1 和 a2 共享同一块内存  a1.Print(); // 输出 10  a2.Print(); // 输出 10  // 当 main 函数结束时,a2 和 a1 的析构函数将被调用  // 由于它们共享同一块内存,这块内存将被释放两次,// 导致未定义行为  return 0;
}

如果类中有一个指向动态分配内存的指针,并且你没有在拷贝构造函数中显式地管理这块内存的复制(即没有创建新的内存副本并将指针指向它),那么原始对象和新对象将共享同一块内存。这可能导致在对象析构时内存被多次释放悬挂指针等问题。

运算符重载

1.运算符重载的概念

运算符重载(Operator Overloading)是C++中的一个重要特性,它允许程序员为已有的运算符(如+、-、*、/等)赋予新的含义,以便它们能够用于用户自定义的类型(如类)上。这意味着我们可以定义运算符如何作用于类的对象,从而使对象的使用更加直观和方便。运算符重载是具由运算符operator定义有特殊函数名的函数。

下面是个简单的示例,判断两个日期是否相等的运算符重载:

using namespace std;
class Date
{
public:Date(int year, int month, int day):_year(year),_month(month), _day(day){// ...}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//private:int _year;int _month;int _day;
};bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}int main()
{Date d1(2024, 7, 6);Date d2(2024, 8, 9);if (d1 == d2){cout << "相等" << endl;}else{cout << "不相等" << endl;}return 0;
}

2.注意事项

(1)重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。一元运算符有一个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。

(2)如果一个重载运算符函数是成员函数,则它的第一个运算对象默认传给隐式的 this 指针,因此运算符重载作为成员函数时,参数比运算对象少一个。

像这样:

class Point 
{
public:int x, y;Point(int x = 0, int y = 0) : x(x), y(y) {}// 重载+运算符,接受另一个Point对象作为参数  Point operator+(const Point& rhs){return Point(x + rhs.x, y + rhs.y);}void print() const {cout << "(" << x << ", " << y << ")" << endl;}
};int main() 
{Point p1(1, 2), p2(3, 4), p3;p3 = p1 + p2; p3.print(); return 0;
}

(3)运算符重载以后,其优先级和结合性与对应的内置类型运算符保持一致。

(4)不能通过连接语法中没有的符号来创建新的操作符:比如operator@。

(5) .(成员访问运算符)、.*(指向成员的指针访问运算符)、::(作用域解析运算符)、sizeof(长度运算符)、? :(条件运算符)等操作符不能重载。

(6)重载操作符至少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如: int operator+(int x, int y) 。

(7)重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。 C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。

像这样:

Date Date::operator++(int) // 注意这里的int参数,它是区分前置和后置的关键  
{  Date tmp = *this; // 创建一个当前对象的副本  *this += 1; // 增加原对象的值  return tmp; // 返回增加之前的对象副本  
}
Date& Date::operator++() // 注意这里没有参数  
{  *this += 1; // 增加原对象的值  return *this; // 返回原对象的引用  
}

赋值运算符重载

1.赋值运算符重载的定义

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

赋值运算符重载是将运算符 = 进行运算符重载,如下所示:

class Date
{
public:Date(int year = 2000, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date& operator=(const Date& d)//赋值运算符重载{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}// 打印函数  void Print() const {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main() 
{Date d1(2024, 9, 1);Date d2;d2 = d1; // 调用赋值运算符  d2.Print(); Date d3;d3 = d2 = d1; // d2 = d1 后返回 d2 的引用,然后 d3 = d2  d3.Print(); return 0;
}

输出结果:

2.注意事项

(1)有返回值,且建议写成当前类类型引用,引用返回可以提高效率,有返回值目的是为了支持连续赋值场景。

(2)没有显式实现时,编译器会自动生成一个默认赋值运算符重载,默认赋值运算符重载行为跟默认构造函数类似,对内置类型成员变量会完成值拷贝/浅拷贝,对自定义类型成员变量会调用他的拷贝构造。

class Time
{
public:// 默认构造函数  Time(){_hour = 0; _minute = 0;_second = 0;}// 构造函数,接受小时、分钟和秒  Time(int hour, int minute, int second){_hour = hour;_minute = minute;_second = second;}// 赋值运算符重载  Time& operator=(const Time& t){cout << "Time& operator=(const Time& t)" << endl;if (this != &t){_hour = t._hour;_minute = t._minute;_second = t._second;}return *this;}// 成员函数用于打印时间  void Print() const{cout << _hour << ":" << _minute << ":" << _second << endl;}private:int _hour;int _minute;int _second;
};class Date
{
private:int _year = 2024;int _month = 9;int _day = 1;Time _t;
};int main()
{Time t1; // 调用默认构造函数  t1.Print(); // 输出: 0:0:0  Time t2(12, 15, 30); // 使用新定义的构造函数  // 显式调用赋值运算符重载  t1 = t2; // 将t2的值赋给t1  t1.Print(); return 0;
}

 输出结果为:

(3)因为编译器默认生成默认赋值运算符重载的是值拷贝,在某些场景下就会出错。与拷贝构造函数类似。

class A
{
public:A(){cout << "A created\n"; // 当A的实例被创建时打印  }~A(){cout << "A destroyed\n"; // 当A的实例被销毁时打印  }
};class MyClass
{
public:MyClass() :// 构造函数中动态分配一个A的实例,并初始化_ptr指向它 _ptr(new A())  {// ... }~MyClass(){delete _ptr; // 析构函数中释放_ptr指向的A的实例  // 注意:如果_ptr被浅拷贝到另一个MyClass实例,这将导致问题  }// 缺少拷贝构造函数和赋值运算符重载,这会导致问题  private:A* _ptr; // 指向需要管理的A类实例的指针  
};int main()
{MyClass a1; MyClass a2 = a1; // 现在a1._ptr和a2._ptr都指向同一个A的实例  MyClass a3; a3 = a1; // 现在a1._ptr、a2._ptr和a3._ptr都指向同一个A的实例  return 0;
}

当main函数结束时,a1、a2和a3的析构函数将按逆序被调用,这将导致同一个A的实例被删除三次,引发未定义行为。

取地址运算符重载

1.const成员函数

将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this

class Date 
{  
public:  Date(int year, int month, int day) :_year(year),_month(month), _day(day) {}  // Print函数被声明为const,表示它不会修改类的任何成员  void Print() const 
{  cout << _year << "-" << _month << "-" << _day << endl;  }  private:  int _year;  int _month;  int _day;  
};  int main() 
{  Date d(2024, 9, 1);  d.Print();  // 调用const成员函数  return 0;  
}

2.取地址运算符重载

我们可以对自定义类型使用运算符需要对其进行重载,自然也可以使用&运算符。

例如这样:

class Date
{
public:Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year; int _month;int _day; 
};

结束语

这部分内容有点多啊,写的比较长。。。

总之,感谢各位大佬!!!

求点赞收藏评论关注!!!

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

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

相关文章

开源网安引领AIGC+开发安全,智能防护铸就软件安全新高度

近日&#xff0c;国内网络安全领域知名媒体数说安全正式发布了《2024年中国网络安全市场100强》和《2024年中国网络安全十大创新方向》。开源网安凭借在市场表现力、资源支持力以及产品在AI方向的创新力上的优秀表现成功入选百强榜单&#xff0c;并被评为“AIGC开发安全”典型厂…

Percona 开源监控方案 PMM 详解

文章目录 前言1. 安装部署1.1 Server 安装1.2 Client 安装 2. 监控数据库2.1 MySQL2.2 PostgreSQL 3. Dashboard 介绍总结 前言 Percona Monitoring and Management (PMM) 是 Percona 公司基于业界流行的组件 Prometheus 和 Grafana 设计开发的一体化数据库监控解决方案。本篇…

【H2O2|全栈】关于HTML(3)HTML基础(二)

HTML相关知识 目录 HTML相关知识 前言 准备工作 标签的具体分类&#xff08;二&#xff09; 本文中的标签在什么位置使用&#xff1f; 本期前置知识点 超文本 超文本引用和源属性 图片标签 锚链接 iframe 锚点 预告和回顾 后话 前言 本系列博客将分享HTML相关…

【数据结构】你知道什么是二叉树的顺序存储结构吗?

文章目录 前言1. 顺序结构2. 实现顺序结构二叉树2.1 堆的概念与结构2.2 堆的实现2.2.1 向上调整算法2.2.2 向下调整算法 3. 结语 前言 二叉树一般可以使用两种结构存储&#xff0c;一种顺序结构&#xff0c;一种链式结构。本文将要介绍的是二叉树的顺序存储结构。 1. 顺序结构…

python安装以及访问openAI API

安装python 我是python小白&#xff0c;所以需要一步一步来&#xff0c;先安装。 一口吃不成胖子&#xff0c;记住。 从官网下载python&#xff0c;目前最新版本是3.12&#xff0c;但是据说稳定版3.11更好一点&#xff0c;所以&#xff0c;下载3.11&#xff0c;注意不要下载…

stm32的内部时钟源 | RC震荡电路

文章目录 前言学习了解 前言 了解到 内部高速RC振荡器&#xff08;HSI&#xff09;就是RC震荡器实现的&#xff0c;故想对RC震荡做些了解与分析。 学习了解 【不需要晶振&#xff0c;也可产生时钟脉冲&#xff01;RC振荡器的工作原理&#xff0c;维恩电桥振荡器&#xff01;…

string字符会调用new分配堆内存吗

gcc的string默认大小是32个字节&#xff0c;字符串小于等于15直接保存在栈上&#xff0c;超过之后才会使用new分配。

Kafka 分布式消息系统详细介绍

Kafka 分布式消息系统 一、Kafka 概述1.1 Kafka 定义1.2 Kafka 设计目标1.3 Kafka 特点 二、Kafka 架构设计2.1 基本架构2.2 Topic 和 Partition2.3 消费者和消费者组2.4 Replica 副本 三、Kafka 分布式集群搭建3.1 下载解压3.1.1 上传解压 3.2 修改 Kafka 配置文件3.2.1 修改z…

axure之变量

一、设置我们的第一个变量 1、点击axure上方设置一个全局变量a 3 2、加入按钮、文本框元件点击按钮文档框展示变量值。 交互选择【单击时】【设置文本】再点击函数。 点击插入变量和函数直接选择刚刚定义的全局变量&#xff0c;也可以直接手动写入函数(注意写入格式。) 这…

Gitflow基础知识

0.理想状态 现状 听完后的理想状态 没使用过 git 知道 git 是什么&#xff0c;会用 git 基础流程命令 用过 git&#xff0c;但只通过图形化界面操作 脱离图形化界面操作&#xff0c;通过 git 命令操作 会 git 命令 掌握 gitflow 规范&#xff0c;合理使用 rebase 和解决…

spark sql 优化

1. 配置 比例内存 : core 1:2 2. 增加 core 数可以增加 执行任务的 线程数 3. 计算有大表&#xff0c;并发生shuffle 时&#xff0c;生成的任务数是由spark.sql.shuffle.partitions 决定的&#xff0c;所以针对大表shuffle &#xff0c;要增加spark.sql.shuffle.partitio…

C++笔记20•数据结构:哈希(Hash)•

哈希 1.无序的关联式容器&#xff08;unordered_map&unordered_set&#xff09; unordered_map与unordered_set几乎与map与set是一样的&#xff0c;只是性能unordered_map与unordered_set比map与set更优一些。还有就是unordered_map与unordered_set是无序的&#xff0c;…

差异基因富集分析(R语言——GOKEGGGSEA)

接着上次的内容&#xff0c;上篇内容给大家分享了基因表达量怎么做分组差异分析&#xff0c;从而获得差异基因集&#xff0c;想了解的可以去看一下&#xff0c;这篇主要给大家分享一下得到显著差异基因集后怎么做一下通路富集。 1.准备差异基因集 我就直接把上次分享的拿到这…

服务器流量监控工具vnStat的简单使用以及关于Linux的软中断信号(signal)的一点内容

一、服务器流量监控工具vnStat的简单使用 vnStat是为Linux和BSD设计的基于控制台的网络流量监控工具&#xff0c;通过它可以非常方便在命令行查看流量统计情况。它可以保留某个或多个所选择的网络接口的网络流量日志。为了生成日志&#xff0c;vnStat使用内核提供的信息。换句话…

misc流量分析

一、wireshark语法 1、wireshark过滤语法 &#xff08;1&#xff09;过滤IP地址 ip.srcx.x..x.x 过滤源IP地址 ip.dstx.x.x.x 过滤目的IP ip.addrx.x.x.x 过滤某个IP &#xff08;2&#xff09;过滤端口号 tcp.port80tcp.srcport80 显示TCP的源端口80tcp.dstport80 显示…

Python和C++多尺度导图

&#x1f3af;要点 热化学属性观测蒙特卡罗似然比灵敏度分析时间尺度上动力学化学催化反应动力学建模自动微分电化学分析模型反应动力学数学模型渔业生态不确定性模型敏感性分析空间统计地理模型分析技术多维数据表征实现生成艺术图案流苏物体长度比&#xff0c;面积比和复杂度…

深度学习实战:如何利用CNN实现人脸识别考勤系统

1. 何为CNN及其在人脸识别中的应用 卷积神经网络&#xff08;CNN&#xff09;是深度学习中的核心技术之一&#xff0c;擅长处理图像数据。CNN通过卷积层提取图像的局部特征&#xff0c;在人脸识别领域尤其适用。CNN的多个层次可以逐步提取面部的特征&#xff0c;最终实现精确的…

Django+Vue3前后端分离学习(二)(重写User类)

一、重写User类&#xff1a; 1、首先导入User类&#xff1a; from django.contrib.auth.models import User 2、然后点在User上&#xff0c;按住ctrl 点进去&#xff0c;发现 User类继承AbstractUser Ctrl点进去AbstractUser&#xff0c;然后将此方法全部复制到自己APP的mo…

3 html5之css新选择器和属性

要说css的变化那是发展比较快的&#xff0c;新增的选择器也很多&#xff0c;而且还有很多都是比较实用的。这里举出一些案例&#xff0c;看看你平时都是否用过。 1 新增的一些写法&#xff1a; 1.1 导入css 这个是非常好的一个变化。这样可以让我们将css拆分成公共部分或者多…

WebDriver与Chrome DevTools Protocol:如何在浏览器自动化中提升效率

介绍 随着互联网数据的爆炸式增长&#xff0c;爬虫技术成为了获取信息的重要工具。在实际应用中&#xff0c;如何提升浏览器自动化的效率是开发者常常面临的挑战。Chrome DevTools Protocol&#xff08;CDP&#xff09;与Selenium WebDriver相结合&#xff0c;为浏览器自动化提…