【C++笔记】vector使用详解及模拟实现

前言

各位读者朋友们,大家好!上期我们讲了string类的模拟实现,这期我们开启vector的讲解。

一.vector的介绍及使用

1.1 vector的介绍

vector的文档
使用STL的三个境界:能用、明理、能扩展,下面学习vector,我们也按照这个境界去学习。

1.2 vector的使用

我们在学vector的时候,一定要看文档:vector的文档vector在实际中非常重要,在实际中我们熟悉常用的接口即可,下面给出了常用的需要掌握的接口。

1.2.1vector的定义

在这里插入图片描述
vector是可以改变大小的数组序列容器,也就是数据结构的顺序表。

constructor构造函数声明接口说明
vector()重点无参构造
vector (size_type n, const value_type& val = value_type())构造并初始化n个val
vector(const vector& x);重点拷贝构造
vector(InputIterator first,InputIterator last);使用迭代器进行初始化构造
  • 无参构造
    在这里插入图片描述
  • 构造并初始化n个val
    在这里插入图片描述
  • 迭代器区间构造
    在这里插入图片描述

1.2.2 vector iterator的使用

iterator的使用接口说明
begin + end (重点)获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 反向迭代器
    在这里插入图片描述

1.2.3 vector空间增长的问题

容器空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize(重点)改变vector的size
reserve(重点)改变vector的capacity

capacity
在这里插入图片描述

// 测试vector的默认扩容机制
void TestVectorExpand()
{
 size_t sz;
 vector<int> v;
 sz = v.capacity();
 cout << "making v grow:\n";
 for (int i = 0; i < 100; ++i) 
 {
 v.push_back(i);
 if (sz != v.capacity()) 
 {
 sz = v.capacity();
 cout << "capacity changed: " << sz << '\n';
 }
 }
}

在这里插入图片描述
可以看出在VS环境下,编译器是1.5倍扩容的,而在g++环境中是2倍扩容的。

resize
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果在resize时,我们没有给初值,会使用默认构造给的值。

  • reserve
    在这里插入图片描述
    在n>capacity时,会扩容,但是在n<capacity时,vector的reserve在任何环境中都不会缩容。但是string在这种情况下,g++环境中会进行缩容。
