类内默认函数

news2024/9/22 7:38:08

目录

前言:

1. 构造函数

1.1 概念

1.2 特性

1)

2) 

2. 析构函数

2.1 概念

2.2 特性

3. 拷贝构造

3.1 概念

3.2 特征

 4. 赋值运算符重载

4.1 运算符重载

4.2 赋值运算符重载

5.3 前置++和后置++的重载


前言:

        问:当我们构建了一个类,里面什么都没有写时,也就是空类,它里面是否真的什么都没有?

        答案是否,当任何一个类在没有写东西时,编辑器会为我们生成6个成员函数,至于是那四个请看我下面的介绍。

1. 构造函数

1.1 概念

        我想大家在平时练习代码时,或者在写项目的时候总是会出现一个情况——那就是不喜欢初始化,或者是忘记了初始化,比如在写我们常用的数据结构栈或者队列、链表之类的。我猜测不只是我们认为初始化这种东西麻烦,就连C++的祖师爷也认为这玩意不符合我们喜欢偷懒的习惯,所以才增加了这样一个函数。

         通过上面的讲诉,我想大家也能够推断出构造函数的作用,那就是在我们实例化对象的时候由编辑器自动为我们初始化。当然也并不是说编辑器能够为我们做到任何事情,部分功能还是需要我们自己来实现,这些我后面会讲解。

        下面先看一下没有构造函数实例化一个对象并初始化有多麻烦。

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

1.2 特性

1)

        1. 函数名于类名相同。

        2. 无返回值(字面意思)。

        3. 构造函数支持函数重载。

        4.对象实例化时编译器自动调用对应的构造函数。

class Date
{
public:
	// 1.无参构造函数
	Date()
	{}
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

         上述代码中,我们看到构造函数的写法和我们平时写的函数差异很大,那就是它的名字被固定了,只能和类的名字相同,并且真正的没有返回值,不是void而是没有。

        构造函数被分为了无参构造函数和有参构造函数,因为C++支持函数重载的原因,所以编辑器能够通过我们不同的实例化方式进行初始化。

void TestDate()
{
	Date d1; // 调用无参构造函数
	Date d2(2015, 1, 1); // 调用带参的构造函数

	//Date d3();  //错误
}

        注意看无参构造函数的调用方式和我们的有参构造函数是有区别的,并且最大的区别并不是在带不带参数的原因,而是当我们想要调用无参构造函数的时候,在实例化的后面不能添加()。不仅仅是不能添加,而是我写上去了还会报错。这个时候有的小伙伴可能就有点疑惑了,这不是有病吗?保持一致不好吗?

         事实上我们的祖师爷考虑得还是很齐全的,而是朋友们忘记了一件事情,那就是函数声明的书写方式。

        我们仔细观察一下无参构造函数的调用加上()。

Date d3();                        //错误的无参构造函数调用

int Function();                  //函数声明

        首先Date是不是我们自定义的的一个类型?d3是不是一个函数名?加上括号是不是整个句子就变成了一个函数的调用?只不过这个函数没有参数罢了。

         通过编辑器的报错提示,我们就清楚了编辑器是分不清楚我们这个地方到底是想要实例化一个对象还是对函数进行声明。这么看来不是我们觉得编辑器有病,而是编辑器觉得我们有病了,哈哈。

        对于无参构造函数的理解大家不能仅仅将其想为没有参数的函数,还记得C++中新添加的一个东西——缺省参数吗?当我们定义的构造函数中函数里的参数是全缺省,那么他就是无参构造函数。

class Date
{
public:
	// 1.无参构造函数
	Date()
	{}
    
    //全缺省
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

         注意看上面的报错信息,结合我所讲解的内容,没有写参数的构造函数和全缺省没有形成函数重载,反而造成了重定义的错误,也就证明了我们的全缺省也是无参构造函数的一种形势。

        由上述问题我们也得知了一个信息,那就是有参构造函数只要参数不同可以不断的定义,但是无参构造函数由且只能有一个。

2) 

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

        这句话的意思就是我们如果没有写任何的构造函数,那么编辑器会自动为我们生成一个无参构造函数,当我们自定义了任意一种构造函数,那么编辑器都不会生成。

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

