类和对象 (中)

news2024/11/20 9:26:07

在这里插入图片描述

文章目录

    • 类的六个默认成员函数
    • 构造函数
    • 析构函数
      • 特性
      • 使用
    • 总结构造函数和析构函数
    • 拷贝构造函数
      • 特性
      • 拷贝构造总结
    • 赋值运算符的重载
      • 运算符重载
      • 赋值运算符重载
      • 总结拷贝构造函数和赋值运算符重载
    • 关于operator<<重载
    • 日期类实现
    • const 修饰的成员函数
    • 取地址重载以及const取地址重载

二次修订版本,date: 2024 3 12;

类的六个默认成员函数

当我们在类中什么都不写的时候,编译器会在类内默认生成六个成员函数。

image.png
分别是:构造函数,析构函数,拷贝构造,赋值运算符重载,取地址重载和const取地址。

构造函数

构造函数是负责对象的初始化工作的,并不是创建对象。

构造函数会在对象实例化的时候自动调用完成对象的初始化工作。

构造函数的特征函数名与类名相同,无返回值。创建对象的时候由编译器自动调用,可以保证每个对象都有一个初始值,在对象的生命周期内只会调用一次。构造函数可以重载。

image.png
这里我们写了一个Date()的构造函数,创建对象后并不需要我们主动调用这个对象就已经初始化好了。
注意:void并不是无返回值而是空返回值,无返回值就直接不写。
当然如果想要主动传参也是可以的。就如下面这样:

class Date
{
public:
	Date()
	{
		_year = 2022;
		_month = 6;
		_day = 18;
	}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 6, 23);
	return 0;
}

这两个构造函数构成了函数重载(这就使得我们可以有多种初始化对象的方式)可以使用默认值也可以使用给定值初始化。但是这样写两个函数略显复杂,可以使用全缺省的构造函数替代。

class Date
{
public:
	Date(int year= 2022, int month = 6, int day = 18)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

就像上面这个样子,我们不传参的时候自动调用就是使用缺省值。

但是要注意下面这种写法,是错误的

class Date
{
public:
	Date()
	{
		_year = 2022;
		_month = 6;
		_day = 18;
	}
	Date(int year= 2022, int month = 6, int day = 30)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

这里既写了无参的构造函数,又写了全缺省,当不传参的时候,编译器不知道调用无参的构造函数,还是调用全缺省的构造函数使用默认值初始化,这个错误叫做调用不明确也就是二义性。

如果不写构造函数,那编译器会自动生成一个默认构造函数

class Date
{
public:

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

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

image.png
默认生成的构造函数对于内置类型(就是int,int*,包括Date*等)不会进行初始化,但是对于自定义类型,就会调用这个类的默认构造函数对这个类的对象进行初始化,如果这个类没有默认构造函数会报错。( 数据成员“Date::_a”不具备相应的 默认构造函数 或重载解决不明确,因此已隐式删除函数)

class A
{
public:
	A()
	{
		_a = 10;
		_b = 20;
	}
private:
	int _a;
	int _b;
};
class Date
{
public:

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

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

image.png
上面的代码,在Date类里面包含了一个A的类的对象,在调用构造函数的时候对于int这些内置类型,编译器不会进行处理,对于A这样的自定义类型的对象,会去调用他的默认构造函数进行初始化。
image.png
使用栈实现队列的时候,MyQueen的构造函数可以不用写,系统生成的会调用Stack的默认构造函数完成初始化
最后注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。默认成员函数就是不需要传参就可以调用的函数。

析构函数

构造函数不是创建对象而是负责初始化对象,析构函数也不是负责销毁对象,而是在对象销毁之前调用,用来清理资源。

特性