int main()
{
	vector<int> v(10, 520);
	v.reserve(20);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.reserve(15);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.reserve(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	return 0;
}

在这里插入图片描述
g++环境:
在这里插入图片描述

1.2.4 vector的增删查改

vector的增删查改接口说明
push_back尾插
pop_back尾删
find查找(这个是算法模块实现,不是vector的成员接口)
insert在pos位置之前插入的数据
erase删除pos位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问
  • push_back在这里插入图片描述
int main()
{
	vector<int> v(1, 520);
	v.push_back(1314);
	return 0;
}

在这里插入图片描述

  • pop_back
    在这里插入图片描述
int main()
{
	vector<int> v(1, 520);
	v.pop_back();
	return 0;
}

在这里插入图片描述

  • find
    在这里插入图片描述
    find函数找出第一个和val相等的元素,并返回这个元素的迭代器,如果没有找到就返回last(最后一个元素下一个位置的迭代器)。
int main()
{ 
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(520);
	v.push_back(1314);
	v.push_back(3);
	v.push_back(7);
	vector<int>::iterator it = find(v.begin(), v.end(), 520);
	cout << *it << endl;

	it = find(v.begin(), v.end(), 10);
	if (it == v.end())
	{
		cout << "没找到,返回最后一个元素下一个位置的迭代器" << endl;
	}
	return 0;
}

在这里插入图片描述

  • insert
    在这里插入图片描述
    这里的插入都是使用的迭代器
    iterator insert (iterator position, const value_type& val)
int main()
{
	vector<int> v(5, 520);
	v.insert(v.begin() + 3, 1314);
	for (auto a : v)
	{
		cout << a << ' ';
	}
	cout<<endl;
	return 0;
}

在第三个位置之前插入1314
在这里插入图片描述
void insert (iterator position, size_type n, const value_type& val)

int main()
{
	vector<int> v(5, 520);
	v.insert(v.begin() + 3, 2,1314);
	for (auto a : v)
	{
		cout << a << ' ';
	}
	cout << endl;
	return 0;
}

在这里插入图片描述
void insert (iterator position, InputIterator first, InputIterator last)

int main() 
{
	vector<int> v1(5, 520);
	vector<int> v2(5, 1314);
	v1.insert(v1.begin() + 3, v2.begin() + 3, v2.end());// [begin,end)
	for (auto a : v1)
	{
		cout << a << " ";
	}
	cout << endl;
	return 0;
}

在第三个位置之前插入迭代器区间,左闭右开
在这里插入图片描述

在这里插入图片描述

  • erase
    在这里插入图片描述
    erase (iterator position)
int main()
{
	vector<int> v1(5, 520);
	v1.push_back(1314);
	for (auto a : v1)
	{
		cout << a << " ";
	}
	cout << endl;
	v1.erase(v1.end() - 1);
	for (auto a : v1)
	{
		cout << a << " ";
	}
	cout << endl;
	return 0;
}

删除最后一个位置的元素
在这里插入图片描述
erase (iterator first, iterator last)
在这里插入图片描述
依旧是左闭右开区间

int main()
{ 
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(520);
	v.push_back(1314);
	v.push_back(3);
	v.push_back(7);
	v.erase(v.begin() + 1, v.end() - 1);
	for (auto a : v)
	{
		cout << a << " ";
	}
	return 0;
}

在这里插入图片描述

  • swap
    在这里插入图片描述
int main() {
	vector<int> v1(2, 520);
	vector<int> v2(4, 1314);
	cout << "交换前:" << endl;
	for (auto a : v1) {
		cout << a << " ";
	}
	cout << endl;
	for (auto a : v2) {
		cout << a << " ";
	}
	cout << endl;
	v1.swap(v2);
	cout << "交换前:" << endl;
	for (auto a : v1) {
		cout << a << " ";
	}
	cout << endl;
	for (auto a : v2) {
		cout << a << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

  • operator[]
    在这里插入图片描述
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(520);
	v.push_back(1314);
	v.push_back(3);
	v.push_back(7);
	for (int i = 0; i < v.size(); ++i)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

  • 流提取和流插入
    vector是不支持流插入和流提取重载的
vector<int> v;
//流提取单个数据
int x;
cin >> x;
v.push_back(x);
// 多组数据
vector<int>v1(3, 0);
for (int i = 0; i < 3; ++i)
{
	cin >> v1[i];
}
// 流插入
for (auto a : v1)
{
	cout << v1[i] << " ";
}

1.3 vector类型存储

在这里插入图片描述
vcector可以看作顺序表,它不仅可以存储内置类型,也可以存储自定义类型。

1.3.1 vector< string >

int main()
{
	vector<string> v;
	string s("5201314");
	v.push_back(s);

	v.push_back("1314520");
	for (auto a : v)
	{
		cout << a << " ";
	}
	cout << endl;
	return 0;
}

但是这样在范围for遍历打印的时候会进行string类的拷贝给a,这样效率会很低,所以我们可以使用引用,不修改数据的情况下加上const。
在这里插入图片描述

1.3.2 vector<vector< int >>

vector<vector< int >>可以看成是二维数组,下面通过vector的结构看一下:
vector可以看成是顺序表:
在这里插入图片描述
在这里插入图片描述
其实编译器这里实例化出了两个模板,一个是vector<vector< int >> ,一个是vector< int >。
在这里插入图片描述
所以我们在遍历二维数组的时候实际上是调用了两次operator[]
在这里插入图片描述
第一次调用返回vector<vector< int >>& ,第二次调用返回int&,也就是第一次调用找到存在整个vector的对应位置的vector,第二次调用返回对应位置的vector的相应位置的元素。
在这里插入图片描述
上面理解了之后我们看一道OJ题杨辉三角
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv(numRows);// 这里未传实参,
        // 使用缺省值vector<int>
        for (int i = 0; i < numRows; ++i)
        {
            vv[i].resize(i + 1, 1); //将所有值都初始化为1
        }
        for (int i = 2; i < numRows; ++i)// 从下标为2的那一行开始
        {
            for (int j = 1; j < vv[i].size() - 1; ++j)// 不计算第一个和最后一个数据
                // vv[i].size 是当前一行的数据个数
            {
                vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
            }
        }
        return vv;
    }
};

二. vector深度剖析及模拟实现

在这里插入图片描述
通过vector的源码,我们看到,vector的底层实际有3个成员变量,它们分别对应

startbegin(顺序表的头)
finishend(顺序表最后一个元素的下一个位置)
end_of_storagecapacity(顺序表的空间)

在这里插入图片描述
下面开始模拟实现vector:
vector的底层

namespace Yuey
{
	template <class T>
	class vector
	{
	public:
		typedef T* iterator;

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

2.1 push_back

template <class T>
class vector
{
public:
	typedef T* iterator;
	size_t size()
	{
		return _finish - _start;// [_statr, _finish) 左闭右开相减得元素个数
	}
	size_t capacity()
	{
		return _end_of_storage - _start;// [_statr, _end_of_storage) 左闭右开相减得元素个数
	}
	void reserve(size_t n)
	{
		if (n > capacity())
		{
			// 开空间拷贝
			T* tmp = new T[n];
			memcpy(tmp, _start, size() * sizeof(T));
			delete[] _start;
			_start = tmp;
			_finish = _start + size();
			_end_of_storage = _start + n;
		}
	}
	T& operator[](size_t n)
	{
		assert(n < size());
		return _start[n];
	}
	void push_back(const T& x)
	{
		if (size() == capacity())
		{
			reserve(capacity() == 0 ? 4 : 2 * capacity());
		}
		*_finish = x;
		++_finish;
	}
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _end_of_storage = nullptr;
};

为了实现push_back这一功能,我们实现了size()、capacity()、reserve()函数,为了方便测试又实现了[]的重载。下面测试一下push_back这一接口:
在这里插入图片描述
发现挂了,调试看一下
在这里插入图片描述

void reserve(size_t n)
{
	if (n > capacity())
	{
		// 开空间拷贝
		size_t old_size = size(); //记录扩容前的size
		T* tmp = new T[n];
		memcpy(tmp, _start, size() * sizeof(T));
		delete[] _start;
		_start = tmp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
	}
}

2.2 迭代器

typedef T* iterator;
const typedef T* const_iterator;
iterator begin()
{
	return _start;
}
iterator end()
{
	return _finish;
}
const_iterator begin() const
{
	return _start;
}
const_iterator end() const
{
	return _finish;
}

在这里插入图片描述

2.3 vector的打印

void print_vector(const vector<int>& v)
{
	vector<int>::const_iterator  it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
}

这样写只能打印vector< int >类型的,没法打印存储其它类型的vector,但是打印的逻辑是一样的,因此我们还可以再套一层模板给打印函数:

	template <class T>
	void print_vector(const vector<T>& v)
	{
	    typename vector<T>::const_iterator it = v.begin();
		// auto it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

在这里插入图片描述

2.4 pop_back

bool empty()
{
	return _start == _finish;
}
void pop_back() 
{
	assert(!empty());// 尾删但不能为空
	--_finish;
}

在这里插入图片描述

2.5 迭代器失效

1. 在指定位置插入一个数据

在这里插入图片描述
在这里插入数据的时候没有进行扩容,程序可以正常运行,但是如果我们进行扩容,程序还能正常运行吗?
在这里插入图片描述
程序不能正常运行,这就涉及到了迭代器失效的问题
在这里插入图片描述
调试发现pos是大于end的,while循环不能执行
在这里插入图片描述

void insert(iterator pos, const T& val)
{
	// 扩容
	if (size() == capacity())
	{
		size_t len = pos - _start;
		reserve(capacity() == 0 ? 4 : 2 * capacity());
		pos = _start + len;
	}
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}
	*pos = val;
	++_finish;
}

在这里插入图片描述
改进之后就解决了这个问题。

void test_vector6()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	int x;
	cin >> x;
	vector<int>::iterator p = find(v.begin(), v.end(), x);
	if (p != v.end())
	{
		v.insert(p, 520);
		(*p) *= 10;
	}
	print_vector(v);
}

2. insert之后的迭代器失效

想要通过find函数找一个数值,并在这个数据的位置插入520,然后让pos位置的值乘10
在这里插入图片描述
在这里插入图片描述

发现并没有实现需求,这是因为pos还是指向原空间的位置,没有指向扩容后的空间,虽然我们上面处理了这种情况,但是是对形参进行的修改,实参是不会改变的。那形参使用引用可不可以解决这个问题呢?答案是可以的,但是在这里插入图片描述
这种情况就是不适用了,返回值存在一个const临时对象中,调用会存在权限放大的问题,那将普通引用改为const引用不就好了?改为const引用之后pos的值就不能更改了。
在这里插入图片描述
库中的insert函数返回了新插入元素的迭代器,我们想修改可以对返回值进行操作后再修改。
改进的insert函数:

	iterator insert(iterator pos, const T& val)
	{
		assert(pos >= _start && pos <= _finish);
		// 扩容
		if (size() == capacity())
		{
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : 2 * capacity());
			pos = _start + len;
		}
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end + 1) = *end;
			--end;
		}
		*pos = val;
		++_finish;

		return pos;
	}

