<C++> 类和对象-类的默认成员函数

news2024/11/19 14:38:59

1.类的默认成员函数

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

如果一个类中什么成员都没有,简称为空类。

空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下默认成员函数。

  1. 默认构造函数:如果没有为类显式定义构造函数,编译器会生成一个默认构造函数。这个构造函数没有参数,用于创建对象时执行必要的初始化操作。
  2. 拷贝构造函数:如果没有为类定义拷贝构造函数,编译器会生成一个默认的拷贝构造函数。这个构造函数用于在对象初始化时,将一个已存在的对象的值复制到新对象中
  3. 赋值重载运算符:如果没有为类定义赋值重载运算符,编译器会生成一个默认的拷贝赋值运算符。这个运算符用于将一个已存在的对象的值赋给另一个已存在的对象。
  4. 析构函数:如果没有为类定义析构函数,编译器会生成一个默认的析构函数。析构函数在对象被销毁时调用,用于释放对象占用的资源。

2.构造函数

2.1 构造函数的概念

对于以下Date类:

#include <iostream>
using namespace std;
class Date {
public:
    void Init(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;
    d1.Init(2022, 7, 5);
    d1.Print();
    Date d2;
    d2.Init(2022, 7, 6);
    d2.Print();
    return 0;
}

对于Date类,可以通过Init公有函数给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

答案就是使用构造函数。

构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2 构造函数的特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

其特征如下:

1.函数名与类名相同。

2.无返回值

3.对象实例化时编译器自动调用对应的构造函数。也就是说在创建对象的时候,就对成员变量进行初始化。

4.构造函数可以重载。(一个类可以有多个构造函数,也就是多种初始化方式)

示例:

#include <iostream>
using namespace std;
class Date {
public:
    Date() {
        _year = 1;
        _month = 1;
        _day = 1;
    }

    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;
    Date d2(2023, 2, 3); 
	
    d1.Print();
    d2.Print();   
	
    Date d3();
    //注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明

    return 0;
}

5.无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

为什么只能有一个默认构造函数? 因为调用的时候会发生歧义

不传参数就可以调用构造函数,一般建议每个类都提供一个默认构造函数

class Date {
public:
    Date() {
        _year = 1;
        _month = 1;
        _day = 1;
    }
	
    //全缺省构造函数
    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;
};

全缺省构造函数不能和无参构造函数同时存在 - 出现歧义,编译器不知道调用哪个

在这里插入图片描述

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

#include <iostream>
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;
};

int main() {
    // 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
    // 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
    // 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
    Date d1;
    d1.Print();
    return 0;
}

但是我们发现输出结果是随机值?

在这里插入图片描述

关于编译器生成的默认成员函数,很多人会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象的值,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。

C++规定默认生成的构造函数:

1.内置类型成员不做处理
2.自定义类型的成员,会去调用自定义类型的类的构造函数。
注意:C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

默认构造函数的场景:

#include <iostream>
using namespace std;

class Time {
public:
    Time() {
        cout << "Time()" << endl;
        _hour = 0;
        _minute = 0;
        _second = 0;
    }

private:
    int _hour;
    int _minute;
    int _second;
};

class Date {
private:
    // 基本类型(内置类型)
    int _year;
    int _month;
    int _day;
    // 自定义类型
    Time _t;
};

int main() {
    Date d;
    return 0;
}

在这里插入图片描述

可以发现d对象生成的默认构造函数调用了自定义类型_t的构造函数

注意:

C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值

示例:

class Date {
private:
    // 基本类型(内置类型)
    int _year = 2023;
    int _month = 8;
    int _day = 4;
    // 自定义类型
    Time _t;
};

2.3 初始化列表

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

但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

在效率上,初始化列表通常比在构造函数体内初始化成员变量更高效,因为它们在对象创建的同时就初始化了成员变量,而不是先创建对象再对成员变量进行赋值。

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

class Date {
public:
    Date(int year, int month, int day)
        //初始化列表
        : _year(year), _month(month), _day(day) {}

private:
    int _year;
    int _month;
    int _day;
};

注意:

1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2.类中包含以下成员,必须放在初始化列表位置进行初始化:

  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)
class A {
public:
    A(int a)
        : _a(a) {}

private:
    int _a;
};

class B {
public:
    B(int a, int ref)
        : _aobj(a), _ref(ref), _n(10) {}

private:
    A _aobj;     // 没有默认构造函数
    int &_ref;   // 引用
    const int _n;// const
};

3.尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。

class Time {
public:
    Time(int hour = 0)
        : _hour(hour) {
        cout << "Time()" << endl;
    }

private:
    int _hour;
};