  1. 析构函数名是类名,但是前面要加~
  2. 无返回值无参数
  3. 一个类只有一个析构函数,如果不写系统自动生成。
  4. 析构函数会在对象销毁(生命周期结束)的时候由C++编译系统自动调用。

C++的析构函数本质上对于内置类型是没有什么用的,只对使用malloc申请过空间的才有用,用来防止内存泄漏,在服务器方面,内存泄漏是很严重的问题而且相比于不初始化,忘记释放内存是不会报错的,这就是隐藏的利刃,当服务器因为内存泄漏崩溃之后,你才能发现内存泄漏的问题。

C++不像java还有垃圾回收器(GC),C++只能靠析构函数和智能指针来防止内存泄漏。

使用

class SList
{
public:
	SList(int size = 10)
	{
		_arr = (int*)malloc(sizeof(int) * size);
		_size = 0;
		_capacity = size;
	}
	~SList()
	{
		if (_arr)
		{
			free(_arr);
			_arr = nullptr;
			_size = 0;
			_capacity = 0;
		}
	}

private:
	int* _arr;
	int _size;
	int _capacity;
};

int main()
{
	SList _new;

	return 0;
}

这里我们就是自己定义的析构函数,在_new这个对象生命周期结束的时候,就会自动释放对象内的_arr,malloc出来的空间,又叫清理资源。

int main()
{
	SList n1;
    SList n2;
    SList n3;
	return 0;
}

这里定义了三个对象,在定义的时候,构造顺序是n1,n2,n3,在析构的时候是n3,n2,n1;因为我们的对象创建在栈上,栈区的性质是后进先出,最后入栈的n3会是最先出栈的,因此也是n3先析构的。

析构函数也是默认成员函数,不写的时候也是会默认生成一个的,他的处理方式和构造函数也是一样的。对于内置类型不起作用,对于自定义类型会去调用它的析构函数

因此,如果我们malloc申请了空间,那么就需要自己写出析构函数进行内存释放,方便的是不需要我们手动去调用。

class String
{
public:
	String(const char* str = "jack")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
	char* _str;
};
class Person
{
private:
	String _name;
	int _age;
};
int main()
{
	Person p;
	return 0;
}

这段代码可以看出,编译器默认生成的析构函数对于自定义类型是去调用它自身的析构函数。
如果对象内没有资源需要清理,不需要写,系统自动生成就够用,而且在release版本下,编译器会自动去掉没有资源需要清理的析构函数。
对同一块内存空间释放两次,会发生错误。

总结构造函数和析构函数

对于内置类型,构造函数和析构函数都是不起作用的,对于自定义类型会调用这个自定义类型对象的默认构造和析构函数。

拷贝构造函数

拷贝构造函数的应用场景是:创建一个对象用另一个同类的对象对他进行初始化。
就像是内置类型中的int a = b;用b给a初始化值。

拷贝构造的代码

class Date
{
public:
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //这就是拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(d1);
	Date d3 = d1;
	return 0;
}

拷贝构造函数是构造函数的重载。下面定义d2,d3对象的时候初始化都是调用了拷贝构造函数,这里两种形式都可以。第二种实际上是重载赋值运算符然后调用了拷贝构造函数。

特性

1,拷贝构造是构造函数的重载函数
2,拷贝构造函数的参数只有一个且只能是类的引用类型的(隐含的类型*this指针不算)如果参数不是引用会引发无限递归。
原因:如果使用传值,参数是Date d,那么值传参的时候就要将实参的值拷贝给形参,这时候就需要调用拷贝构造,然后就无限递归了。
image.png
当不写拷贝构造函数,编译器默认生成的拷贝构造函数完成的是浅拷贝,也就是值拷贝,在Date类这里是没有错误的,但是一旦成员对象有指针,指针指向的空间是malloc出来的时候就会出错了。

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

int main()
{
	Date d1;
	d1.SetDate(2022, 6, 18);
	Date d2(d1);
	Date d3 = d1;
	return 0;

image.png
编译器默认生成的拷贝构造函数对于我们的Date类是完全没有问题的。浅拷贝完全能满足Date类对象的需求。

那么现在来看下面这段代码

class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		_size = 0;
		_capacity = capacity;
	}

