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

news2024/12/28 2:07:05

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/928565.html

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

相关文章

【深度学习_TensorFlow】过拟合

写在前面 过拟合与欠拟合 欠拟合&#xff1a; 是指在模型学习能力较弱&#xff0c;而数据复杂度较高的情况下&#xff0c;模型无法学习到数据集中的“一般规律”&#xff0c;因而导致泛化能力弱。此时&#xff0c;算法在训练集上表现一般&#xff0c;但在测试集上表现较差&…

【Vue3】Vuex状态管理

Vuex状态管理 附&#xff1a;eslin中定义了不使用会报错认识应用状态管理什么是状态管理旧模式下的状态管理 Vuex的基本使用安装Vuex单一状态树 State使用vuex的方式&#xff08;Vue3&#xff09;mapState辅助函数&#xff08;Vue2&#xff09; Mutationsmutations的使用和携带…

前向传播与反向传播涉及到的知识点

目录 引入&#xff1a; 一、神经网络&#xff08;NN&#xff09;复杂度 空间复杂度 时间复杂度 二、学习率 三、指数衰减学习率——解决lr的选择 1.总结经验 2.公式引入 四、激活函数 1.简单模型&#xff1a;最初的理解 2.激活函数MP模型 3.优秀的激活函数的特点 4.…

广州华锐视点:VR技术在航天教育的应用优势

VR技术在航天教育中的应用可以带来许多创新和优势&#xff0c;为学生和公众提供更加生动、沉浸式的航天科学学习体验。以下是VR技术在航天教育中的几个主要应用方面&#xff1a; 航天探索模拟&#xff1a;VR技术可以模拟航天探索的场景&#xff0c;让学生和公众身临其境地感受航…

FFmpeg<第一篇>:环境配置

1、官网地址 http://ffmpeg.org/download.html2、linux下载ffmpeg 下载&#xff1a; wget https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2解压&#xff1a; tar xvf ffmpeg-snapshot.tar.bz23、FFmpeg ./configure编译参数汇总 解压 ffmpeg-snapshot.tar.bz2 之后&…

Vulnhub: DriftingBlues: 1靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.215 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.215 80端口首页源码 访问noteforkingfish.txt&#xff0c;发现为Ook!加密的密文 解密后提示需要用户eric和修改hosts文件&…

【Python编程】将同一种图片分类到同一文件夹中

一、数据结构如下&#xff1a; 二、编程工具&#xff1a;Jupyter-Notebook 三、代码&#xff1a; import os import cv2 import shutilpath0os.getcwd()\\apple\\RGB path1os.getcwd()\\apple\\tof_confidence path2os.getcwd()\\apple\\tof_depth path3os.getcwd()\\apple\\…

教你写出高质量函数,简单又实用

在编写函数时&#xff0c;程序员通常需要遵循以下步骤进行&#xff1a; 1、确定最佳的设计逻辑是编写函数时应该考虑的重要因素。这些因素包括设计合理的数据结构、算法和逻辑封装&#xff0c;并且还要考虑到用户的安全因素。挑战在于确保所设计的方案既满足客户需求&#xff…

药品不良反应数据库有哪些?记住这个就够用了

药品不良反应数据库的作用是收集、记录和分析药品使用过程中可能出现的不良反应信息。这些数据库通常由医药监管机构、医药数据公司、医疗机构和科研机构等建立和维护&#xff0c;在监测和评估药品的安全性、发现和确认新的不良反应、支持药品监管决策、提供医学信息和警示等方…

【局部活动轮廓】使用水平集方法实现局部活动轮廓方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

会员管理系统实战开发教程04-会员开卡

我们已经用3篇篇幅介绍了会员管理的功能&#xff0c;接着就要开发会员的业务。通常我们开通会员之后需要给会员开通会员卡&#xff0c;一个会员可以有多张会员卡。 在数据源设计的时候&#xff0c;像这种一个会员有多张会员卡的&#xff0c;我们称之为一对多的关系&#xff0c…

Sentinel流量控制与熔断降级

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

Levels - 场景参考:山脉景观(Landscape Mountains)

一些从前的笔记的归档&#xff0c;记录了一些UE4资产的相关信息&#xff1b; 山脉景观&#xff08;Landscape Mountains&#xff09;&#xff1a; 项目的地形材质比较复杂&#xff0c;有几个比较重要的大效果功能&#xff0c;一个是沉积岩的效果&#xff1a; 沉积岩效果的功能…

聊聊NFC技术

目录 1.什么是NFC 2.NFC技术的原理 3.NFC技术的应用 4.NFC技术对生活的影响 1.什么是NFC NFC&#xff08;Near Field Communication&#xff09;是一种短距离无线通信技术&#xff0c;可在10厘米以内进行点对点的数据传输。它是基于RFID&#xff08;Radio Frequency Identif…

基于OpenCV实战(基础知识二)

目录 简介 1.ROI区域 2.边界填充 3.数值计算 4.图像融合 简介 OpenCV是一个流行的开源计算机视觉库&#xff0c;由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包&#xff0c;可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和计算机视觉应用。Ope…

IDEA 出现问题:.gitgnore忽略文件失效解决方案

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1f3c6; ❤️技术活&#xff0c;该赏 ❤️点赞…

光惯混合动作捕捉系统:开启空间定位全新时代

在当今的技术前沿&#xff0c;光惯混合动作捕捉系统正在崭露头角&#xff0c;成为定位技术的新标杆&#xff0c;更是一项生活中不可或缺的实用工具。它是一个集光学与惯性测量技术于一体的全新定位解决方案&#xff0c;巧妙地融合了光学与惯性测量的优势&#xff0c;以其突破性…

Mars3d的历史版本api文档下载地址

说明&#xff1a; 1.我们一直在进步迭代新功能升级产品打造服务&#xff0c;但是鉴于目前依然有很多老的项目无法升级&#xff0c;需要维护。现提供历史版本的api文档供大家学习参考 2.建议跟随官网同步升级新版本&#xff0c;新功能更多&#xff0c;更值得大家快速上手。 3…

【速成】蓝桥杯嵌入式省一教程:(十一)UART串口通信

与I2C、SPI一样&#xff0c;UART是一种通信协议&#xff0c;设备之间依靠Rx(Receive)与Tx(Transmit)两条线进行数据传输。一个单片机通常内置有多个UART&#xff0c;而这些UART通常都与单片机上的USB接口连接在一起&#xff0c;因此只需要将单片机通过数据线与电脑相连&#xf…

CK-GW06-E03与汇川PLC的EtherNet/IP通信

准备阶段&#xff1a; CK-GWO6-E03网关POE交换机网线汇川PLC编程软件汇川AC801-0221-U0R0型号PLC 1.打开汇川PLC编程软件lnoProShop(V1.6.2)SP2 新建工程&#xff0c;选择对应的PLC型号&#xff0c;编程语言选择为“结构化文本&#xff08;ST&#xff09;语言”&#xff0c;然…