        按照我们的特性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,double、char)等等它不会做任何处理,但是对于我们的自定义类型,它是会去调用那个自定义类型的构造函数的。

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

 

         通过上述代码和图也能反映编辑器对于内置类型不做处理,但是对于自定义类型会调用它的构造函数。

        这一点说实话,我认为C++没有设计得很好,同时也不止是我,很多大佬也认为这一点设计得有问题,对于内置类型,说实话直接初始化为0也行啊,直接不进行初始化但是对自定义类型做初始化不是相当于把亲儿子往外丢嘛。

         所以说,基于这一点,C++在C11这个版本为其添加了一个补丁,也就是在成员变量声明的时候写一个缺省参数,也就是内置类型成员变量在类中声明时可以给默认值。

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 = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

         注意,这个功能是C11添加的一个补丁,所以不能保证它能在所有版本的编辑器下都能够实现编译,所以说,我们平时写还是写下类的构造函数更好。

2. 析构函数

2.1 概念

        如果说我之前写的构造函数相当于malloc、new等等,那么这里谈论的析构函数就相当于free、delete释放内存这一个过程,当然我只是描述这一个过程,实际上二者不是同一个东西。

        它的产生一是为了方便我们使用,因为懒惰的我们总是会忘记free或者delete,那么这个时候析构函数的产生就尤为的重要,虽然对于我们平时写的小项目没有什么影响,但是对于工程当中产生的内存泄漏可就是一个大问题了。

        析构函数和构造函数一样,不需要我们调用,并且每一个对象只能调用一次析构函数,因为调用析构函数的原因只有一个,那就是这个对象的生命周期已经结束。

2.2 特性

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

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType)* capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			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);
}

        上方是一个栈类,有它的构造函数Stack(),也有析构函数~Stack(),同时也有普通的成员函数,由此代码我们也能清晰的知道析构函数的写法就是在类名的前面加上~表示,和构造函数一样,它也是没有返回值的。不过他与构造函数不同,它不支持重载,主要是也没有办法实现重载,因为整个对象的使用中,我们都没有办法调用到析构函数,它只能由编辑器调用。

        但是我觉得析构函数不支持重载的原因是没有重载的必要,难道你释放空间还会用不同的方式释放?想来点花活?要是不信非要写几个析构函数你就等哭吧。

         同样,析构函数也是默认函数之一,所以就算我们不写,编辑器还是会为我们这些小蠢蛋写上的,不过它也是一个空的构造函数,它对于内置类型不做处理,但是对于自定义成员类型会调用它的析构函数。看下代码和图。

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour = 1;
	int _minute = 1;
	int _second = 1;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

         当然,既然它是系统为我们写的要求就别那么高,他一定是不能完成所有的释放功能的,就比如说需要释放掉重堆上申请来的空间就不行。

        综上,我们可以知道其实析构函数并不是每一个类都必须写上,只有内置类型的类就不用写,这样想更容易理解一点,我反正都不要它了,我留他干嘛,而且内置类型开的空间又不是我申请的而是编辑器自己申请的,就没有释放的必要了,随着它的自然周期结束就行。

        但是对于有从堆上申请的空间时,析构函数就必须出场了,防止内存泄漏。

3. 拷贝构造

3.1 概念

        拷贝构造其实就是一个对象生成另外一个对象,按照现实生活中来说的话也就是双胞胎之类的,不是让你女朋友给你找个女朋友哈,别乱想哦。

         拷贝构造函数的写法和构造函数一样,只不过他只有单个形参,并且该形参一般是该类型对象的const引用,一般是在创建新对象时由编译器自动调用。

        想想看为什么拷贝一个对象只需要一个参数呢?一般来说不应该是两个参数吗,一个用于被拷贝,一个用于拷贝?

        这就需要我们前一篇的知识啦,那就是每一个对象里面悄悄藏了一个this指针,也就不需要两个指针咯。

