C++初阶(五)--类和对象(中)--默认成员函数

目录

一、默认成员函数(Default Member Functions)

 二、构造函数( Constructor)

1.构造函数的基本概念

2.构造函数的特征

3.构造函数的使用

无参构造函数 和 带参构造函数

注意事项:

4.默认构造函数

隐式生成的默认构造函数:

默认构造函数的特性 :

注意事项:

5.构造函数的特性

三、析构函数(Destructor)

1.析构函数的基本概念

2.析构函数的特征

3.析构函数的使用

4.默认析构函数

5.析构顺序问题

6.析构函数特性

四、 拷贝构造函数

1.定义

2.拷贝构造函数的特性

3.拷贝构造函数的用法

对象初始化

函数返回值按值返回

4.拷贝构造函数的深拷贝与浅拷贝

5.引用传参(面试题)


一、默认成员函数(Default Member Functions)

如果一个类中什么成员都没有,简称为空类
空类中真的什么都没有吗?并不是,任何类在什么都不写时编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

class Date {};

 二、构造函数( Constructor)

1.构造函数的基本概念

构造函数是一种特殊的成员函数,它在对象创建时自动调用,用于初始化对象的成员变量。构造函数的名字必须与类名完全相同,并且没有返回类型。

注意:构造函数只是初始化对象,不开辟空间创建对象。

2.构造函数的特征

构造函数是特殊的成员函数,主要特征如下:

  • 构造函数的函数名和类名是相同的 (比如类名是 Date,构造函数名就是 Date)
  • 构造函数无返回值 (它不具有返回类型,因此不能直接返回值)
  • 构造函数支持重载 (仔细看下面的例子)
  • 会在对象实例化时自动调用对象定义出来。

3.构造函数的使用

 无参构造函数 和 带参构造函数

#include <iostream>  
using namespace std;

class MyClass 
{
public:

    MyClass() 
    {
         cout << "无参构造函数被调用" << endl;
    }

    MyClass(int val)
    {
        value = val;
        cout << "带参数的构造函数被调用,值为: " << value << endl;
    }
private:
    int value;

};

int main() 
{
    MyClass obj1; // 调用无参构造函数,注意后面没有括号哦~  
    MyClass obj2(10); // 调用带参数的构造函数  
    return 0;
}

运行结果:

解读:不给参数时就会调用 无参构造函数,给参数则会调用 带参构造函数。 

注意事项:

 构造函数是特殊的,不是常规的成员函数,不能直接调

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

int main(void)
{
    Date d1;
    d1.Date(); // 不能这么去调,构造函数是特殊的,不是常规的成员函数!

    return 0;
}

 

 如果通过无参构造函数创建对象,对象后面不用跟括号,否则就成了函数声明。 

#include <iostream>  
using namespace std;

class MyClass 
{
public:

    MyClass() 
    {
         cout << "无参构造函数被调用" << endl;
    }

    MyClass(int val)
    {
        value = val;
        cout << "带参数的构造函数被调用,值为: " << value << endl;
    }

    void Print()
            {
                cout << "lllll" << endl;
            }

private:
    int value;

};

int main() 
{
    MyClass obj1(); // 调用无参构造函数  
    obj1.Print();
    MyClass obj2(10); // 调用带参数的构造函数  
    obj2.Print();
    return 0;
}

 带上括号:

不带括号:

 这里如果调用带参构造函数,我们需要传递三个参数(这里我们没设缺省) 。 

 如果你没有自己定义构造函数(类中未显式定义),C++ 编译器会自动生成一个无参的默认构造函数。当然,如果你自己定义了,编译器就不会帮你生成了。 

class Date 
{
public:
    //如果用户显式定义了构造函数,编译器将不再生成
   /* Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }*/
    void Print()
    {
        printf("%d-%d-%d\n", _year, _month, _day);
    }

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

int main(void)
{
    Date d1;
    d1.Print();
    return 0;
}

