C++ STL学习
C++ STL学习
容器库概览
对可以保存在容器中的元素的限制
容器需要支持拷贝操作,因此,保存在容器中的元素要能够支持拷贝操作。可以为不支持特定操作需求的元素类型定义容器,但是这样就只能使用容器那些不要求容器元素有特定操作的操作类型。
一般来说,每种容器都定义在一个与容器名相同的头文件中,对大多数容器,但不是所有容器,需要提供元素类型来生成特定的容器。
特别的,容器的元素的类型可以是另一种容器,只要在外层容器的<>中指定内层容器的类型即可。在老版本的编译器中,这种以其他容器为元素的容器,在声明或者定义时,需要在内层容器的<>和外层容器的<>之间插入空格,如vector<vector<int> > v1,新版本的编译器则无此要求。
容器支持的操作
容器类型支持的操作可以根据容器的大类分成一定的层次:
- 顺序容器和关联容器都支持的操作;
- 仅部分顺序容器支持的操作;
- 仅部分关联容器支持的操作;
- 少数特殊的容器,如
array、forward_list或者无序容器支持的操作。
所有容器都支持的操作或容器成员
下表是所有容器都支持的操作。
| 支持操作或成员 | 说明 |
|---|---|
| 类型别名 | |
iterator |
此容器类型的迭代器 |
const_iterator |
const类型的iterator |
difference_type |
有符号整型,足够表示此种容器的两个迭代器之间的距离 |
value_type |
此容器的元素的类型 |
size_type |
无符号整型,足够表示此种容器可能的最大容器大小 |
reference |
元素的左值类型,等价于value_type & |
const_reference |
const版本的reference,等价于const value_type & |
| 构造函数 | |
C c |
默认构造函数,构造空容器 |
C c1(c2) |
使用c2拷贝构造c1,c2和c1的容器类型和元素类型必须一样 |
C c1 = c2 |
使用c2拷贝构造c1,c2和c1的容器类型和元素类型必须一样 |
C c(b, e) |
使用迭代器b和e的半开区间[b,e)拷贝构造c,不要求容器类型和元素类型一样,只要求[b, e)中的元素和c的元素类型相容 |
C c{a, d, ...} |
列表初始化c |
C c = {a, b, ...} |
将c初始化为列表中元素的拷贝 |
赋值和swap |
|
c1 = c2 |
将c1种的元素替换为c2中的元素 |
c1 = {a, b, ...} |
将c1中的元素替换为列表中的元素(《C++ Primer》中说array不支持此操作,其实使用g++测试发现array是支持此操作的) |
c1.swap(c2) |
交换c1和c2中的元素 |
swap(c1, c2) |
等价于c1.swap(c2) |
| 添加、删除元素 | |
(array不支持,在不同的容器中,接口可能不同) |
|
c.insert(args) |
将args中的元素拷贝进c |
c.empalce(inits) |
使用inits在c中原地构造一个c的元素 |
c.erease(args) |
将args指定的元素从c中删除 |
c.clear() |
删除c中的所有元素,返回void |
| 比较操作 | |
==, != |
相等(不相等)的关系运算符 |
<, <=, >=, >(无序关联容器不支持) |
小于、不大于、不小于、大于的关系运算符 |
| 获取迭代器 | |
c.begin(), c.end() |
指向c的首元素和尾元素后一位置的迭代器 |
c.cbegin(), c.cend() |
指向c的首元素和尾元素后一位置的const类型的迭代器 |
| 反向容器的额外成员 | |
(forward_list不支持) |
|
reverse_iterator |
按逆序寻址元素的迭代器 |
const_reverse_iterator |
const版本的reverse_ietator |
c.rbegin()、c.rend() |
指向c的首元素之前位置和尾元素的迭代器 |
c.crbegin()、c.crend() |
const版本的c.rbegin()、c.rend() |
迭代器
称某种数据类型是迭代器,当且仅当此类型支持一套操作,此套操作可以访问元素或者从某个元素移动到另一个元素。
迭代器有着相同的接口:如果某个迭代器可以提供某种操作,那么所有提供相同操作的迭代器在实现上都是相同的。
迭代器的公共操作
下表是所有的迭代支持都的操作。
| 操作 | 说明 |
|---|---|
iter1 == iter2 |
当iter1和iter2指向同一个容器中的同一个元素或尾后迭代器时成立 |
iter1 != iter2 |
等价于!(iter1 == iter2) |
迭代器的类型
迭代器的const属性
所有的拥有迭代器的标准库类型使用两个关键字来表示迭代器类型:
iterator表示通过此类迭代器可以对迭代器指向的元素进行读写;const_iterator表示通过此类型只能迭代器指向的元素进行读操作,不可改变迭代器指向的元素的值。
对于const类型的容器,只能使用const_iterator的迭代器。
迭代器的操作类型
可以按照迭代器的操作类型将迭代器分成5类,这5类迭代器形成一种操作层次:除输出迭代器外(输出迭代器可以看做输入迭代器在功能上的补集——输出迭代器只写而不读元素),高层次的迭代器支持低层次的迭器所支持的所有操作。它们所支持的操作范围可以用下列的不等式表示:
随机访问迭代器 > 双向迭代器 > 前向迭代器 > (输入迭代器(读元素),输出迭代器(写元素))读写元素 = 输入迭代器(读元素)+ 输出迭代器(写元素)
| 迭代器类型 | 支持的操作 |
|---|---|
| 输入迭代器 | 除支持的公共操作外,支持前置的和后置的++,它使输入迭代器指向容器中的下一个元素位置。支持*解引用运算符,它只会出现在=右侧,用于读取元素。支持->箭头运算符,等价于(*it).mem,它解引用迭代器,并提取被指向对象的成员。输入迭代器只能顺序读取容器中的元素,递增迭代器可能使所有指向流的迭代器失效 |
| 输出迭代器 | 除支持的公共操作外,支持前置的和后置的++,它使输入迭代器指向容器中的下一个元素位置。支持*解引用运算符,它只会出现在=左侧,用于写元素。只能向一个输出迭代器赋值一次 |
| 前向迭代器 | 支持输入迭代器和输出迭代器的所有操作。只能在序列中沿着一个方向移动。可以多次读写同一个元素 |
| 双向迭代器 | 除支持前向迭代器的所有操作外,还支持前置的和后置的--运算符。可前向、后向移动,读写序列中的元素 |
| 随机访问迭代器 | 除支持双向迭代器的所有操作,还支持迭代器的算术运算。提供在常量时间内访问序列中任意元素的能力 |
随机访问迭代器支持的算术运算如下表所示。
| 算术运算类型 | 含义 |
|---|---|
iter + n |
迭代器加上一个整型数,结果仍然是一个迭代器,指示的位置比iter指示的位置前进了n个单位,结果或是指向一个元素,或者一个尾后迭代器 |
iter - n |
迭代器减去一个整型数,结果仍然是一个迭代器,指示的位置比iter指示的位置后退了n个单位,结果或是指向一个元素,或者一个尾后迭代器 |
iter += n |
迭代器加法的复合赋值语句,将iter的值加上n后再赋值给iter |
iter -= n |
迭代器减法的复合赋值语句,将iter的值减去n后再赋值给iter |
iter1 - iter2 |
迭代器相减的结果是它们之间的距离,表示ite2r向前移动差值个单位后将得到iter1。参与运算的两个迭代器必须是指向同一个容器的迭代器,或是同一个容器的尾后迭代器 |
>, >=, <, <= |
迭代器的关系运算符,若迭代器指向的位置在另一个迭代器指向的位置之前,则说前者小于后者。参与关系运算的两个迭代器必须是指向同一个容器的迭代器,或是同一个容器的尾后迭代器 |
迭代器范围
一个迭代器范围由一对迭代器表示,这个两个迭代器分别指向同一个容器中的元素,或是同一个容器中的尾后位置。这两个迭代器分别用begin和end表示(或是用first和last表示),他们标记了一个容器中的一个元素范围。
通常end或last不会指向容器中的一个元素,而是指向容器中的尾后位置,这样的元素范围被称为左闭合区间,其数学描述为:[begin,end),表示范围自begin起,至end前结束,begin和end可以指向相同的位置,但是不能指向容器首元素之前的位置。
使用左闭合区间的编程假定
标准库使用左闭合区间表示元素范围是因为左闭合区间有下面三种方便的性质,假定begin和end表示一个合法的迭代器范围,则有:
- 范围为空当且仅当
begin == end; - 若
begin != end,则范围内至少存在一个元素; - 可以通过递增
begin使begin == end。
顺序容器
顺序容器的特点是元素在容器中的相对位置和元素加入容器时的先后顺序有关,元素之间的逻辑结构是线型的。
关联容器的特点是元素在容器中的相对位置和与每个元素绑定的关键字有关,元素之间的逻辑结构是非线型的,如树型结构、表型结构或者其他非线型结构。
顺序容器概述
顺序容器的类型和特点
C++标准库提供了如下的顺序容器。
| 容器类型 | 特点 |
|---|---|
vector |
动态数组,逻辑上相邻的元素,在内存中也相邻。支持随机访问元素,不能在直接头部删除和添加元素(借助迭代器指向待删除的元素可以)。在一次添加、删除元素的操作后,在添加、删除位置之后的所有元素在内存中都要移动位置来保持元素连续存储的特点,在一次添加、删除元素后可能容器的容量会改变,所以元素要整体移动到内存中的新位置去,所以适合在尾部添加元素,在其他位置添加元素效率很低 |
string |
和vector类似的容器,和vector之间的区别是string容器元素是字符 |
decque |
双端队列,支持随机访问。在底层实现上,采用链表和动态数组的方式。在头部和尾部添加、删除元素效率高,在其他位置添加、删除元素效率低 。 |
list |
双向链表,不支持随机访问,在逻辑上相邻的元素,在内存中不一定相邻。在任何位置添加、删除元素效率高 |
forward_list |
单向链表,只支持从头部向尾部的单向遍历,不支持随机访问,在逻辑上相邻的元素,在内存中不一定相邻。在任何位置添加、删除元素的效率高。 |
array |
固定大小的数组,支持随机访问,不能添加、删除元素 |
顺序容器在以下两个方面有不同程度的性能折中:
- 向容器中添加和删除元素的开销;
- 非顺序访问元素的开销。
确定使用哪种顺序容器
如无必要,使用vector容器,除非你有充分的理由选择其他容器。
在以下情况时可以考虑使用别的容器:
- 如果程序要求在容器中间添加、删除元素,使用
list或者更forward_list; - 如果要求程序在头尾添加、删除元素,但是不会在中间添加、删除元素,使用
deque。
顺序容器的操作
顺序容器的定义和初始化
除所有容器都支持的容器的定义和初始化操作外,仅有顺序容器(除array外)还支持接受一个大小参数的构造函数,如下表所示。
| 构造函数 | 含义 |
|---|---|
C seq(n) |
seq包含了n个元素,这些元素进行了值初始化,此构造函数是explict属性的 |
C seq(n, t) |
seq包含了n个元素,这些元素通过t进行初始化 |
向顺序容器添加元素
下表是
| 操作 | 含义 |
|---|---|
向容器中添加元素的操作会改变容器的大小,array类型具有固定的大小,因此array不支持添加元素的操作。 |
|
vector和strng不支持push_front、emplace_front操作。forward_list有自己版本的insert和emplace操作,不支持push_back、emplace_back、emplace_front、push_front操作 |
|
c.push_back(t) |
使用t在c的尾部拷贝构造一个元素 |
c.empalce_back(inits) |
使用inits在c的尾部就地构造一个元素 |
c.push_front(t) |
使用t在c的首元素拷贝构造一个元素,容器中原有的所以元素都要后移一个位置 |
c.emplace_front(inits) |
使用inits在c的头部就地构造一个元素,容器中原有的所以元素都要后移一个位置 |
c.insert(p, t),c.insert(p, inits) |
在迭代器p指向的位置之前创建一个值为t或是由inits拷贝初始化的元素,返回一个指向被插入的元素的迭代器 |
c.insert(p, b, e) |
将迭代器范围[b, e)内的元素插入到迭代器p指向的位置前,返回一个指向最新被插入元素的迭代器 |
c.insert(p, n , t) |
在迭代器p指向的位置前插入n个值为t的元素,返回一个指向最新被插入元素的迭代器 |
c.insert(p, il) |
在迭代器p指向的位置前插入一个由{}包围的元素值列表中的元素,返回一个指向最新被插入的元素的迭代器 |
初始化和插入操作的关键概念
- 被插入容器中的元素是传递给容器的元素的拷贝,而不是传递给容器的元素本身,在初始化操作或者插入操作完成后,容器中的元素对象和提供值的元素之间就没有关系了,对容器中的元素对象的操作不会对提供值的元素对象产生影响,反之亦然。
- 在插入操作中,
insert、push操作和emplace操作由效率上的区别。如果传递给容器的参数是容器元素的一个左值,则insert和push操作会调用容器元素的拷贝构造函数,将传给容器的对象拷贝给容器元素的对象来完成插入操作;如果传递给容器的参数是容器元素的一个右值,则可能会调用容器元素对象的移动构造函数(具体情况视编译器的实现而定),而emplace操作会直接调用与传入容器的对象相匹配的构造函数来在容器的内存空间中就地构造元素对象,没有了拷贝操作。 - 在使用
insert进行插入的操作中,迭代器可以是反向迭代器,这样最新被插入的元素是序列中的第一个元素。
访问操作
下表定义了顺序容器对容器中成员的访问操作
| 操作类型 | 含义 |
|---|---|
forward_list不支持back操作;下标和at操作只适用于vector、string、deque、array |
|
c.back() |
返回c中尾元素的引用,若c为空,则行为未定义 |
c.front |
返回c中首元素的引用,若c为空,则行为未定义 |
c[n] |
返回c中下标为n的元素的引用,要求n >= 0 && n <= c.size(),否则行为未定义 |
c.at(n) |
返回c中下标为n的元素的引用,如果下标越界,则抛出out_of_range的异常 |
删除元素的操作
下表定义了顺序容器删除元素的操作
| 操作 | 含义 |
|---|---|
删除操作会改变容器的大小,所有array不适用删除操作。forward_list有自己版本的erease,不支持pop_back。vector、string不支持pop_front |
|
c.pop_back() |
删除c中的首元素,若c为空,则行为未定义。函数返回void |
c.pop_back() |
删除c的尾元素,若c为空,则行为未定义。函数返回void |
c.erease(p) |
删除迭代器p指向的元素,返回一个指向被删除元素下一位置的迭代器,若p指向尾元素,则返回尾后迭代器。若p是尾后迭代器,则函数行为未定义 |
c.erease(b, e) |
删除迭代器范围[b, e)指向的元素,返回一个指向最后一个被删除元素下一位置的迭代器,若e是尾后迭代器,则返回尾后迭代器 |
c.clear() |
清空容器c中所有元素,返回void |
注意:删除元素的成员函数不检查其参数,在删除元素前需要保证被删除的元素是存在的。
特殊的forward_list的操作
在单向链表中删除或者插入一个元素,被删除或者插入元素的前驱节点的链接会发生变化,但是单链表只能单向访问元素,无法从被删除的元素访问到其前驱节点,因此,对于forward_list容器,删除或者插入元素是通过改变待删除或是插入元素的前驱节点实现的。
单链表forward_list定义了一个首前迭代器before_begin(),允许我们删除首元素或是在首元素之前插入元素。
下表是forward_list支持的删除或是插入元素的操作
| 操作 | 含义 |
|---|---|
lst.before_begin() |
返回lst的首前迭代器,它不指向容器中的元素,不能解引用 |
lst.cbefore_begin() |
返回lst的const版本的首前迭代器 |
lst.insert_after(p, t) |
在迭代器p后一位置插入一个值为t的元素,返回一个被插入元素的迭代器 |
lst.inset_after(p, n , t) |
在迭代器p后一位置开始插入n个值为t的元素,返回一个最新被插入的元素的迭代器 |
lst.insert_after(p, b, e) |
在迭代器p后一位置开始插入由迭代器范围[b, e)指向的元素,返回一个指向最新被插入元素的迭代器,若范围为空,则返回p |
lst.inset_after(p, il) |
在迭代器p后一位置开始插入由{}包围的元素列表il,返回一个指向最新被插入的元素的迭代器,若il为空,则返回p |
lst.emplace_after(p, inits) |
在迭代器p后一位置插入由inits在lst内存空间中就地初始化的元素,返回一个指向最新被插入的元素的迭代器 |
lst.erease_after(p) |
删除迭代器p后一位置的元素,返回一个指向被删除元素之后位置的迭代器 |
lst.erase_after(b, e) |
删除由迭代器范围[b, e)指向的元素,返回一个指向最后一个被删除元素之后位置的迭代器,若不存在这样的元素,则返回尾后迭代器 |
注意:上述的和迭代器p相关的操作,都需要程序员保证迭代器有效,否则函数行为未定义。
改变容器的大小
顺序容器支持的容器大小操作如下表所示。
| 操作 | 含义 |
|---|---|
array不支持容器的大小操作,因为array容器的大小固定,是类型的一部分 |
|
c.resize(n) |
调整c的大小为n个元素,若n > c.size(),则多余的元素会进行值初始化;若n < c.size(),则多余的元素会被舍弃 |
c.resize(n, t) |
调整c的大小为n个元素,若n > c.size(),则多余的元素的值会被初始化为值t;若n < c.size(),则多余的元素会被舍弃 |
vector对象的空间增长策略
对于string和vector容器,因为容器要保证逻辑上相邻的元素在内存中也相邻,所以,当容器中没有空间能再容纳新元素时,向容器中插入新元素,将导致容器重新申请内存空间。为了保证效率,标准库实现时,申请新的内存空间后的总容量将比实际需要的容量要大。容器会将旧元素全部拷贝到新的内存空间中去,再在新的内存空间中插入新元素。
管理容量的成员函数
下表是顺序容器用来管理容器容量的成员函数。
| 操作 | 含义 |
|---|---|
shrink_to_fit只适用于vector、deque和string容器,capacity和reserve只适用于vector和string |
|
c.shink_to_fit |
将容器c的容量缩小到与c.size()相同大小,并不保证可以回退多余的空间,具体的实现可以忽略此请求 |
c.capacity() |
不重新分配内存空间的情况下当前容器c可以保存的元素的最大数量 |
c.reserve(n) |
使容器c分配至少能容纳n个元素的内存空间,只有当n > c.size()时才会重新分配内存空间 |
注意:vector和string的内存增长策略随着实现的不同而不同,通常的做法是:在需要重新分配内存空间时将当前的内存空间的容量翻倍。所有的内存空间分配策略遵循着一个原则:使用push_back向容器中添加元素时有高效率,在一个初始为空的vector上调用n此push_back来创建一个n个元素的容器时,所花费的时间不能超过n的常数倍。
容器操作可能使迭代器失效
向容器中插入或删除元素可能会导致指向容器元素的迭代器、指针或引用失效,使用失效的指针、引用或是迭代器会引发严重的程序错误。
容器中插入或删除元素后指向容器的迭代器、指针和引用的情况如下表。
| 容器类型 | 插入元素 | 删除元素 |
|---|---|---|
vector和string |
若重新分配内存空间,迭代器、指针和引用都会失效;未重新分配内存空间,指向插入元素位置之前的迭代器、引用和指针仍然有效,插入位置之后迭代器、指针和引用会失效 | 指向被删除元素之后的迭代器、指针和引用会失效 |
list和forward_list |
全部有效 | 全部有效 |
deque |
在除首尾位置外的其他位置插入元素会导致迭代器、指针和引用失效;在首元素位置插入元素将会导致迭代器失效,但是指针和引用仍然有效 | 删除首元素,全部有效;删除尾元素,尾后迭代器失效;在首尾位置外的其他位置删除元素,会导致全部失效 |
注意:每次删除或插入元素后都要重新计算end()返回的迭代器,不要使用end()迭代器的保存值。
额外的string操作
除了顺序容器的公共操作外,string还定义了一些额外操作,这些操作要么提供了string类和C风格字符数组之间的相互转换,要么增加了使用下标替代迭代器的版本。
构造string的其他方法
下表是除公共操作外的构造string的其他方法。
| 操作 | 含义 |
|---|---|
n、len、pos都是无符号整型值 |
|
string s(cp, n) |
使用cp指向的数组的前n个字符拷贝初始化s,cp指向的数组应该至少包含n个字符 |
string s(s2, pos) |
s是string类型的s2的从下标pos开始的字符的拷贝,若pos > s2.size(),函数行为未定义 |
string s(s2, pos, len) |
s是string类型的s2的从下标pos开始的len个字符的拷贝,若pos > s2.size(),函数行为未定义,无论len是多大,拷贝构造函数之多拷贝s2.size() - pos个字符 |
substr操作
下表是string类型的substr操作。
| 操作 | 含义 |
|---|---|
s.substr(pos) |
返回一个string,内容是s的从下标pos开始直到最后一个字符的string |
s.substr(pos, n) |
返回一个string,内容是从s的下标pos开始的n个字符,若n > s.size() - pos,则至多拷贝s.size() - n个字符 |
关联容器
泛型算法
C++ STL学习的更多相关文章
- 标准模板库(STL)学习探究之stack
标准模板库(STL)学习探究之stack queue priority_queue list map/multimap dequeue string
- 标准模板库(STL)学习探究之vector容器
标准模板库(STL)学习探究之vector容器 C++ Vectors vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被 ...
- ###STL学习--vector
点击查看Evernote原文. #@author: gr #@date: 2014-08-11 #@email: forgerui@gmail.com vector的相关问题.<stl学习> ...
- ###STL学习--关联容器
点击查看Evernote原文. #@author: gr #@date: 2014-08-23 #@email: forgerui@gmail.com STL中的关联容器. ###stl学习 |--迭 ...
- ###STL学习--迭代器
点击查看Evernote原文. #@author: gr #@date: 2014-08-23 #@email: forgerui@gmail.com STL中的迭代器. ###stl学习 |--迭代 ...
- ###STL学习--函数对象
点击查看Evernote原文. #@author: gr #@date: 2014-08-13 #@email: forgerui@gmail.com 在stl中,函数对象被大量地使用,用以提高代码的 ...
- ###STL学习--适配器
点击查看Evernote原文. #@author: gr #@date: 2014-08-24 #@email: forgerui@gmail.com STL中的适配器. ###stl学习 |--迭代 ...
- STL学习:STL库vector、string、set、map用法
本文仅介绍了如何使用它们常用的方法. vector 1.可随机访问,可在尾部插入元素:2.内存自动管理:3.头文件#include <vector> 1.创建vector对象 一维: (1 ...
- Effective STL 学习笔记 39 ~ 41
Effective STL 学习笔记 39 ~ 41 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...
- Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value
Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value */--> div.org-src-container ...
随机推荐
- [转帖]Linux系统NVME盘分区和挂载
https://www.jianshu.com/p/04327f1b97cb 查看系统里面识别到的硬盘和分区的信息 $ sudo fdisk -l Disk /dev/nvme1n1: 1.8 TiB ...
- [转帖]CPU缓存行
https://www.jianshu.com/p/e338b550850f CPU缓存 执行程序是靠运行CPU执行主存中代码,但是CPU和主存的速度差异是非常大的,为了降低这种差距,在架构中使用了C ...
- [转帖]龙芯总强调同频性能,是因奔腾4要到25+GHz才比得上酷睿11代单核
https://baijiahao.baidu.com/s?id=1734320620568707041 闲话不说,先上图,是SPEC CPU 2006 int(单任务)测试的成绩: 上图中的成绩 ...
- SQL注入payload学习整理
SQLserver 用的payload 0101%'and 1=(select @@version) and '%'=' GS的一个客户端参数 <add PropertyName="F ...
- 我在京东做研发 | 京东云算法科学家解析爆火的ChatGPT
令人惊艳的ChatGPT横空出世 背后有怎样的前沿技术支撑 走向大规模产品应用又有何局限 深耕对话式AI技术十余年 京东云算法科学家将带您一同走进技术世界 解析ChatGPT的技术亮点与局限 分享下一 ...
- Docker 完整指南
欢迎来到 Docker 的完整指南!在这个教程中,我们将深入研究 Docker 的各种特性,从基础的容器操作到高级的网络配置和数据管理.让我们一步步地探索 Docker 的丰富功能. 1. 安装 Do ...
- 大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数设计以及ICL底层机制等原理详解
大语言模型的预训练[5]:语境学习.上下文学习In-Context Learning:精调LLM.Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解 1.I ...
- 基于.NET+FreeSql实现的仿掘金专栏前后端分离的CMS
前言 今天分享一款基于.NET+FreeSql实现的仿掘金专栏前后端分离.支持Docker部署.集成了OAtuh2授权登录.QQ.Github.Gitee快速登录.简单实用的CMS:lin-cms-d ...
- 集成Unity3D到iOS应用程序中
如果想让原生平台(例如 Java/Android.Objective C/iOS 或 Windows Win32/UWP)包含 Unity 功能,可以通过Unity 生成UnityFramework静 ...
- WPF性能优化:形状(Shape)、几何图形(Geometry)和图画(Drawing)的使用
在用户界面技术中,绘图是一个绕不开的话题.WPF提供了多种可根据应用程序要求进行优化的2D图形和图像的处理功能,包括画刷(Brush).形状(Shape).几何图形(Geometry).图画(Draw ...