	~stack()
	{
		if (_a != nullptr)
		{
			free(_a);
			_a = nullptr;
			_size = 0;
			_capacity = 0;
			cout << "free is ok" << endl;
		}
	}
private:
	int* _a;
	int _size;
	int _capacity;
};

int main()
{
	stack s1;
	stack s2(s1);
	return 0;
}

这段代码我们手动实现了构造和析构,然后调用编译器默认的拷贝构造函数给对象s2初始化成与s1相同的对象,但是程序运行到最后的时候却崩溃了。

image.png
image.png
上面崩溃的图中我们可以看出,完成了一次s2对象的析构,对s1进行析构的时候程序挂了?从调试图看到两个对象的成员对象_a指向的是同一块空间,原因就是对同一块空间释放了两次所以程序崩溃了。这也说明了系统默认生成的拷贝构造函数进行了浅拷贝。
因此对于这种类,就需要手动实现深拷贝构造函数
至于为什么释放两次就会崩溃,系统方面的问题就是,这块空间收回来之后,有可能又给了别人用,别人还没用完你给他释放了这是错误的。所以不能两次释放同一块空间。

拷贝构造总结

对于date这种类,没有涉及到malloc或者指针之间的关系的时候是可以不写的,系统默认生成的拷贝构造函数实现的浅拷贝完全够用,但是到了stack这种类的时候就不能用默认拷贝构造函数了。

赋值运算符的重载

运算符重载

内置类型想要比较大小或者加减乘除赋值,都可以用运算符来实现,±*/,但是自定义类型想进行运算只能通过函数来实现,不仅麻烦而且可读性也不高。所以C++引入了运算符重载,让自定义类型也可以通过运算符重载后使用运算符进行运算。

运算符重载的特性
1,运算符重载函数名是operator后面接上要重载的符号。
2,函数的返回值与普通函数类似,可以根据情况自定义。运算符重载函数的参数必须有一个类的类型的参数或者枚举类型的参数。
3,重载的操作符不能改变其原来的含义,比如+不能重载成减法。
4,如果运算符重载函数定义为全局的函数,要显示写出类类型的参数,定义成成员函数就会少一个,有一个参数是this指针。(this指针一定是第一个形参)
5,“.*”,“::”,“:?”,“sizeof”,“.” 这五个运算符,点星,域作用访问限定符,三目运算符,sizeof,和点操作符,是不可以重载的。

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造和析构都可以使用默认。
	bool operator==(const Date& d)
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}

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

int main()
{
	Date d1;
	Date d2(d1);
	if (d1 == d2)
		cout << "yes" << endl;
	return 0;
}

这段代码我们重载了==来判断自定义的类是不是相等。

赋值运算符重载

特性:
1,参数类型(参数类型是引用,可以减少传值的时候拷贝构造的性能消耗)
2,返回值(为了可以使用连续赋值,返回值要是第一个对象参数的引用)
3,检测一下是不是在给自己赋值,如果是直接返回即可。
4,返回的是第一个对象的引用,因为第一个对象出函数也不会销毁所以返回值是*this
5,如果这个类没有手动写,那编译器会自动生成一个赋值运算符的重载函数。

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

int main()
{
	Date d1(2022,6,19);
	Date d2;
	d2 = d1;
	Date d3;
	d3 = d2 = d1;
	return 0;
}

通过返回类的引用可以实现连续赋值。
赋值运算符是默认成员函数,就算我们不写,编译器也会自动生成。结论是:这个默认生成的赋值运算符重载函数,与默认生成的拷贝构造函数是类似的,只能实现按照字节序拷贝也就是浅拷贝,如果遇到Stack那种存在malloc空间的类就会出现和拷贝构造函数类似的错误,对同一块空间释放了两次。
image.png

总结拷贝构造函数和赋值运算符重载

1,拷贝构造函数和构造函数形成了函数重载,参数是类对象的引用,无返回值
2,赋值运算符重载是一个函数名operator=的函数,参数是对象类的引用,返回值是对象的引用,因为这个对象出了函数作用域并不会销毁。
3,拷贝构造函数和赋值运算符重载函数,如果不写,系统默认生成的都是实现的浅拷贝或者说是值拷贝,对于日期类这种,没有问题,但是对于Stack就会出现问题。

关于operator<<重载

operator<<是不可以在类内实现的,因为类内默认第一个参数为this指针,调用的时候就是d1<<cout ,因此operator<<一般在类外定义,可以通过1,友元声明访问private成员。2,通过类内的get方法获取成员。

tips:全局函数或者变量定义在.h文件中的时候,如果被两个cpp文件包含会现重定义的情况,解决方案:

  1. 全局函数的声明与定义分离
  2. 加static修饰函数(static会改变函数的链接属性,只在当前文件可见)
  3. 用inline修饰operator<<,内联函数会在调用的地方展开不会进入符号表。

日期类实现

//Date.h
#pragma once
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int day = days[month];
		if (month == 2
			&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			day += 1;
		}
		return day;
	}
	//打印日期
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		if (month > 12 || day<0 || day>GetMonthDay(year, month))
		{
			cout << "非法日期" << endl;
		}
			_year = year;
			_month = month;
			_day = day;
	}
	// 拷贝构造函数
	// d2(d1)
	Date(const Date& d)
	{
		*this = d;//复用赋值运算符重载
	}

	// 赋值运算符重载
	// d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	// 析构函数
	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
	// 日期+=天数
	Date& operator+=(int day)
	{
		_day += day;
		while (_day> GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month++;
			if (_month > 12)
			{
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	// 日期+天数
	Date operator+(int day)
	{
		Date ret(*this);
		ret += day;
		return ret;
	}
	// 日期-天数
	Date operator-(int day)
	{
		Date ans(*this);
		ans -= day;
		return ans;
	}
	// 日期-=天数
    // 日期-=day可以复用 日期 += -day;
	Date& operator-=(int day)
	{
		_day -= day;
		while (_day <= 0)
		{
			_month--;
			if (_month == 0)
			{
				_year--;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}
	// 前置++
	Date& operator++()
	{
		*this += 1;
		return *this;
	}
	// 后置++
	Date operator++(int)
	{
		Date ret = *this;
		*this += 1;
		return ret;
	}
	// 后置--
	Date operator--(int)
	{
		Date ret = *this;
		*this -= 1;
		return ret;
	}
	// 前置--
	Date& operator--()
	{
		*this -= 1;
		return *this;
	}

	// >运算符重载
	bool operator>(const Date& d)
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year)
		{
			if (_month > d._month)
			{
				return true;
			}
			else if (_month == d._month)
			{
				if (_day > d._day)
				{
					return true;
				}
			}
		}
		return false;
	}
	// ==运算符重载
	bool operator==(const Date& d)
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}
	// >=运算符重载
	inline bool operator >= (const Date& d)
	{
		if (_year < d._year)
		{
			return false;
		}
		else if (_year == d._year)
		{
			if (_month < d._month)
			{
				return false;
			}
			else if (_month == d._month)
			{
				if (_day < d._day)
				{
					return false;
				}
			}
		}
		return true;
	}

	// <运算符重载
	bool operator < (const Date& d)
	{
		return !((*this) >= d);
	}
	// <=运算符重载
	bool operator <= (const Date& d)
	{
		return !(*this > d);
	}
	// !=运算符重载
	bool operator != (const Date& d)
	{
		return !(*this == d);
	}
	// 日期-日期 返回天数
	int operator-(const Date& d)
	{
		int count = 0;
		int flag = 1;
		Date max = *this;
		Date min = d;
		if (max < min)
		{
			max = d;
			min = *this;
			flag = -1;
		}

		while (max != min)
		{
			min++;
			count++;
		}
		return count * flag;
	}
private:
	int _year;
	int _month;
	int _day;
};

//test.cpp测试文件
#include"Date.h"

void Test1()
{
	Date d1;
	d1.Print();
	Date d2(2022, 5, 3);
	d2.Print();
	Date d4(2022, 7, 31);
	d4.Print();
	Date sum = d4 + 1000;
	sum.Print();
	d4.Print();


}

void Test2()
{
	Date d1(2022, 1, 10);
	Date d2 = d1 - 20;
	d2.Print();
	d1.Print();
	Date d3(2022, 12, 31);
	Date ret = d3++;
	ret.Print();
	d3.Print();
}

void Test3()
{
	Date d1(2022, 2, 28);
	Date d2 = --d1;
	d2.Print();
	d1.Print();
}

void Test4()
{
	Date d1(2022, 2, 28);
	Date d2(2022, 2, 28);
	cout << (d1 != d2) << endl;
}
void Test5()
{
	Date d1(2022, 6, 19);
	Date d2 = d1;
	Date d3(2022, 7, 18);
	cout << (d3 - d1) << endl;
}
int main()
{
	Test5();
	return 0;
}

可以看到在上面代码的实现中,很多函数是可以复用的。比如拷贝构造函数可以复用赋值运算符重载。比大小的我们只需要实现一个>和 ==的实现,其他的都可以复用这两个函数加上取反符号来实现
**注意:**最后的日期减日期的实现可以使用找出较大的那个日期和较小的那个日期,然后让小的那个不断自增等和较大的那个相等了这时候计数器里面的值就是他们之间的天数差。
留一个问题,int占位符如何是保证后置++能调用到正确地重载函数呢?
答案是:后置++编译器在调用的时候会默认传一个int类型的参数过去,参数加上一个int来占位,与前置++的函数构成重载。

const 修饰的成员函数

const对象调用函数的时候,需要this指针指向的对象不可以被修改,比如Print,或者是>这类比较重载的,否侧const对象调用不了这些函数。

	bool operator==(const Date& d)const
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}
    //bool operator==(const Date* const this,const Date& d)