class Date {
public:
    Date(int day) {}

private:
    int _day;
    Time _t;   //调用Time()的构造函数
};

int main() {
    Date d(1);
}

4.初始化列表中的初始化顺序与成员变量在类中的声明顺序一致,跟初始化列表顺序无关

#include <iostream>
using namespace std;

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();
}

a2先初始化,a1在初始化 此时调用初始化列表,而a1此时是随机值,所以a2(a1)后a2为随机值

2.4 explicit

explicit 是一个关键字,通常用于修饰单参数构造函数,其目的是防止隐式类型转换。它影响编译器在构造函数调用时是否进行隐式的类型转换。

当一个类的构造函数只有一个参数,并且没有使用 explicit 关键字进行修饰时,编译器会在需要的情况下自动执行该构造函数,将参数类型转换为类类型,从而创建一个临时对象。

使用 explicit 关键字修饰构造函数可以阻止这种隐式类型转换,从而防止一些意外的行为和潜在的错误。这对于避免编译器进行不必要的自动类型转换非常有用,因为有时候这种转换可能会引发不明确的代码行为。

示例1:

class Date {
public:
    //1.单参构造函数,没有使用explicit修饰,具有类型转换作用
    /*Date(int year) : _year(year) {}*/

    // 2,虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用 - int转换为Date类
    Date(int year, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1(2022);

    d1 = 2023;  //用一个整型变量给日期类型对象赋值
    //实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
    return 0;
}

示例2:

使用explicit禁止类型转换

class Date {
public:
    // explicit修饰构造函数,禁止类型转换
    explicit Date(int year, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1(2022);

    d1 = 2023;   //err 编译器报错,没有与这些操作数匹配的 "=" 运算符,操作数类型为:  Date = int
    return 0;
}

示例3:

class MyClass {
public:
    explicit MyClass(int value) : val(value) {}

private:
    int val;
};

void func(MyClass obj) {
    // ...
}

int main() {
    MyClass obj1 = 42;  // 编译错误,因为构造函数是 explicit 的,禁止隐式类型转换
    MyClass obj2(42);   // 正确,显式调用构造函数
    func(42);           // 编译错误,因为构造函数是 explicit 的,禁止隐式类型转换
    func(obj2);         // 正确,调用 func 时显式地传递 MyClass 对象
    return 0;
}

总结: 使用 explicit 关键字可以帮助避免隐式的类型转换,从而提高代码的清晰性和可靠性。

3.析构函数

析构函数是一种特殊的成员函数,用于在对象被销毁时执行必要的清理工作和资源释放操作。它与构造函数相反,构造函数在对象创建时调用,而析构函数在对象销毁时自动调用。析构函数的名称与类名相同,前面加上一个波浪号~

析构函数的主要目的是在对象生命周期结束时执行资源的清理,例如释放动态分配的内存、关闭文件、释放其他资源等。在 C++ 中,析构函数可以确保对象销毁时的资源管理,以防止内存泄漏和资源泄漏。

析构函数的特征:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  5. 先构造的后析构,后构造的先析构,因为对象是定义在函数中的,函数调用会建立栈帧,栈帧中的对象构造和析构也要符合先进后出的原则。

关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定义类型成员调用它的析构函数。

#include <iostream>
using namespace std;
class Time {
public:
    ~Time() {
        cout << "~Time()" << endl;
    }

private:
    int _hour;
    int _minute;
    int _second;
};

class Date {
private:
    // 基本类型(内置类型)
    int _year = 1970;
    int _month = 1;
    int _day = 1;
    // 自定义类型
    Time _t;   //释放Date的时候调用_t的析构函数
};

int main() {
    Date d;
    return 0;
}

输出结果:

~Time()

如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

4.拷贝构造函数

拷贝构造函数是一种特殊的构造函数,用于创建一个对象,该对象是已存在对象的副本。它通常用于在对象初始化期间以及函数传递参数等情况下创建新对象,这些新对象与已有对象具有相同的值。拷贝构造函数采用一个参数,即要被拷贝的对象的引用。

拷贝函数的特征如下:

1.拷贝构造函数是构造函数的一个重载形式。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

3.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

为什么会引发无穷递归?

假设拷贝构造函数的参数是非引用类型:

class MyClass {
public:
    MyClass(MyClass other) {  // 这里不是引用类型
        // ...
    }
};

当调用拷贝构造函数时,它需要一个对象作为参数。如果参数是非引用类型,那么传递给拷贝构造函数的参数对象会通过拷贝构造函数来创建一个临时的对象副本。但这会导致无限循环,因为在创建临时对象的过程中,又会调用拷贝构造函数来创建另一个临时对象,以此类推,导致无限递归。

使用引用类型的参数可以避免这个问题。当参数是引用类型时,传递给拷贝构造函数的是原始对象本身的引用,而不是创建的临时对象。这样就避免了无限循环的情况。

正确示例:

#include <iostream>
using namespace std;
//正确的写法
class Date {
public:
    Date(int year = 2023, 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(2021, 2, 3);
    Date d2(d1);
    d2.Print();
    return 0;
}

输出结果:

2021/2/3

4.1浅拷贝和深拷贝

浅拷贝是指仅仅复制对象的成员变量的值,包括指针变量。在浅拷贝中,复制的对象和原始对象共享相同的资源,这可能导致意外的副作用。如果资源被释放或修改,两个对象都会受到影响。

编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗? 当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

#include <iostream>
using namespace std;

//自动生成构造拷贝函数对自定义类型进行拷贝
typedef int DataType;
class Stack {
public:
    Stack(size_t capacity = 10) {
        cout << "Stack(size_t capacity = 10)" << endl;

        _array = (DataType*)malloc(capacity * sizeof(DataType));
        if (nullptr == _array) {
            perror("malloc申请空间失败");
            exit(-1);
        }

        _size = 0;
        _capacity = capacity;
    }