 输出结果:

没有定义构造函数,对象也可以创建成功,因此此处调用的是 编译器默认生成的构造函数。

接下来,我们来讲一下默认构造函数。 

4. 默认构造函数

概念:

默认构造函数是一种没有参数或参数全部有默认值的构造函数。当没有为类显式定义任何构造函数时,编译器会自动生成一个默认的无参构造函数。这个构造函数通常不会执行任何初始化操作,但它是存在的,可以用于创建类的对象。

无参构造函数、全缺省构造函数、自动生成的构造函数都被称为 默认构造函数

并且 默认构造函数只能有一个!

隐式生成的默认构造函数:

#include <iostream>  
using namespace std;

class MyClass 
{
public:
    // 没有显式定义任何构造函数  
};

int main() 
{
    MyClass obj; // 调用隐式生成的默认构造函数  
    return 0;
}

在这个例子中,MyClass没有显式定义任何构造函数,因此编译器会为其生成一个默认的无参构造函数。在main函数中创建MyClass类型的对象obj时,会调用这个隐式生成的默认构造函数。

默认构造函数的特性 :

  1. 无参或全默认参数:默认构造函数要么没有参数,要么所有参数都有默认值。
  2. 自动生成:当类中没有显式定义任何构造函数时,编译器会自动生成一个默认构造函数。
  3. 初始化成员变量:虽然隐式生成的默认构造函数不会执行任何自定义的初始化操作,但它仍然会调用成员变量的默认构造函数(如果成员变量是类的对象)。

成员变量的默认构造函数调用 :代码示例

#include <iostream>  
using namespace std;

class Member 
{
public:
    Member() 
    {
        cout << "Member默认构造函数被调用" << endl;
    }
};

class MyClass 
{
private:
    Member member;
public:
    // 没有显式定义任何构造函数  
};

int main() 
{
    MyClass obj; // 调用隐式生成的默认构造函数,同时调用Member的默认构造函数  
    return 0;
}

 输出:

在这个例子中,MyClass包含了一个Member类型的成员变量。当创建MyClass类型的对象时,隐式生成的默认构造函数会调用Member的默认构造函数来初始化member。 

注意事项:

语法上无参和全缺省可以同时存在,但如果同时存在会引发二义性:

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

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

int main(void)
{
    Date d1;
    return 0;
}

5.构造函数的特性

通过刚才的讲解我们知道了任何一个类的默认构造函数,只有三种:

  • 无参的构造函数
  • 全缺省的构造函数
  • 我们不写,编译器自己生成的构造函数

如果你没有自己定义构造函数(类中未显式定义),C++ 编译器会自动生成一个无参的默认构造函数。当然,如果你自己定义了,编译器就不会帮你生成了。

代码:

class Date 
{
public:
    // 让编译器自己生成一个
    void Print() 
    {
        printf("%d-%d-%d\n", _year, _month, _day);
    }

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

int main(void)
{
    Date d1;  // 这里调用的是默认生成的无参的构造函数
    d1.Print();

    return 0;
}

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

解答:

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

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

 运行结果:

对于用户定义的类型(如类、结构体),如果它们没有定义自己的构造函数,编译器会生成一个默认的无参数构造函数(也称为“合成默认构造函数”)。这个构造函数会按成员变量在类中声明的顺序调用它们的默认构造函数(如果有的话)。

代码示例:

#include <iostream>  
#include <string>  
  
class MemberClass 
{  
public:  
    MemberClass() 
    {  
        std::cout << "MemberClass default constructor called" << std::endl;  
    }  
};  
  
class MyClass 
{  
public:  
    MemberClass member; // 用户定义类型成员变量  
    // MyClass没有定义自己的构造函数,所以编译器会生成一个默认构造函数  
};  
  
int main() 
{  
    MyClass obj; // 调用MyClass的默认构造函数,它会调用MemberClass的默认构造函数  
    return 0;  
}