3.2 特征

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

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

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// Date(const Date& d) // 正确写法
	Date(const Date d) // 错误写法:编译报错,会引发无穷递归
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

        大伙对于这里的无穷递归可能有一些问题,所以我决定为大家讲解一下。

        提问:平时我们的传参数传参,函数接收到的是拷贝对象还是传参本身?

        答:是传参的拷贝对象。

        提问:那么对于自定义类型的传参是否有区别?

        答:没有任何区别。

        提问:当拷贝对象中有申请的空间,编辑器应该如何拷贝?

        答:对于C语言来说,直接按字节进行拷贝,不管是否是自定义类型还是内置类型,但是C++中进行了优化,对于内置类型编辑器也是按字节拷贝,但是对于自定义类型,编辑器会去调用该类型的拷贝构造。

        提问:C语言中直接对任意类型按字节拷贝是否会有问题?

        答:问题很大,因为按字节拷贝也就证明了拷贝的对象会指针指向被拷贝对象的从堆上申请的空间,如果我们不清楚这个问题,那么务必会产生对于数据的紊乱,甚至释放两次该空间。所以说,这一部分其实是C语言没有考虑清楚的一个点,是错误的。

        提问:那么C++是如何做的?

        答:如刚才的答案,对于任意一次自定义类型拷贝,编辑器都会调用该类型的拷贝构造,也就是说,就算是作为函数的形参他也会去调用拷贝构造。但是拷贝函数必须用引用传参,否则会产生无穷递归,产生原因请看下图。

         相信大家看了这一问一答也能明白为什么在写拷贝构造函数的时候需要用引用传参的原因了,因为引用传参是不会产生备份的。至于里面的const是为了防止某些粗心蛋乱改,写不写都是一样的,只不过写上更加的完美。

        当然拷贝构造也是默认函数之一,所以就算我们不写,编辑器也会生成一个,不过默认生成的拷贝函数就会回归C语言的模式,按字节拷贝,也就是浅拷贝。

        至于使用条件相信大家也能分辨出来,也就是当类里面只有内置类型时可以不写,但是有从堆上申请的空间时,就必须写上拷贝构造函数,实现深拷贝。

class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const 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 d1;
	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构
	//造函数
	Date d2(d1);    //拷贝构造
    Date d3 = d2;   //拷贝构造
	return 0;
}

        像上述代码这样的类,我们就算不写拷贝构造也是没有任何问题的。

        但是请看下面的代码。

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}

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

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType *_array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

         看到了吗?朋友们,编辑器对于同一片重堆上申请的空间进行了析构2次,也就造成了此次程序崩溃的一点,要是不知道这一个知识点,估计找bug的找到猴年马月去。这也就是浅拷贝的危害之一,所以要不要写拷贝函数请各位朋友考虑清楚哦。

图解:

 4. 赋值运算符重载

4.1 运算符重载

        在了解赋值运算符重载之前我们得先了解运算符重载是怎样的才行。

        C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

        函数名字为:关键字operator后面接需要重载的运算符符号。

        函数原型:返回值类型 operator操作符(参数列表)。

注意:
        不能通过连接其他符号来创建新的操作符:比如operator@
        重载操作符必须有一个类类型参数
        用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
        作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this。
        .*   ::   sizeof   ?:   . 注意以上5个运算符不能重载。

为什么会出现运算符重载呢?

        事实上,我们学习C++,里面会包含各种各样的类,有的类之间呢有需要有比较之类的函数操作,但是每一次比较都要去写整个函数调用感觉太麻烦了,所以就重新对运算符附加新的比较类型,让我们能够方便的对类于类之间进行交互使用。

类外的操作符重载:

// 全局的operator==
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//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;
}
void Test()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	cout << (d1 == d2) << endl;
}

        对于有多个运算符重载的问题其实很简单,编辑器会自动会我们解决这个问题,所以不需要我们担心,因为这个运算符重载实际上也就是函数重载,就算函数名相同,但是参数类型不同也是构成重载的。

        但是朋友们发现了没,当我们将运算符重载写为全局的,它就变成公有的了,那么此时,封装性如何保证?

        其实解决方案有两个,一个是通过写友元函数解决,而是直接将我们的函数写为类内的成员函数就能解决。

类内的操作符重载:

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// bool operator==(Date* this, const Date& d2)
	// 这里需要注意的是,左操作数是this,指向调用函数的对象
	bool operator==(const Date& d2)
	{
		return _year == d2._year
		&& _month == d2._month
			&& _day == d2._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

        当我们定义为类内的成员函数时,就必须将操作数对应,this指针永远是左操作数,另外一个类是右操作数。

4.2 赋值运算符重载

        赋值运算符重载是类中的默认成员函数之一,也就是说当我们实例化了两个对象,例如我们实例化了像个对象d1、d2。我们可以直接d2 = d1,直接上d1里面的数据拷贝到d2当中去。

        当然也和我之前所说的一样,这个过程是一个浅拷贝过程,所以在有深拷贝的时候需要自己在类中或者类外写下赋值运算符重载。

赋值运算符重载格式:

        参数类型: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;
};

        值得注意的一点是,其它的操作符重载可以定义在类外或者是类里面,但是对于赋值运算符重载只能实现在类里面作为成员函数,不信的话请复制下方代码进行编译。

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

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

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