    void Push(const DataType& data) {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    ~Stack() {
        cout << "~Stack()" << endl;

        if (_array) {
            free(_array);
            _array = nullptr;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    DataType* _array; 
    size_t _size; 
    size_t _capacity; 
};

int main() {
    Stack st1;
    st1.Push(1);
    st1.Push(2);
    st1.Push(3);

    Stack st2(st1);
    return 0;
}

在这里插入图片描述

可以发现程序直接崩溃了,什么原因呢?
在这里插入图片描述

注意:

类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写,并且必须是深拷贝!

深拷贝是指在对象复制时,不仅复制对象的成员变量的值,还要复制指针指向的资源本身。这样新对象和原始对象就完全独立,对一个对象的修改不会影响另一个对象。

示例:

#include <iostream>
using namespace std;

typedef int DataType;
class Stack {
public:
    Stack(size_t capacity = 10) {
        cout << "Stack(size_t capacity = 10)" << endl;

        _array = (DataType*)malloc(capacity * sizeof(DataType));
        if (nullptr == _array) {
            perror("malloc申请空间失败");
            exit(-1);
        }

        _size = 0;
        _capacity = capacity;
    }

    void Push(const DataType& data) {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    //stack类的拷贝构造深拷贝
    Stack(const Stack& st) {
        cout << "Stack(const Stack& st)" << endl;
        //深拷贝开额外空间,为了避免指向同一空间
        _array = (DataType*)malloc(sizeof(DataType) * st._capacity);
        if (nullptr == _array) {
            perror("malloc申请空间失败");
            exit(-1);
        }
        //进行字节拷贝
        memcpy(_array, st._array, sizeof(DataType) * st._size);
        _size = st._size;
        _capacity = st._capacity;
    }

    ~Stack() {
        cout << "~Stack()" << endl;

        if (_array) {
            free(_array);
            _array = nullptr;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    DataType* _array;
    size_t _size;
    size_t _capacity;
};

class MyQueue {
public:
    //MyQueue什么都不写,会调用默认的构造函数,也就是Stack类的构造函数
    // 默认生成构造
    // 默认生成析构
    // 默认生成拷贝构造

private:
    //默认构造函数初始化 - 默认析构函数
    Stack _pushST;
    //默认构造函数初始化 - 默认析构函数
    Stack _popST;
    int _size = 0;
};

int main() {
    Stack st1;
    st1.Push(1);
    st1.Push(2);
    st1.Push(4);

    Stack st2(st1);
    cout << "=============================" << endl;

    MyQueue q1;
    //q1拷贝q2  q1中有两个Stack类和一个size,size直接拷贝,stack类是调用stack拷贝构造进行拷贝
    MyQueue q2(q1);

    return 0;
}

4.2 拷贝构造函数典型调用场景:

a、使用已存在对象创建新对象

b、函数参数类型为类类型对象

c、函数返回值类型为类类型对象

示例:

#include <iostream>
using namespace std;
class Date {
public:
    Date(int year, int minute, int day) {
        cout << "Date(int,int,int):" << this << endl;
    }

    Date(const Date &d) {
        cout << "Date(const Date& d):" << this << endl;
    }
    
    ~Date() {
        cout << "~Date():" << this << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

Date Test(Date d) {
    Date temp(d);
    return temp;
}

int main() {
    Date d1(2022, 1, 13);
    Test(d1);
    return 0;
}

5.赋值重载

默认情况下,C++会为类生成默认的赋值运算符,但如果类包含指针或资源,可能需要自定义赋值运算符以避免浅拷贝问题。

要重载赋值运算符,你需要在类中定义一个名为 operator= 的特殊成员函数。

格式:

参数类型:const T&,传递引用可以提高传参效率

返回值类型T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

检测是否自己给自己赋值

返回*this :要复合连续赋值的含义

示例

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;
    }

    Date &operator=(const Date &d) {
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }

        return *this;
    }

private:
    int _year;
    int _month;
    int _day;
};

特征:

1.赋值运算符只能重载成类的成员函数不能重载成全局函数

class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }
    int _year;
    int _month;
    int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date &operator=(Date &left, const Date &right) {
    if (&left != &right) {
        left._year = right._year;
        left._month = right._month;
        left._day = right._day;
    }
    return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

原因:

赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

3.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?

这个问题跟拷贝构造函数一样,涉及到深拷贝问题。

如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

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

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

相关文章

kali安装使用dirsearch扫描文件

apt-get install dirsearch使用 irsearch -u [协议://域名 | ip端口]可以在这里面看到扫出来的结果 利用 发现了 通过githack将源码下来 知乎&#xff1a;AJEST安全实验室

[技术杂谈]MobaXterm中文乱码编码问题一种解决方法

今日使用mobaxterm连接树莓派发现安装出现乱码&#xff0c;看不清文字是什么。最最简单方式是ssh设置终端字体&#xff0c;具体步骤为&#xff1a; 1. 右键会话&#xff0c;点击编辑会话 2.在以下画面点击终端字体设置 3.选择编码&#xff1a;GBK或者ISO-8859-1

Git 版本控制系统

git相关代码 0、清屏幕&#xff1a;clear 1、查看版本号 git -v2、暂存、更改、提交 3、当前项目下暂存区中有哪些文件 git ls-files4、查看文件状态 git status -s5、暂时存储&#xff0c;可以临时恢复代码内容 git restore 目标文件 //&#xff08;注意&#xff1a;完全…

mysql处理json格式的字段,一文搞懂mysql解析json数据

文章目录 一、概述1、什么是JSON2、MySQL的JSON3、varchar、text、json类型字段的区别 二、JSON类型的创建1、建表指定2、修改字段 三、JSON类型的插入1、字符串直接插入2、JSON_ARRAY()函数插入数组3、JSON_OBJECT()函数插入对象4、JSON_ARRAYAGG()和JSON_OBJECTAGG()将查询结…

南卡再次发布新款OE-CC开放式耳机,被誉为年度开放式耳机百元标杆!

​国内著名声学品牌南卡&#xff0c;在近日又推出一款全新开放式耳机OE-CC&#xff0c;致力打造舒适、安全、健康听歌新体验&#xff0c;该耳机以其卓越的音质和令人惊叹的性价比而备受瞩目。再次证明了南卡在开放式音频领域的领先地位&#xff0c;被誉为年度开放式耳机百元标杆…

暄桐展览| 我们桐学有自己的习作展(1)

林曦老师《从书法之美到生活之美》的第五阶课程《静定的滋养2021》已告一段落。570天的用功&#xff0c;桐学们的技艺都有了水涨船高的进益。      无论书法课&#xff08;全阶和五阶&#xff09;还是国画课&#xff0c;暄桐都有一套完整系统的教学体系&#xff0c;也会在桐…

攻防世界-reverse-666

1. 题目描述 下载附件&#xff0c;发现是一个可执行文件 2. 思路分析 先逆向分析下源码 整个程序的逻辑还是比较简单的&#xff0c;输入key&#xff0c;对key进行encode&#xff0c;如果加密后的字符串和指定字符串相同&#xff0c;那么key就是我们需要的flag&#xff0c;…

AntDB-M的审计功能

数据库的审计功能是指对数据库访问行为进行监管&#xff0c;记录数据库里面发生了什么操作&#xff0c;是数据库系统安全功能的组成部分。 AntDB-M的审计功能关注客户端的连接信息&#xff0c;比如&#xff1a;用户名和主机地址、客户端发送的SQL语句、SQL执行访问的对象、修改…

W6100-EVB-PICO进行UDP组播数据回环测试(九)

前言 上一章我们用我们的开发板作为UDP客户端连接服务器进行数据回环测试&#xff0c;那么本章我们进行UDP组播数据回环测试。 什么是UDP组播&#xff1f; 组播是主机间一对多的通讯模式&#xff0c; 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将…

打破数据孤岛!时序数据库 TDengine 与创意物联感知平台完成兼容性互认

新型物联网实现良好建设的第一要务就是打破信息孤岛&#xff0c;将数据汇聚在平台统一处理&#xff0c;实现数据共享&#xff0c;放大物联终端的行业价值&#xff0c;实现系统开放性&#xff0c;以此营造丰富的行业应用环境。在此背景下&#xff0c;物联感知平台应运而生&#…

IDEA书签使用

在你想要创建书签的地方按F11就能创建书签 在行那里按这一行前面就会打个√&#xff0c;再按一下F11他又会删除 当然也可以按CtrlF11自己定义是√还是字母或者是数字 也可以在文件上加书签 想要快速定位到书签&#xff0c;按ShiftF11查看书签&#xff0c;双击就定位到你这个…

java八股文面试[数据结构]——ConcurrentHashMap原理

HashMap不是线程安全&#xff1a; 在并发环境下&#xff0c;可能会形成环状链表&#xff08;扩容时可能造成&#xff0c;具体原因自行百度google或查看源码分析&#xff09;&#xff0c;导致get操作时&#xff0c;cpu空转&#xff0c;所以&#xff0c;在并发环境中使用HashMap是…

python 模块xlwt 写入.xls文件

Python操作Excel的模块有很多&#xff0c;并且各有优劣&#xff0c;不同模块支持的操作和文件类型也有不同。下面是各个模块的支持情况&#xff1a; xlrd&#xff1a;xlrd 读取.xls文件xlwings&#xff1a;xlwings 读取写入Excel文件openpyxl&#xff1a;openpyxl 读取写入.xl…

【linux】2 make/Makefile和gitee

文章目录 一、Linux项目自动化构建工具-make/Makefile1.1 背景1.2 实例代码1.3 原理1.4 项目清理 二、linux下第一个小程序-进度条2.1 行缓冲区2.2 进度条 三、git以及gitee总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;" 一…

十一、pikachu之XXE

文章目录 1、XXE漏洞概述1.1 XML定义1.2 XML结果1.2 XML文档格式1.2.1 DTD内部文档声明1.2.2 DTD外部文档声明1.2.3 DTD声明 2、实战 1、XXE漏洞概述 XXE(xml external entity injection)&#xff1a;即xml外部实体注入漏洞&#xff0c;也就是说服务端接收和解析了来自用户端的…

我裸辞去面试大公司python岗位了!

最近换工作了&#xff0c;坐标上海&#xff0c;裸辞&#xff0c;之前早有前辈们说过&#xff0c;“裸辞一时爽,一直裸辞一直爽”&#xff0c;这话一点不假&#xff0c;裸辞你要面临没有收入来源&#xff0c;但是每天眼睁睁看着各种花销不断支出的煎熬&#xff0c;我主要是觉得一…

高忆管理:k线图24种经典图解?

K线图是股市技能剖析中的常用工具&#xff0c;它可以描绘出一段时间内股票或指数的开盘价、收盘价、最高价和最低价等信息&#xff0c;为投资者提供了重要的信息。在这篇文章中&#xff0c;咱们将从多个角度剖析24种经典的K线图&#xff0c;协助读者深入了解和应用它们。 榜首&…

stm32基于HAL库驱动外部SPI flash制作虚拟U盘

stm32基于HAL库驱动外部SPI flash制作虚拟U盘 &#x1f4cc;参考文章&#xff1a;https://xiaozhuanlan.com/topic/6058234791&#x1f39e;实现效果演示&#xff1a; &#x1f516;上图中的读到的FLASH_ID所指的是针对不同容量&#xff0c;所对应的ID。 //W25X/Q不同容量对应…

(五)k8s实战-配置管理

一、ConfigMap 使用 kubectl create configmap -h 查看示例&#xff0c;构建 configmap 对象 1) 基于文件夹&#xff0c;加载文件夹下所有配置文件&#xff0c;创建 kubectl create configmap <configmapName> --from-file<dirPath>2) 指定配置文件&#xff0c;创…

vue3的hooks你可以了解一下

更详细的hooks了解参考这个大佬的文章&#xff1a;掘金&#xff1a;Hooks和Mixins之间的区别 刚开始我简单看了几篇文章感觉Hooks这个东西很普通&#xff0c;甚至感觉还不如vue2的mixin好用。还有export import 感觉和普通定义一个utils文件使用没什么区别。但是Hooks这个东西肯…