直接在函数后面加上const即可,这时候函数就变成了注释掉的样子。

下面来思考这几个问题

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其它的非const成员函数吗?
  4. 非const成员函数内可以调用其它的const成员函数吗?

答案:
1,不可以,const对象不可以调用非const对象,const对象只读,如果变成了普通对象就是可读可写的,权限放大了。
2,可以,非const的普通对象是可读可写的,调用const成员函数被修饰成只读的,权限缩小了。
3,不可以,const成员函数的this指针是const属性的,调用非const函数会变成可读可写的this,权限放大了。
4,可以,非const函数调用const的成员函数,普通的this指针,传给了const的this指针,权限缩小。

取地址重载以及const取地址重载

	//&运算符重载
	const Date* operator&()const
	{
		return this;
	}
	Date* operator&()
	{
		return this;
	}
//第一个是给const对象调用的,第二个是给普通对象调用的。

取地址并不需要什么重载函数,取这个对象的地址的时候直接取就可以了。
唯一可能的用法就是下面这种。

	//&运算符重载
	const Date* operator&()const
	{
		return nullptr;
	}
	Date* operator&()
	{
		return nullptr;
	}

那就是不想让你取到这个类的对象的地址,可以像上面这样子写。

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

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

相关文章

JVM 类的加载篇