 在上面的代码中,当MyClass的对象obj被创建时,它的成员变量member会被初始化,这会调用MemberClass的默认构造函数。

注意: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
{
public:
		void Print()
		{
			cout << _year << "-" << _month << "-" << _day << endl;
		}
private:
	// 基本类型(内置类型)
	int _year = 2024;
	int _month = 12;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

注意:构造函数遵循先定义先构造原则。 

三、析构函数(Destructor)

1.析构函数的基本概念

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

对象在销毁时会自动调用析构函数,完成对象的一些资源清理工作。

2. 析构函数的特征

1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

class MyClass 
{
public:
    ~MyClass() {
        // 释放资源的代码  
        std::cout << "Destructor called!" << std::endl;
    }
};

在上面的例子中,~MyClass() 是 MyClass 类的析构函数。

3.析构函数的使用

在我们前面学习栈的实现的时候,动态开辟后的内存需要手动进行释放,但是我们很有可能会忘记释放,因为即使不释放空间,程序也可以正常运行。如果使用析构函数,那么会自动调用,能够有效减少忘记释放的可能。 

#include<iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 4)//全缺省默认构造函数
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc fail!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)//入栈
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()//析构函数
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
void TestStack()
{
	Stack s;
	s.Push(1);
	s.Push(2);
}

我们在设置栈的构造函数时,定义容量 capacity 时利用缺省参数默认给个4的容量,这样用的时候默认就是4,如果不想要4可以自己传。

如此一来,就可以保证了栈被定义出来就一定被初始化,用完后会自动销毁。以后就不会有忘记调用 destroy 而导致内存泄露的惨案了,这里的析构函数就可以充当销毁的作用。

4.默认析构函数

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

#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;
};
int main()
{
	Date d;
	return 0;
}
// 程序运行结束后输出:~Time()

在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数? 

解答:

main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。

 5.析构顺序问题

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

	~Date()
	{
		cout << "~Date()->" << _year << endl;
	}
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
};
void func()
{
	Date d3(3);//局部对象
	static Date d4(4);//局部的静态
}
Date d5(5);//全局对象
static Date d7(7);//全局静态
Date d6(6);
static Date d8(8);

// 局部对象(后定义先析构) -》 局部的静态 -》全局对象(后定义先析构)
int main()
{
	Date d1(1);//局部对象 
	Date d2(2);
	func();

	return 0;
}

析构函数执行顺序:局部对象(后定义先析构) -》 局部的静态 -》全局对象(后定义先析构) 

6.析构函数特性

📌 如果不自己写构造函数,让编译器自动生成,那么这个自动生成的 默认构造函数:

对于 "内置类型" 的成员变量:不会做初始化处理。
对于 "自定义类型" 的成员变量:会调用它的默认构造函数(不用参数就可以调的)初始化,如果没有默认构造函数(不用参数就可以调用的构造函数)就会报错!
 而我们的析构函数也是这样的。

📌 如果我们不自己写析构函数,让编译器自动生成,那么这个 默认析构函数:

对于 "内置类型" 的成员变量:不作处理 。
对于 "自定义类型" 的成员变量:会调用它对应的析构函数  。

四、 拷贝构造函数

在C++编程中,拷贝构造函数是一个非常重要的概念,它用于创建一个新对象,作为现有对象的副本。拷贝构造函数在对象复制、函数参数传递、返回值以及容器操作等场景中扮演着关键角色。

1.定义

拷贝构造函数是一种特殊的构造函数,它接受一个同类型的对象作为参数,并用于初始化新创建的对象。其语法如下:

类名(const 类名& 形参);
ClassName(const ClassName& other);

这里,ClassName 是类的名称,other 是传递给拷贝构造函数的参数,它是一个常量引用,以避免不必要的对象复制。

2.拷贝构造函数的特性

它也是一个特殊的成员函数,所以他符合构造函数的一些特性:

① 拷贝构造函数是构造函数的一个重载形式。函数名和类名相同,没有返回值。

② 拷贝构造函数的参数只有一个,并且 必须要使用引用传参!

3.拷贝构造函数的用法