5.3 前置++和后置++的重载

        本来来说,这个重载不应该放到这里来讲,但是他又十分的有用,所以我提前讲解了。

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

	Date& operator++()
	{
		_day += 1;
		return *this;
	}

	Date operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}
private:
	int _year;
	int _month;
	int _day;
};


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

        大家看出二者的区别了吗? 那就是后置++的运算符重载的参数里面有一个int,只能是int不能是其它的任何类型,可以不要任何的变量接收,这就是C++的规定,其余的问题请别问我,我不知道。

 其中有几个需要注意的点:

前置++:返回+1之后的结果
注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率

后置++:
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递
注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this+1,而temp是临时对象,因此只能以值的方式返回,不能返回引用


        以上就是我本节想讲解的全部内容啦,有不周到的地方请多多包涵啦,也请大家多多支持我啦!

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

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

相关文章

AI智能机器人,在这里也可以体验~

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”现在是&#xff1a;2023年2月17日00:14:42前言最近AI智能chatgpt特别的火&#xff0c;相信好多人都已经体验过了&#xff0c;之前我的群里也接入过&#xff0c;奈何总是收到警告和限制…

泛微发布内外协同的客服管理系统-睦客邻

客户服务是企业业务环节中的关键一环&#xff0c;也是确保客户最终满意度的关键一环。好的客户服务是一个组织能持续经营的有力保证。 如何让客服成为客户的好邻里&#xff0c;让客户真实地感受到全方位的服务&#xff0c;从而让组织的服务产生力量和价值。企业对客服有着系统…

LV8731V-TLM-H 带保护步进电机驱动器特性简述

LV8731V-TLM-H是一个2−通道H−桥驱动器IC&#xff0c;它可以切换步进电机驱动器和两个有刷电机驱动器&#xff0c;前者支持1/16−步进分辨率的微步进驱动&#xff0c;后者支持电机的前进、后退、制动和待机。它非常适合驱动办公室设备和娱乐应用中使用的刷直流电机和步进电机。…

Web--Maven

1.maven管理项目的区别 2. 安装后&#xff0c;conf目录下的setting文件中&#xff0c;对本地仓库的配置 此处可替换成自定义的本地仓库地址&#xff0c;默认为c:/user/17860/.m2/repository(我的电脑上的&#xff09; 3.maven项目的标准目录结构 4.项目的生命周期 5.Maven概…

内网渗透(三十七)之横向移动篇-Pass the Hash 哈希传递攻击(PTH)横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

面试攻略,Java 基础面试 100 问(七)

String 是最基本的数据类型吗? 不是。Java 中的基本数据类型只有 8 个&#xff1a;byte、short、int、long、float、 double、char、boolean&#xff1b;除了基本类型&#xff08;primitive type&#xff09;和枚举类型&#xff08;enumeration type&#xff09;&#xff0c…

分享一个外贸客户案例

春节期间一个外贸人收到了客户的回复&#xff0c;但因为自己的处理方式造成了一个又一个问题&#xff0c;我们可以从中学到一些技巧和知识。“上次意大利的客人询价后&#xff0c;一直没回复&#xff08;中间有打过电话&#xff0c;对方说口语不行&#xff0c;我写过邮件跟进过…

数据结构与算法之二分查找分而治之思想

决定我们成为什么样人的&#xff0c;不是我们的能力&#xff0c;而是我们的选择。——《哈利波特与密室》二分查找是查找算法里面是很优秀的一个算法&#xff0c;特别是在有序的数组中&#xff0c;这种算法思想体现的淋漓尽致。一.题目描述及其要求请实现无重复数字的升序数组的…

论文阅读笔记《DEEP GRAPH MATCHING CONSENSUS》

核心思想 本文提出一种基于图神经网络的图匹配方法&#xff0c;首先利用节点相似度构建初始的匹配关系&#xff0c;然后利用局部的一致性对初始的匹配关系进行迭代优化&#xff0c;不断筛除误匹配点&#xff0c;得到最终的匹配结果。本文还提出几种措施来降低计算复杂度&#x…