我们都知道一个类从加载到卸载一共分为七个过程 加载 - 链接(验证 - 准备 - 解析) - 初始化 - 使用 - 卸载 下文我们将详细解析这些过程 谁需要加载? 在Java中数据类型分为基本数据类型和引用数据类型,基本数据类型由虚拟机预定义,引用数据类型则需要类的加载 1.加载/装载(loa…

腾讯云最新优惠券领取入口及使用指南

​腾讯云作为国内领先的云服务提供商&#xff0c;以其稳定、高效的服务赢得了广大用户的信赖。为了吸引用户&#xff0c;腾讯云经常会推出各种优惠活动&#xff0c;其中最常见的就是腾讯云优惠券。本文将为大家分享腾讯云最新优惠券领取入口及使用指南&#xff0c;助力大家轻松…

从汇编来角度剖析C语言函数调用过程

目录 1.引言 2.寄存器 3.栈帧 4.函数调用前调用者的动作 5.被调用者在函数调用后的动作 6.被调用者返回前的动作 7.调用者在返回后的动作 8.总结 1.引言 当一个c函数被调用时&#xff0c;一个栈帧(stack frame)是如何被建立&#xff0c;又如何被消除的。这些细节跟操作…

爆肝整理万能sass框架:react18+webpack5+typescript+ant Design,框架在手,交付无忧!!!

来活了&#xff0c;要求一周时间内快速给xxx业务开发一个sass系统平台&#xff0c;要求有角色权限控制&#xff0c;推荐模块&#xff0c;各种业务内容模块&#xff0c;莫慌&#xff0c;直接上代码&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1.系统框架配置…

前端跨页面通信的几种方式---同源

参考链接 1、LocalStorage:当 LocalStorage 变化时&#xff0c;会触发storage事件。利用这个特性&#xff0c;我们可以在发送消息时&#xff0c;把消息写入到某个 LocalStorage 中&#xff1b;然后在各个页面内&#xff0c;通过监听storage事件即可收到通知。 2、BroadCast C…

12 list的使用

文档介绍 文档介绍 1.list是可以在常数范围内的任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代 2.list的底层是带头双向链表循环结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和…

C++的This详解

目录 听说点赞的UU会有好运哦&#xff01; 听说点赞的UU会有好运哦&#xff01; 听说点赞的UU会有好运哦&#xff01; this指针&#xff1a; 面试常考this&#xff1a; 特别注意&#xff1a; this指针&#xff1a; 类不保存成员函数&#xff0c;而是放在公共代码段&…

Flutter第四弹:Flutter图形渲染性能

目标&#xff1a; 1&#xff09;Flutter图形渲染性能能够媲美原生&#xff1f; 2&#xff09;Flutter性能优于React Native? 一、Flutter图形渲染原理 1.1 Flutter图形渲染原理 Flutter直接调用Skia。 Flutter不使用WebView&#xff0c;也不使用操作系统的原生控件,而是…

【计算机网络实践】FileZilla Server1.8.1实现局域网ftp文件传输

大二新生随便写写笔记&#xff0c;轻喷&#xff0c;鉴于本人在网络搜索中并未搜索到1.8.1版本的使用方法&#xff0c;因而瞎写一页。 一、准备 下载一个FileZilla Server1.8.1在你想作为服务器的主机上&#xff08;此处直接在官网下载即可&#xff1a;Download FileZilla Serve…

stimulsoft report for js vue3使用

项目后端使用的java&#xff0c;试验过积木报表&#xff08;web界面类型的&#xff09;、JasperReport&#xff08;.jasper报表文件&#xff09;、stimulsoft web版本&#xff08;.mrt报表文件&#xff09; 我们的项目是前后端分离的&#xff0c;用积木报表&#xff08;开箱即…

Spring boot 集成netty实现websocket通信

一、netty介绍 Netty 是一个基于NIO的客户、服务器端的编程框架&#xff0c;使用Netty 可以确保你快速和简单的开发出一个网络应用&#xff0c;例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程&#xff0c;例如&#xff1a;基于TCP和U…

【研发日记】,Matlab/Simulink开箱报告(十)——Requirements Toolbox

前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;五&#xff09;——S-Fuction模块(C MEX S-Function)》 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;六&#xff09;——S-Fuction模块&#xff08;TLC&#xff09;》 见《开…

狂揽Github—start19.7k☆开源OCR—Umi-OCR

文章目录 背景Umi-OCR—源码下载Umi-OCR—可执行程序下载页面介绍截图OCR识别批量OCR识别批量文档二维码全局设置 总结&#xff1a; 背景 大家都知道我是一个Python办公自动化的小小程序员&#xff0c;经常收集一些免费开源的OCR供大家使用&#xff0c;目前我已经写出来多家OCR…

集智书童 | 炸裂 !轻量化YOLO | ShuffleNetv2与Transformer结合,重塑YOLOv7成就超轻超快YOLO

本文来源公众号“集智书童”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;炸裂 &#xff01;轻量化YOLO | ShuffleNetv2与Transformer结合&#xff0c;重塑YOLOv7成就超轻超快YOLO 随着移动计算技术的迅速发展&#xff0c;在移动…

flutter入门

本文真对 Flutter 的技术特性&#xff0c;做了一些略全面的入门级的介绍&#xff0c;如果你听说过Flutter&#xff0c;想去了解它&#xff0c;但是又不想去翻厚厚的API&#xff0c;那么本文就是为你准备的。 随着纯客户端到Hybrid技术&#xff0c;到RN&Weex&#xff0c;再…

【OpenGL手册13】 光照贴图

目录 一、说明二、漫反射贴图三、镜面光贴图四、采样镜面光贴图练习 一、说明 在上一节中&#xff0c;我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观&#xff0c;但是这仍不能对一个…

C#重新认识笔记_ FixUpdate + Update

C#重新认识笔记_ FixUpdate Update Update: 刷新频率不一致,非物理对象的移动&#xff0c;简单的刷新可用&#xff0c; FixedUpdate: 刷新频率一致,按照固定频率刷新&#xff0c;一般调用FixedUpdate之后&#xff0c;会立即进入必要的物理计算中,因此&#xff0c;任何影响刚…

层次式架构设计

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/136660435 一. 概述 1、软件体系结构为软件系统提供了结构、行为和属性的高级抽象&#xff0c;由构成系统的元素描述这些元素的相互作用、指导元素集成的模式以及这些模式的约束组成。层次式体系结构设…

王庆:当下股市过于悲观,A股、港股基本完成补跌和普跌过程,逆向布局时机已到

核心观点&#xff1a; 1、房地产对中国经济增长拖累最严重的时期正在过去...密切关注真正拐点的出现。 2、当前资本市场从价格表现上来讲&#xff0c;表现的远远超过了基本面所决定的悲观程度。 由于当前资本市场过于悲观&#xff0c;那么反过来就是孕育着机会。 3、我们判…

leetcode刷题(javaScript)——分治思想(二分查找、快速排序)相关场景题总结

分治思想是一种将问题分解成更小的子问题&#xff0c;然后解决子问题并将结果合并的算法设计策略。二分查找、快速排序和折半查找都属于分治思想的经典算法。在leetcode里&#xff0c;分治思想一般结合其他场景出现&#xff0c;构成复合型题目。但是在看题时一定要了解能否用分…