对象初始化

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

    /* Date d2(d1); */
    Date(Date& d) 
    {         // 这里要用引用,否则就会无穷递归下去
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    void Print() 
    {
        printf("%d-%d-%d\n", _year, _month, _day);
    }

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

int main(void)
{
    Date d1(2024, 10, 9);
    Date d2(d1);          // 拷贝复制

    // 看看拷贝成功没
    d1.Print();
    d2.Print();

    return 0;
}

 运行结果:

函数返回值按值返回

class Date {
public:
    Date(int year = 0, 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() {
        std::cout << _year << "-" << _month << "-" << _day << std::endl;
    }

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

Date Birthday()
{
    Date time(2004, 10, 29);
    return time;// 可能会调用拷贝构造函数来返回对象的副本

}

int main()
{
    Date T = Birthday();// 拷贝构造函数被调用,用返回的对象副本初始化 T
    return 0;
}

在这个例子中,Birthday函数返回一个对象,当这个对象被返回并用于初始化 T 时,可能会调用拷贝构造函数来创建 T。 

4.拷贝构造函数的深拷贝与浅拷贝

除了上面提到的基本用法,拷贝构造函数还涉及到深拷贝和浅拷贝的概念。

浅拷贝

默认情况下,C++ 的拷贝构造函数执行的是浅拷贝。这意味着它只是简单地复制对象的成员变量值。如果成员变量是基本类型(如 intchar 等),那么浅拷贝没有问题。但如果成员变量是指针或引用类型,浅拷贝就可能会导致问题。

class ShallowPerson {
private:
    std::string* namePtr;
    int age;

public:
    ShallowPerson(std::string n, int a) 
    {
        namePtr = new std::string(n);
        age = a;
    }

    // 浅拷贝构造函数(默认情况下的拷贝构造函数行为)
    ShallowPerson(const ShallowPerson& other) 
    {
        namePtr = other.namePtr;
        age = other.age;
        std::cout << "浅拷贝构造函数被调用,可能会有问题哦!😕" << std::endl;
    }

    ~ShallowPerson() 
    {
        delete namePtr;
    }

    void introduce() 
    {
        std::cout << "我叫 " << *namePtr << ",我 " << age << " 岁啦!" << std::endl;
    }
};

int main() 
{
    ShallowPerson sp1("酒鬼猿", 20);
    ShallowPerson sp2 = sp1;

    sp1.introduce();
    sp2.introduce();

    // 当其中一个对象被销毁时,可能会导致另一个对象出现问题
    return 0;
}

 

 在这个例子中,浅拷贝只是复制了指针的值,而不是复制指针所指向的内容。这就导致两个对象的指针指向了同一块内存。当其中一个对象被销毁并释放内存时,另一个对象的指针就变成了悬空指针,再使用这个指针就会导致未定义的行为。

深拷贝

为了解决浅拷贝的问题,我们需要实现深拷贝。深拷贝会复制对象的所有成员变量,包括指针所指向的内容。

class DeepPerson {
private:
    std::string* namePtr;
    int age;

public:
    DeepPerson(std::string n, int a) {
        namePtr = new std::string(n);
        age = a;
    }

    // 深拷贝构造函数
    DeepPerson(const DeepPerson& other) {
        age = other.age;
        namePtr = new std::string(*(other.namePtr)); // 复制指针所指向的内容
        std::cout << "深拷贝构造函数被调用,一切都好啦!😀" << std::endl;
    }

    ~DeepPerson() {
        delete namePtr;
    }

    void introduce() {
        std::cout << "我叫 " << *namePtr << ",我 " << age << " 岁啦!" << std::endl;
    }
};


int main() {
    DeepPerson dp1("酒鬼猿", 21);
    DeepPerson dp2 = dp1;

    dp1.introduce();
    dp2.introduce();

    // 现在即使一个对象被销毁,另一个对象也不会受到影响
    return 0;
}

 

在深拷贝构造函数中,我们为新对象分配了新的内存,并将原对象指针所指向的内容复制到新内存中。这样,两个对象就拥有了各自独立的内存空间,不会相互影响。 

 5.引用传参(面试题)

面试题: 为什么必须使用引用传参呢?

解答:

当我们使用值传递的方式传递参数时,实际上是将实参复制一份传递给函数的形参。这意味着会创建一个新的对象,这个新对象是实参的副本。

假设我们有一个类 MyClass,如果拷贝构造函数不使用引用传参,如下所示:

class MyClass 
{
public:
    int value;
    MyClass(MyClass obj) 
    { 
        value = obj.value;
    }
};

当我们使用一个已有的对象去初始化另一个新对象时,比如这样的代码:

MyClass obj1;
obj1.value = 10;
MyClass obj2(obj1);

在这个过程中,会调用拷贝构造函数来初始化 obj2。但是,由于拷贝构造函数的参数是按值传递的,所以在调用拷贝构造函数时,会将 obj1 复制一份传递给拷贝构造函数的形参 obj。而这个复制的过程又会触发拷贝构造函数的调用,因为要创建形参 obj 的副本。这样就形成了一个无限递归的过程。

具体来说,当执行 MyClass obj2(obj1); 时:

  1. 首先尝试调用拷贝构造函数来创建 obj2,将 obj1 作为实参传递给拷贝构造函数。
  2. 由于拷贝构造函数是按值传递参数,所以要创建形参 obj 的副本,这就需要再次调用拷贝构造函数。
  3. 第二次调用拷贝构造函数时,又要创建新的形参副本,再次触发拷贝构造函数的调用。
  4. 这个过程会不断重复下去,直到程序耗尽栈空间而崩溃。

为了避免这种无限递归的情况,拷贝构造函数的参数应该使用引用类型,当使用引用传参时,形参只是实参的一个别名,不会创建新的对象副本。这样在调用拷贝构造函数时,就可以直接使用传入的对象进行初始化,而不会触发新的拷贝构造函数调用,从而避免了无限递归的问题。

本篇博客到此结束,如果大家浏览后发现什么错误之处,或者有什么问题,评论区留言哦~

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

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

相关文章

023_Layout_and_Containers_in_Matlab界面布局与容器

容器 基于uifigure进行的图形用户界面设计&#xff0c;可以分为以下几种容器类型&#xff1a; 图窗&#xff1a;uifigure布局&#xff1a;uigridlayout面板&#xff1a;uipanel标签页&#xff1a;uitabgroup、uitab 这几个对象除uifigure外相互可以形成嵌套的关系&#xff0…

理解TypeScript模块-声明-配置

目录 模块化内置类型导入命名空间类型查找内置类型外部定义声明第三方库自定义声明 tsconfig.json文件在Vue项目中应用在React项目中应用 模块化 在TypeScript中最主要使用的模块化方案就是ES Module&#xff0c;先理解 TypeScript 认为什么是一个模块&#xff1a; JavaScript…

【视频生成大模型】 视频生成大模型 THUDM/CogVideoX-2b

【视频生成大模型】 视频生成大模型 THUDM/CogVideoX-2b CogVideoX-2b 模型介绍发布时间模型测试生成的demo视频生成视频限制 运行环境安装运行模型下载开源协议参考 CogVideoX-2b 模型介绍 CogVideoX是 清影 同源的开源版本视频生成模型。 基础信息&#xff1a; 发布时间 2…

VScode远程服务器进行开发(三)

VScode远程服务器进行开发&#xff08;三&#xff09; 1、配置你的~/.ssh/config文件 在家目录中配置 ~/.ssh/config 文件&#xff0c;没有配置过的同学可以自行百度&#xff0c;基本上就是远程服务器的罗列&#xff1b; 同时&#xff0c;在你安装了 Remote Development 后&…

群晖使用Docker搭建NASTool自动化观影工具并实现在线远程管理

文章目录 前言1. 本地搭建Nastool2. nastool基础设置3. 群晖NAS安装内网穿透工具4. 配置公网地址5. 配置固定公网地址 前言 本文主要分享一下如何在群晖NAS中本地部署Nastool&#xff0c;并结合cpolar内网穿透工具&#xff0c;轻松实现公网环境远程管理与访问本地NAS中储存的影…

“网络协议入门:HTTP通信的四大组成部分“

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f439;今日诗词: 春水满四泽&#xff0c;夏云多奇峰&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微…

大话设计模式解读07-模板方法

上篇文章&#xff0c;介绍了《大话设计模式》的第9章——原型模式。 本篇&#xff0c;来介绍《大话设计模式》的第10章——模板方法。并通过C代码实现实例代码的功能。 1 模板方法 模板方法模式&#xff08;TemplateMethod&#xff09;&#xff1a;定义一个操作中的算法的骨架…

【简单版】通过 Window.performance 实现前端页面(性能)监控

1 背景 前端监控系统告警xx接口fetchError 问题&#xff1a;前端监控系统没有更多的错误信息&#xff0c;查询该fetch请求对应的接口日志返回200状态码、无请求异常记录&#xff0c;且后台能查到通过该fetch请求成功发送的数据。那是前端页面的错误还是前端监控系统的问题&…

如何轻松使用pip安装Git仓库中的私有Python模块(使用pip和Git仓库发布和安装私有Python模块)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Git模块 📒📝 Git仓库要求🔖 项目目录结构🔖 文件说明📝 编写setup.py📝 配置MANIFEST.in📝 推送代码到Git仓库📝 使用pip安装模块🔖 使用用户名和密码🔖 使用Personal Access Token (PAT)🔖 示例📝 更…

基于SpringBoot的在线数据平台设计与实现(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Apache Lucene 10 已发布!Lucene 硬件效率改进及其他改进

作者&#xff1a;来自 Elastic Adrien Grand Apache Lucene 10 刚刚发布&#xff0c;重点关注硬件效率&#xff01;查看主要版本亮点。 Apache Lucene 10 终于发布了&#xff01;自 Lucene 9.0&#xff08;于 2021 年 12 月发布&#xff0c;距今已有近 3 年&#xff09;以来&a…

Faster RCNN

经过R-CNN和Fast RCNN的积淀&#xff0c;Ross B. Girshick在2016年提出了新的Faster RCNN&#xff0c;在结构上&#xff0c;Faster RCNN已经将特征抽取(feature extraction)&#xff0c;proposal提取&#xff0c;bounding box regression(rect refine)&#xff0c;classificati…

医疗领域的RAG技术:如何通过知识图谱提升准确性

在医学领域&#xff0c;准确的信息检索和处理至关重要。随着大型语言模型&#xff08;LLMs&#xff09;的兴起&#xff0c;检索增强生成&#xff08;RAG&#xff09;技术在医学信息处理中的应用越来越受到关注。本文将探讨RAG技术在医学领域的应用&#xff0c;特别是如何利用知…

【wpf】07 后端验证及令牌码获取步骤

由于在用wpf开发应用程序时&#xff0c;从后端获取数据需要用到 Authorization 授权的Bearer令牌&#xff0c;而这个令牌的获取需要登录后台进行获取&#xff0c;这里登录时还涉及到的验证码的操作&#xff0c;所以在获取过程中&#xff0c;需要对后台系统进行登录并拿到这个Be…

潮流头像社 1.1 | 打造个性化专属头像的设计工具。

潮流头像社提供了丰富多样的头像制作工具和素材&#xff0c;让用户可以尽情地展示自己的独特魅力。无论是想要换脸特效、滤镜、贴纸、文字还是配饰&#xff0c;用户都可以在这里找到自己喜欢的元素&#xff0c;并且将其融合到自己的头像设计中&#xff0c;创造出一个独一无二的…

87. 多边形轮廓Shape(圆弧)

先掌握上节课关于Shape内容&#xff0c;再继续学习本节课关于Shape圆弧的介绍。 圆弧方法.arc() 圆弧方法.arc()使用方式和原来学习过的圆弧曲线ArcCurve整体相似&#xff0c;区别在于圆心定位方式有差异。 圆弧.arc()参数的圆心坐标是相对当前.currentPoint而言,而不是坐标原…

MFC工控项目实例二十五多媒体定时计时器

承接专栏《MFC工控项目实例二十四模拟量校正值输入》 用多媒体定时器实现0.1秒计时器 1、在SEAL_PRESSUREDlg.h文件中添加代码 #include<MMSystem.h> #pragma comment(lib,"winmm.lib")class CSEAL_PRESSUREDlg : public CDialog { public:CSEAL_PRESSUREDlg(…

【动态规划】【斐波那契数列模型】三步问题、第N个泰波那契数、使用最小花费爬楼梯

模板 算法原理 做动态规划的题目&#xff0c;一般会先创建一个一维数组 dp&#xff0c;称之为 dp表我们想办法填满这个 dp表&#xff0c;里面的某个值就是最终结果 采用动态规划&#xff0c;一般分五步&#xff1a; 状态表示 是什么&#xff1f; dp 表中每一个值所表示的含义…

王爽汇编语言第三版实验3

实验任务 将下面的程序保存为t1.asm&#xff0c;将其生成可执行文件t1.exe 用Vscode编写源程序t1.asm 用脚本一键生成可执行文件t1.exe 成功运行 查看资源管理器&#xff0c;成功生成T1.obj与t1.exe文件‘ 用debug跟踪t1.exe的执行过程&#xff0c;写出每一步执行后&#xff…

基于SSM的大学校医院信息管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着高校规模的不断扩大和师生健康意识的增强&#xff0c;大学校医院面临着日益增长的医疗服务需求。传统的纸质病历、手工预约和药品管理方式已难以满足高效、准确和便捷的服务要求。因此&#xff0c;开发一套基于SSM&#xff…