将我们的insert函数也加上返回值之后再去实现想要的功能:
在这里插入图片描述
实现了。
上面这种情况也是迭代器失效的一种,对于使用完insert之后的迭代器就失效了,不要直接进行访问,如果需要访问就去更新迭代器的值!!!
在这里插入图片描述
vs下进行了强制的检查,使用insert之后的迭代器会报错,不管有没有扩容。

3. erase时迭代器失效

删除指定位置的元素:挪动数据从前往后挪动

void erase(iterator pos)
{
	assert(pos >= _start && pos < _finish);
	iterator it = pos + 1;
	while (it < _finish)
	{
		*(it - 1) = *it;
		++it;
	}
	--_finish;
}

这里我们写一个程序测试一下,要求删除偶数:

void test_vector8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	print_Container(v);

	auto it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		++it;
	}
	print_Container(v);
}

在这里插入图片描述
三种情况两种不正确,我们分析一下:

  • 对于第二种
    在这里插入图片描述
  • 第三种情况:
    在这里插入图片描述
    来看一下库中的erase是怎么实现的
    在这里插入图片描述
    和insert一样都带回了一个迭代器,它返回的是被函数调用擦除的最后一个元素之后的元素的新位置的迭代器,也就是pos位置
iterator erase(iterator pos)
{
	assert(pos >= _start && pos < _finish);
	iterator it = pos + 1;
	while (it < _finish)
	{
		*(it - 1) = *it;
		++it;
	}
	--_finish;
	return pos;
}

