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

news2024/9/20 22:55:38

目录

引言

类的默认成员函数

构造函数

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的副本d2
	Date d2(d1);	//拷贝构造

	// 使用拷贝初始化(也是拷贝构造的一种形式)创建d1的副本d3
	Date 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的副本d2
	Date d2(d1);	//拷贝构造

	// 使用拷贝初始化(也是拷贝构造的一种形式)创建d1的副本d3
	Date 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.coloradmin.cn/o/2115923.html

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

相关文章

食家巷桃酥:一口酥脆,回味悠长

在众多的传统糕点中&#xff0c;食家巷桃酥以其独特的魅力脱颖而出&#xff0c;成为了许多人心中的美味记忆。初见食家巷桃酥&#xff0c;那金黄的色泽便让人食欲大增。每一块桃酥都像是一件小小的艺术品&#xff0c;散发着诱人的香气。它的外形饱满&#xff0c;边缘微微隆起&a…

JVM 调优篇1 类的加载器与加载过程

一 基本知识 1.1 JIT&AOT JIT: Just Time compilation 即时编译器 在程序运行时将字节码或中间表示转换为机器代码。 AOT: Ahead of Tmie Compilation &#xff1a; 预编译 在程序运行之前将高级语言代码完全编译成机器代码。 1.2 字面量和符号引用* 字面量&am…

安卓下载工具箱_3.8.1/去浏览器跳转登录就是会员

下载工具箱app是一款支持56种下载协议的安卓万能下载工具,下载工具箱最新版主要提供网络视频解析下载,资源嗅探下载,以及本地视频扫描提取功能,同时提供不常见的视频编辑功能,例如:智能清洗,高帧率/码率等 链接: https://pan.baidu.com/s/1pW0CgmGv9bmzGkBA4rV2oQ?pwdxdby …

群晖NAS安装alist

官方文档&#xff1a;https://alist.nn.ci/zh NAS参考&#xff1a;https://post.smzdm.com/p/an9zpvev/ Windows安装&#xff1a;alist-windows-amd64.zip 手机参考&#xff1a;https://studylessshape.github.io/post/note/alist-install-for-android/ 质感文件可以不用安&…

Science|癌症中三级淋巴结构的免疫调节作用与治疗潜力|顶刊精析·24-09-08

小罗碎碎念 Science文献精析 今天精析的这一篇综述&#xff0c;于2022-01-07发表于Science&#xff0c;主要讨论了癌症中的三级淋巴结构&#xff08;Tertiary Lymphoid Structures, TLS&#xff09;及其在肿瘤免疫反应中的作用。 作者类型作者姓名单位名称&#xff08;中文&…

前端学习笔记-Web APls篇-04

Dom节点&移动端滑动 1.日期对象 日期对象:用来表示时间的对象作用:可以得到当前系统时间 1.1实例化 在代码中发现了 new 关键字时&#xff0c;一般将这个操作称为实例化 创建一个时间对象并获取时间 获得当前时间 获得指定时间 1.2时间对象方法 使用场景&#xff1a…

图的广度优先搜索(BFS)算法与邻接矩阵表示

图的广度优先搜索(BFS)算法与邻接矩阵表示 1. 图的表示2. 广度优先搜索(BFS)BFS 算法步骤:3. 使用邻接矩阵的 BFS 实现4. 运行时间分析时间复杂度:空间复杂度:5. BFS 使用邻接列表与邻接矩阵的比较BFS 在邻接列表上的运行时间:6. 结论在计算机科学中,图是一种重要的数…

Mybatis-设计模式总结

1、Builder模式 Builder模式的定义是“将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。”&#xff0c;它属于创建类模式&#xff0c;一般来说&#xff0c;如果一个对象的构建比较复杂&#xff0c;超出了构造函数所能包含的范围&#x…

Spring05——注解开发定义bean、Spring纯注解开发模式

19-注解开发定义bean Spring的IOC/DI对应的配置开发前面的内容已经讲解完成&#xff0c;但是使用起来相对来说还是比较复杂的&#xff0c;复杂的地方在配置文件。 Spring到底是如何简化代码开发的呢? 要想真正简化开发&#xff0c;就需要用到Spring的注解开发&#xff0c;Spr…

CTFHub技能树-信息泄露-SVN泄漏