SpringBoot学习总结2

1、配置文件 1.1、配置文件类型 properties yaml 优点&#xff1a;比起xml而言&#xff0c;语法更简洁&#xff0c;更轻量级。非常适合用来做以数据为中心的配置文件 基本语法 key: value&#xff1b;:后面要跟一个空格大小写敏感使用缩进表示层级关系缩进不允许使用tab&…

内存屏障1

内存屏障 引入 我们知道 volatile 能保证 JMM约束的 可见性和有序性。 关于有序性&#xff0c;到底该如何理解&#xff1f; 有序性的根本保证&#xff0c;就是 禁止指令重排序 重排序&#xff1a; 重排序是指 编译器和处理器 为了优化程序性能 而对指令序列进行重新排序…

万字讲解你写的代码是如何跑起来的?

今天我们来思考一个简单的问题&#xff0c;一个程序是如何在 Linux 上执行起来的&#xff1f; 我们就拿全宇宙最简单的 Hello World 程序来举例。 #include <stdio.h> int main() {printf("Hello, World!\n");return 0; } 我们在写完代码后&#xff0c;进行…

【THREE.JS学习(1)】绘制一个可以旋转、放缩的立方体

学习新技能&#xff0c;做一下笔记。在使用ThreeJS的时候&#xff0c;首先创建一个场景const scene new THREE.Scene();接着&#xff0c;创建一个相机其中&#xff0c;THREE.PerspectiveCamera&#xff08;&#xff09;四个参数分别为&#xff1a;1.fov 相机视锥体竖直方向视野…

算法拾遗二十六之暴力递归到动态规划五

算法拾遗二十五之暴力递归到动态规划五题目一&#xff08;返回K次打击后英雄把怪兽砍死的几率&#xff09;【样本对应模型&#xff0c;N和K是样本】题目二&#xff08;返回组成aim的最少货币数&#xff09;从左往右尝试模型题目三&#xff08;返回裂开的数的种类&#xff09;题…

【Kotlin】Kotlin函数那么多,你会几个?

目录标准函数letrunwithapplyalsotakeIftakeUnlessrepeat小结作用域函数的区别作用域函数使用场景简化函数尾递归函数&#xff08;tailrec&#xff09;扩展函数高阶函数内联函数&#xff08;inline&#xff09;inlinenoinlinecrossinline匿名函数标准函数 Kotlin标准库包含几个…

CUDA的统一内存

CUDA的统一内存 文章目录CUDA的统一内存N.1. Unified Memory IntroductionN.1.1. System RequirementsN.1.2. Simplifying GPU ProgrammingN.1.3. Data Migration and CoherencyN.1.4. GPU Memory OversubscriptionN.1.5. Multi-GPUN.1.6. System AllocatorN.1.7. Hardware Coh…

如何学习 Web3

在本文中&#xff0c;我将总结您可以采取的步骤来学习 Web3。从哪儿开始&#xff1f;当我们想要开始新事物时&#xff0c;我们需要一些指导&#xff0c;以免在一开始就卡住。但我们都是不同的&#xff0c;我们有不同的学习方式。这篇文章基于我学习 Web3 的非常个人的经验。路线…

SpringBoot集成WebSocket实现客户端与服务端长连接通信

场景&#xff1a; WebSocket协议是用于前后端长连接交互的技术&#xff0c;此技术多用于交互不断开的场景。特点是连接不间断、更轻量&#xff0c;只有在关闭浏览器窗口、或者关闭浏览器、或主动close&#xff0c;当前会话对象才会关闭。 这里只是简单的记录一下使用方式 一、服…

Proxy lab

CSAPP Proxy Lab 本实验需要实现一个web代理服务器&#xff0c;实现逐步从迭代到并发&#xff0c;到最终的具有缓存功能的并发代理服务器。 Web 代理是充当 Web 浏览器和终端服务器之间的中间人的程序。浏览器不是直接联系终端服务器获取网页&#xff0c;而是联系代理&#x…

关系型数据库的三大范式

一、简而言之 1、是什么&#xff1f; 三大范式是针对关系型数据库的一种数据库设计规范&#xff0c;使数据库设计符合约定的规范要求。 2、为什么要符合该规范&#xff1f; 为了建立冗余较小、结构合理的数据库。 3、三大范式内容的简单理解&#xff08;Normal Form&#…