这样修改之后,上面的两种情况都可以解决了

	void test_vector8()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		print_Container(v);

		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				++it;
			}
		}
		print_Container(v);
	}

几乎所有的erase之后的迭代器都失效了,要更新之后再使用

2.6 resize

在这里插入图片描述
这里的val是使用了匿名对象,调用默认构造函数,对于内置类型和自定义类型都适用,看一下内置类型的默认构造:
在这里插入图片描述

void resize(size_t n, T val = T())
{
	if (n < size())
	{
		_finish = _start + n;
	}
	else // n >= _finish
	{
		reserve(n);// 自动扩容
		while (_finish < _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
}

2.7 构造函数

  • 拷贝构造
vector(const vector<T>& v)
{
	reserve(v.size());
	for (auto& e : v)
	{
		push_back(e);
	}
}

在这里插入图片描述
调用会报错,之前为什么没有报错呢?我们不写构造函数,编译器会帮我们生成默认构造函数,但是我们写了拷贝构造函数,也就相当于我们写了构造函数,编译器就不会帮我们生成默认构造函数了。

  • 迭代器区间构造
    在这里插入图片描述
    这个构造支持不同容器之间的构造
	template<class InputIterator>
	vector(InputIterator first, InputIterator last)
	{
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

在这里插入图片描述
类模板的成员函数也可以作为函数模板

  • n个值构造
vector(size_t n,T val = T())
{
	reserve(n);
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

在这里插入图片描述
下面多测几组:
在这里插入图片描述

我们可以将size_t改为int来解决这个问题。
在这里插入图片描述

2.8 赋值运算符重载

void swap(vector<T>& tmp)
{
	std::swap(_start, tmp._start);
	std::swap(_finish, tmp._finish);
	std::swap(_end_of_storage, tmp._end_of_storage);
}
vector<T>& operator=(vector<T> tmp)
{
	swap(tmp);
	return *this;
}

2.9 扩容时的拷贝问题

在这里插入图片描述
上面出现的问题是扩容后出现了乱码,这是怎么导致的呢?
在这里插入图片描述
这里发现tmp和_start的指向的空间是同一个。
在这里插入图片描述
在扩容时,memcpy拷贝是一个字节一个字节的拷贝,将v1中的内容拷贝到tmp中,由于v1中存的是string类,存储的是指向字符串的指针,将指针拷贝给tmp是浅拷贝,拷贝结束后,调用delete函数, string类会调用析构函数,_str指向的空间被释放,因此tmp拷贝过来的_str被释放了就无法访问,就会出现乱码,然后_start指向的空间也被释放。想要解决这个问题就只能走深拷贝。

void reserve(size_t n)
{
	if (n > capacity())
	{
		// 开空间拷贝
		size_t old_size = size(); //记录扩容前的size
		T* tmp = new T[n];
		//memcpy(tmp, _start, old_size * sizeof(T));
		int i = 0;
		while (i < old_size)
		{
			tmp[i] = _start[i];
			++i;
		}
		delete[] _start;
		_start = tmp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
	}
	// 出作用域tmp销毁
}

这里用了赋值运算符重载,完成了深拷贝,调用的是string类的赋值运算符重载
在这里插入图片描述
原空间被释放了也不影响现在的空间。

结语

以上就讲完了vector的使用及模拟实现,希望对大家有所帮助,感谢大家的阅读,欢迎大家批评指正!

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

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

相关文章

【环境配置】macOS配置jdk与maven

配置jdk与maven 配置jdk与切换java版本命令 maven安装与配置国内镜像源 用到的命令 # 进入 JDK 安装目录 cd /Library/Java/JavaVirtualMachines# 查看文件 ls ➜ jdk-1.8.jdk jdk-11.jdk# 查看路径 pwd ➜ /Library/Java/JavaVirtualMachines# 打开环境变量配置文件 vi &…

借助Excel实现Word表格快速排序

实例需求&#xff1a;Word中的表格如下图所示&#xff0c;为了强化记忆&#xff0c;希望能够将表格内容随机排序&#xff0c;表格第一列仍然按照顺序编号&#xff0c;即编号不跟随表格行内容调整。 乱序之后的效果如下图所示&#xff08;每次运行代码的结果都不一定相同&#x…

Essential Cell Biology--Fifth Edition--Chapter one (6)

1.1.4.4 Internal Membranes Create Intracellular Compartments with Different Functions [细胞膜形成具有不同功能的细胞内隔室] 细胞核、线粒体和叶绿体并不是真核细胞中唯一的膜包围细胞器。细胞质中含有大量的[ a profusion of]其他细胞器&#xff0c;这些细胞器被单层膜…

动态规划29:673. 最长递增子序列的个数

动态规划解题步骤&#xff1a; 1.确定状态表示&#xff1a;dp[i]是什么 2.确定状态转移方程&#xff1a;dp[i]等于什么 3.初始化&#xff1a;确保状态转移方程不越界 4.确定填表顺序&#xff1a;根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接&#xff1a;673.…

M-LAG 技术笔记

M-LAG 简介 M-LAG&#xff08;Multichassis link aggregation&#xff0c;跨设备链路聚合&#xff09;将两台物理设备在聚合层面虚拟成一台设备来实现跨设备链路聚合&#xff0c;从而提供设备级冗余保护和流量负载分担。 M-LAG 基础概念 如 图1-1 所示&#xff0c;Device A …

C语言-指针及变量的概念与使用

1、指针的概念 计算机中所有的数据都必须放在内存中&#xff0c;不同类型的数据占用的字节数不一样&#xff0c;例如 int 占用 4 个字节&#xff0c;char 占 用 1 个字节。为了正确地访问这些数据&#xff0c;必须为每个字节都编上号码&#xff0c;就像门牌号、身份证号一样&a…

tauri开发中,使用node将png图片转成苹果的icns图标格式,解决tauri icon生成的mac图标过大问题

在tauri开发中&#xff0c;我们使用tauri icon生成的图标在windows上是正常的&#xff0c;但是在mac上就显示过大&#xff0c;也可以看tauri的issue&#xff1a;[v2]When using the Tauri Icon to generate icons, it is always larger than other icons in Mac tauri-apps/ta…

【Docker】Mac安装Docker Desktop导致磁盘剩余空间较少问题如何解决?

目录 一、背景描述 二、解决办法 三、清理效果 四、理论参考 解决方法 1. 清理未使用的 Docker 镜像、容器和卷 2. 查看 Docker 使用的磁盘空间 3. 调整 Docker 的存储位置 4. 增加磁盘空间 5. 调整 Docker Desktop 配置 6. 使用 Docker 清理工具&#xff08;例如 D…

微信小程序navigateTo:fail webview count limit exceed

theme: nico 你们好&#xff0c;我是金金金。 场景 uniapp编写微信小程序&#xff0c;使用uni.navigateTo跳转的过程中报错如下&#xff1a; 报错意思也非常明显了&#xff1a;errMsg":"navigateTo:fail webview 数量超出限制 排查 排查之前我先贴一下代码 代码非…

类与对象(2)---类的6个默认成员函数

1.类的6个默认成员函数 任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生成的成员函数称为默认成员函数。 2.构造函数 2.1构造函数特性 构造函数的主要任务是初始化对象。 它有如下特…

[OpenGL]使用OpenGL实现透明效果

一、简介 本文介绍了如何使用OpenGL实现透明效果&#xff08;transparent&#xff09;&#xff0c;并在最后给出了全部的代码。 在实现透明效果时&#xff0c;使用OpenGL中的混合&#xff08;Blend&#xff09;功能&#xff0c;根据纹理贴图的 alpha 分量将各像素&#xff08;…

【实用技能】ASP.NET Core:在同一个 Razor 视图中使用文档编辑器和查看器

Essential Studio for ASP.NET Core UI控件库是构建应用程序所需的卓越套件&#xff0c;提供支持的 ASP.NET Core 工具包拥有超过 85 个组件&#xff0c;包含构建业务线应用程序所需的一切&#xff0c;包括数据网格、图表、甘特图、图表、电子表格、时间表、数据透视网格等流行…

Android从Drawable资源Id直接生成Bitmap,Kotlin

Android从Drawable资源Id直接生成Bitmap,Kotlin val t1 System.currentTimeMillis()val bmp getBmpFromDrawId(this, R.mipmap.ic_launcher_round)Log.d("fly", "1 ${bmp?.byteCount} h${bmp?.height} w${bmp?.width} cost time${System.currentTimeMillis…

【JavaScript】LeetCode:96-100

文章目录 96 单词拆分97 最长递增子序列98 乘积最大子数组99 分割等和子集100 最长有效括号 96 单词拆分 动态规划完全背包&#xff1a;背包-字符串s&#xff0c;物品-wordDict中的单词&#xff0c;可使用多次。问题转换&#xff1a;s能否被wordDict中的单词组成。dp[i]&#x…

【扩散——BFS】

题目 代码 #include <bits/stdc.h> using namespace std; const int t 2020, off 2020; #define x first #define y second typedef pair<int, int> PII; int dx[] {0, 0, 1, -1}, dy[] {-1, 1, 0, 0}; int dist[6080][6080]; // 0映射到2020&#xff0c;2020…

柯桥生活英语口语学习“面坨了”英语怎么表达?

“面坨了”英语怎么表达&#xff1f; 要想搞清楚这个表达&#xff0c;首先&#xff0c;我们要搞明白“坨”是啥意思&#xff1f; 所谓“坨”就是指&#xff0c;面条在汤里泡太久&#xff0c;从而变涨&#xff0c;黏糊凝固在一起的状态。 有一个词汇&#xff0c;很适合用来表达这…

IOT物联网低代码可视化大屏解决方案汇总

目录 参考来源云服务商阿里云物联网平台产品主页产品文档 开源项目DGIOT | 轻量级工业物联网开源平台项目特点项目地址开源许可 IoTGateway | 基于.NET6的跨平台工业物联网网关项目特点项目地址开源许可 IoTSharp | 基于.Net Core开源的物联网基础平台项目特点项目地址开源许可…

CSP-X2024山东小学组T2:消灭怪兽

题目链接 题目名称 题目描述 怪兽入侵了地球&#xff01; 为了抵抗入侵&#xff0c;人类设计出了按顺序排列好的 n n n 件武器&#xff0c;其中第 i i i 件武器的攻击力为 a i a_i ai​&#xff0c;可以造成 a i a_i ai​ 的伤害。 武器已经排列好了&#xff0c;因此不…

信息收集—JS框架识别泄露提取API接口泄露FUZZ爬虫插件项目

前言 免杀结束了&#xff0c;我们开个新的篇章——信息收集。为什么我一开始先写信息收集的文章呢&#xff0c;是因为现在我才发现我的信息收集能力其实有点弱的&#xff0c;所以呢开始知不足&#xff0c;而后进。 什么是JS JS就是JavaScript的简称&#xff0c;它和Java是没…

性能调优专题(9)之从JDK源码级别解析JVM类加载机制

一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 package com.tuling.jvm;public class Math {public static final int initData 666;public static User user new User();public int compute() {…