目录 Git与SVN的区别 漏洞产生的原因 漏洞危害 修复建议 工具下载 解题过程 当开发人员使用 SVN 进行版本控制&#xff0c;对站点自动部署。如果配置不当,可能会将.svn文件夹直接部署到线上环境。这就引起了 SVN 泄露漏洞。 Git与SVN的区别 Git 是分布式版本控制系统&a…

vulhub命令执行/代码执行漏洞

一、Thinkphp5 2-rce远程代码执行漏洞 1.打开靶场环境 2、访问网页 3、构造payload 172.16.1.18:8080?s/Index/index/L/${phpinfo()} 4、写入一句话木马&#xff0c;使用蚁剑连接 172.16.1.18:8080/?s/Index/index/name/${print(eval($_POST[cmd]))} 二、Couchdb 任意命令…

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理&#xff0c;在有一定基础认识的前提下&#xff0c;通过阅读kubelet源码&#xff0c;对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管…

【云原生】docker 部署 Doris 数据库使用详解

目录 一、前言 二、数据分析概述 2.1 什么是数据分析 2.2 数据分析目的和意义 2.3 数据分析常用的技术和工具 2.3.1 编程语言 2.3.2 数据处理和分析库 2.3.3 数据可视化工具 2.3.4 数据库系统 2.3.5大数据处理框架 2.3.6 云服务和平台 2.3.7 其他工具 三、Doris介绍…

Hash Table、HashMap、HashSet学习

文章目录 前言Hash Table&#xff08;散列表&#xff09;基本概念散列函数散列冲突&#xff08;哈希碰撞&#xff09;拉链法红黑树时间复杂度分析 HashMap基础方法使用基本的增删改查其他的方法 实现原理 HashSet基础操作去重原理 前言 本文用于介绍关于Hash Table、HashMap、…

UnLua调用C++函数

一、UnLua调用C全局静态函数 1、新建C类MyLuaUtils&#xff0c;继承BlueprintFunctionLibrary,实现全局静态函数GetInt。 MyLuaUtils.h UCLASS() class LUASHOOTING_API UMyLuaUtils : public UBlueprintFunctionLibrary {GENERATED_BODY()UFUNCTION(BlueprintCallable)static…

leetcode hot100_part17_技巧篇

题目 136.只出现一次的数字 结合题目给的数据特征&#xff0c;使用位运算中的异或^&#xff1b;异或的结果很好记&#xff0c;相互不同就是1&#xff0c;相同就是0&#xff1b;同或一样的 169.多数元素 直接排序了 后面那几个方法不看了&#xff0c;追求效率可以再看&#…

前端工程化详解 包管理工具

前端工程化详解 & 包管理工具 1、工程化体系介绍1.1、 什么是前端工程化1.2、 前端工程化发展 2、脚手架能力2.1 准备阶段2.2 开发阶段2.3 发布流程 3、npm能力3.1 剖析package.json3.1.1 必备属性3.1.2 描述信息3.1.3 依赖配置3.1.4 协议3.1.5 目录&文件相关3.1.5.1 程…

MATLAB基础语法知识

环境的配置等等就不写了&#xff0c;网上还是有很多资源可以找&#xff0c;而且正版的要付费&#xff0c;我也是看的网上的搞定的。 一&#xff0c;初识MATLAB 1.1 MATLAB的优势 不需要过多了解各种数值计算方法的具体细节和计算公式&#xff0c;也不需要繁琐的底层编程。可…

Untiy TTF转换为SDF

Untiy TTF转换为SDF 原因 下载的字体是TTF格式&#xff0c;但是TMP使用的是SDF格式&#xff0c;不支持TTF&#xff0c;需要转换网络没有检索到TTF转SDF的教程&#xff0c;可能是太简单了&#xff0c;自己记录一下吧 Unity内转换即可 在Asset中找到自己的TTF右键点击TTF&…

C++入门基础篇

引言 说到编程语言常常听到的就是C语言C Java 。C语言是面向过程的&#xff0c;C是和Java是面向对象的&#xff0c;那么什么是面向对象呢&#xff1f;什么又是面向过程呢&#xff1f;C是什么&#xff1f;封装、继承、多态是什么&#xff1f;且听我絮絮叨叨。 C入门基础 1.命名…