使用::std::vector作为管理动态数组的优先选择

【C++基础】std::vector详解 是 C++ 标准库中的一个容器,提供了动态数组的功能。它的底层实现通常是使用连续的内存块来存储元素,因此可以通过指针算术来访问元素,并且支持常数时间的随机访问,并支持在容器末尾高效地添加和删除元素。 阅读详情

作者:wangtianxing
提交者:eastvc 发布日期:2003-9-19 17:34:41


摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。

在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。

另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。

由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。

不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。

1. CArray<> VS ::std::vector<> ?

CArray<> 和 ::std::vector<> 一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。

但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对 C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<> 的设计有重大错误。

在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<> 模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了 CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。


概括起来,CArray<> 与 ::std::vector<> 有以下不同:

1) CArray<> 是 MFC 中的,::std::vector<> 存在于任何标准的 C++ 实现中。因此,你用熟了 CArray<> 也只能在 MFC 中用,若用熟了 ::std::vector<>,你可以在任何平台的任何 C++ 编译器下使用。使用标准的部件也有利于别人理解你的程序。 . CArray<> 继承了 CObject,仅仅为了实现 serialization,这是不恰当的, 违反了 "You don't pay for what you don't use." 的 C++ 设计原则。::std::vector<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。

2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:

CArray<int,int> a;
CArray
<int,int> b(a);  // error, must use Copy().
b = a;         // error, must use Copy().
b == a;       // error, you must write your own.
b < a;         // error, you must write your own.

与 CArray<> 相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么 ::std::vector<T> 将自动地是可以比较的。

此外,由于涉及到四个特殊成员函数;

T();     // 缺省构造函数(default constructor)
~T();   // 析构函数(destructor)
T( T const& );   // 拷贝构造函数
T& operator=( T const& );   // 拷贝赋值函数

的自动生成,如果使用 CArray() 作为 T 的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:

struct T
{
   T() 
{}
   T( T 
const& t )
   
{
       a_.Copy( t.a_ );
       i_ 
= t.i_;
       d_ 
= t.d_;
       s_ 
= t.s_;
   }

   T
& operator = ( T const& t )
   
{
       
if( this != &t )
       
{
           a_.Copy( t.a_ );
           i_ 
= t.i_;
           d_ 
= t.d_;
           s_ 
= t.s_;
       }

       
return *this;
   }

private:
   CArray
<int,int> a_;
   
int i_;
   
double d_;
   ::std::
string s_;
}
;

如果使用 ::std::vector<>:

struct T
{
private:
   ::std::vector
<int> a_;
   
int i_;
   
double d_;
   ::std::
string s_;
}
;

上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
T(T const&) 和 operator=() 中去相应地增减。

3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在
::std::vector<> 上运行。例如:

static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
vector
<int> a( init_vals, init_vals + 6 );
*find( a.begin(), a.end(), 6 ) = 5;    // 把6改成5
sort( a.begin(), a.end() );    // 排序。

可以说,CArray<> 的主要设计错误是把一个本来应该是一个简单的“值”类型的东西设计成一个难用的“对象”类型了。所有的“值”的好特性都丧失了,但那些从CArray<>继承的派生类呢?

CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。

同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用
::std::map<>,::std::list<> 等设计更好的东西代替。

2. ::std::vector<> 在哪里?

::std::vector<> 在头文件 <vector> 中定义:

(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)

namespace std 
{
    template
<typename T, typename A = allocator<T> >
    
struct vector
    
{
        
// 具体内容稍后讨论
    }
;


    template
<typename T, typename A>
        
bool operator == ( vector<T,A> const& a, vector<T,A> const&    b );
    template
<typename T, typename A>
        
bool operator != ( vector<T,A> const& a, vector<T,A> const&    b );
    template
<typename T, typename A>
        
bool operator < ( vector<T,A> const& a, vector<T,A> const&    b );
    template
<typename T, typename A>
        
bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
    template
<typename T, typename A>
        
bool operator > ( vector<T,A> const& a, vector<T,A> const&    b );
    template
<typename T, typename A>
        
bool operator >= ( vector<T,A> const& a, vector<T,A> const&    b );
}

vector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:

#include <vector>
typedef ::std::vector
<int> IntVector;
IntVector a;
IntVector b( a );
IntVector c;
c 
= b;
assert( a 
== c );

请注意 <vector> 中定义了六个 vector<T,A> 的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。

另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6 的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。

3. ::std::vector<> 中的类型定义

vector<> 中定义了一些类型,下面只列出常用的:

typedef T value_type;
typedef T0 iterator;
typedef T1 const_iterator;
typedef T2 reverse_iterator;
typedef T3 const_reverse_iterator;

value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。

iterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于 T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:

typedef ::std::vector<int> IntVector;
IntVector::iterator iter;
IntVector::const_iterator c_iter;
// ...
++iter; iter++; // ok: increment, post-increment.
--iter; iter--;     // ok: decrement, post-decrement.
++c_iter; c_iter++; // ok: increment, post-increment.
--c_iter; c_iter--;     // ok: decrement, post-decrement.
*iter = 123;     // ok.
int k = *iter;     // ok.
k = *--c_iter;    // ok.
*c_iter = k;       // error.
c_iter = iter;     // ok: iterator is convertible to const_iterator.
iter = c_iter;     // error: can't convert const_iterator to iterator.

在使用上 iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator 当作真正的 T*/T const*:

T* p = iter;    // may fail to compile.
T const* q = c_iter;    // may fail to compile.

reverse_iterator/const_reverse_iterator 与 iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。

各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector 中的元素,大概相当于一个指示位置的指针就行了。

4. ::std::vector<> 的构造

vector<> 提供了以下构造函数:(忽略 allocator 参数)

vector();
vector( size_t n, T 
const t=T() );
vector( vector 
const & );
vector( const_iterator first, const_iterator last );

1) vector();

构造一个空的 vector,不包含任何元素。

IntVector v1;    // 空的整数向量。

2) vector( size_t n, T const t=T() );

构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:

IntVector v2( 100, 1234 );    // 100 个 1234
IntVector v3( 100 );    // 100 个 0

3) vector( vector const& other );

拷贝构造函数,复制 other 中的内容:

IntVector v4( v2 );    // 100 个 1234

4) vector( const_iterator first, const_iterator last );

事实上,这个构造函数应该为

template<typename Iter>
    vector( Iter first, Iter last );

即拷贝任意的序列 [first,last) 到 vector 中([first, last) 是半开半闭区间)。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧 const_iterator就是 T const*,所以可以如下使用:

int a[] = { 1, 2, 3, 4, 5 };
IntVector v5( a, a 
+ 5 ); // {1,2,3,4,5}
IntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}

5. 访问 vector<> 中的元素

以下成员函数/运算符用于访问 vector 中的一个元素:

T& at( size_t n );
T 
const& at( size_t n ) const;
T
& operator [] ( size_t n );
T 
const& operator [] ( size_t n ) const;
T
& front();
T 
const& front() const;
T
& back();
T 
const& back() const;

请注意,由于 vector 是一个“值”语义的对象,所有的操作函数都必须严格保证 const 的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。

at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出 range_error 异常,operator[]不进行下标检查。

front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。

int a[] = { 4, 1, 4, 1, 5, 8 };
IntVector v( a, a 
+ 6 );
// 使用 front(), back():
v.front() = 3;
v.back() 
= 9;
// 使用 operator [] ():
for( size_t i = 0; i < v.size(); ++i )
::std::cout 
<< v[i] << ' ';

6. ::std::vector<> 的存储管理

以下成员函数用于存储管理:

void reserve( size_t n );
size_t capacity() 
const;
void resize( size_t n, T t=T() );
void clear();
size_t size() 
const;
bool empty() const { return size() == 0; }
size_t max_size() 
const;

另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。

1) max_size()

返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。

2) size()

返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。

3) empty()

如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。

4) clear();

清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。

5) resize( size_t n, T t = T() );

将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。

请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。

6) reserve( size_t n );

事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。

7) capacity();

返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。

vector 管理的动态存储空间是连续的。执行操作

IntVector v(7, 1); // seven ones.
v.reserve( 12 );

后,v 的状态可以用下图表示:

 /--size()---/
|1|1|1|1|1|1|1|-|-|-|-|-|
/--capacity()---------/

其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行

v.push_back( 2 );
v.push_back( 
3 );

后,v 的状态可用下图表示:

 /----size()-----/
|1|1|1|1|1|1|1|2|3|-|-|-|
/----capacity()-------/

执行 resize( 11, 4 ); 后:

 /----size()---------/
|1|1|1|1|1|1|1|2|3|4|4|-|
/----capacity()-------/

capacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:

v[11] = 5; // undefined behavior - anything can happen.

7. 添加元素到 vector 中

下列操作添加元素到 vector 中,并可能引起存储分配:

void push_back( T const& t );
void insert( iterator pos, T const& t=T() );
void insert( iterator pos, size_t n, T const& t );
template
<typename Iter>
    
void insert( iterator pos, Iter first, Iter last );

push_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。

当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。

这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。

IntVector v;
   
// add 0, 1, ..., 99 to v:
for( int i = 0; i < 100; ++i )
v.push_back( i );
   
// append 9, 8, 7,..., 0 to the end:
int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
v.insert( v.end(), a, a 
+ 10 );

8. 删除元素

下列成员函数完成元素删除:

void erase( iterator );
void erase( iterator first, iterator last );
void pop_back();
void clear();

这些函数分别删除一个,一串,最后一个,或全部元素。

IntVector v;
for( int i = 0; i < 100; ++i )
    v.push_back( i );
   
// 删除 50, 51, ..., 89:
v.erase( v.begin() + 50, v.end() - 10 );
   
// 删除 49, 48:
v.pop_back();
v.pop_back();
   
// 全部删除:
v.clear();

注意,删除操作不会引起存储分配,因此 capacity() 不变。

9. 作为序列访问 vector 中的元素

序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。

“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。

Iterator是传统的 C/C++ 中指针的抽象和进一步分类。在 C++ 中把 iterator划分为 input iterator, output iterator, forward iterator,bidirectional iterator, random access iterator 五类。其中的 randomaccess iterator 是最强的一类,即允许的操作最多。C++ 中的指针类型以及vector<>/deque<> 的 iterator/const_iterator / reverse_iterator/const_reverse_iterator 都满足 random access iterator 的要求。

vector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种iterator:

iterator begin();
iterator end();
const_iterator begin() 
const;
const_iterator end() 
const;
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin() 
const;
const_reverse_iterator rend() 
const;

这里我们不讨论iterator的一般概念,只举几个 random access iterator 的例子:

int a[] = { 1, 2, 3, 4, 5, 6 };

[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里iterator的类型为 int*。

[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。iterator的类型仍然是 int*。

IntVector v( 100, 1 ); // 100 个 1

[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,iterator的类型是 IntVector::iterator。

[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。

[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。

[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。

下图是有十个元素的 vector 的 begin() / end() / rbegin() / end() 的示意:

begin() ----------> end()

  |                             |

  v                            v

|0|1|2|3|4|5|6|7|8|9|

 ^                            ^

 |                              |

rend() <---------- rbegin()

IntVector v;
for( int i = 0; i < 10; ++i )
v.push_back( i );
   
// print 0, 1, 2, ..., 9:
for( IntVector::iterator i = v.begin(); i != v.end(); ++i )
::std::cout 
<< *i << ' ';
   
// print 9, 8, ..., 0:
for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )
::std::cout 
<< *i << ' ';

除了使用 begin() / end() / rbegin() / rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:

::std::vector<HANDLE> handles;
handles.push_back( handle1 );
handles.push_back( handle2 );


::WaitForMultipleObjects(handles.size(), 
&handles[0],TRUE, INFINITE);

这在与 C 库函数接口时尤其有用。

10. 赋值和交换

vector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:

IntVector v( 100, 123 );
IntVector v1;
v1 
= v;

vector 另外还提供了

template<typename Iter>
void assign( Iter first, Iter last );
void assign( size_t n, T const& t = T() );

用于赋值:

int a[] = { 1, 3, 5, 7 };
v.assign( a, a 
+ 4 ); // v 将包含 1, 3, 5, 7.
v.assign( 100 ); // 100 个 0

还有一个很重要的操作:

void swap( vector& v ) throw();

用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。

事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:

struct MyVal
{
  
// blah blah.
  void swap( MyVal& ) throw();
}
;
   
namespace std {
  template
<>
    
void swap( MyVal& a, MyVal& b )
    
{ a.swap( b ); }
}

关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。

11. 使用 vector 时的存储管理策略

从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:

IntVector v;
v.reserve( 
100 );
for( int i = 0; i < 100; ++i )
    v.push_back( i );

请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:

IntVector v;
v.resize( 
100 ); // v 已经包含 100 个 0.
for( int i = 0; i < 100; ++i )
    v[i] 
= i; // 可以赋值

有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:

IntVector(v).swap( v );

有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是

IntVector(v.begin(),v.end()).swap(v);

如果一个 vector 中可能要存储的元素个数较多(例如,超过100个),而且事先无法确定其个数(因此无法调用 reserve()),那么通常 vector 不是一个恰当的数据结构,应该考虑用 ::std::deque<>。与 vector<> 相比,deque<>不保证背后的存储空间是连续的(因此象上面的WaitForMultipleObjects()中的应用不能用 deque<HANDLE> 代替),但有较好的伸缩性,还可以在数组的前端用 push_front()/pop_front() 增减元素(hence its name, doubly endedqueue)。

使用onnx c++部署pytorch神经网络模型全流程 C++调用ONNXRUNTIME推理pytorch模型 阅读详情

相关推荐

std::vector排序详解

一个详细全面的关于vector排序的例子。 涉及到 如何继承std::binary_function、如何重载operator().

容器库——std::vector

容器库——std::vector详解

qq_44778120的博客 1万+

切比雪夫(最小区域法)直线拟合算法

切比雪夫,最小区域法直线拟合

闪电彬彬的博客 3965

C++ 中的 std::vector:动态数组操作和常用函数解析

C++ 中的 std::vector 是一个非常实用的容器,它提供了动态大小的数组功能,可以存储任意类型的元素,并且可以方便地进行元素的添加、删除和查询操作。本博客不仅介绍了一些常用的成员函数和操作符,如 push_back()、pop_back()、size()、empty() 和 clear(),还详细对比了 push_back() 和 emplace_back() 之间的区别。此外,博客还提供了一些小示例程序,以帮助读者更好地理解这些概念。

南城小馆 6974

uni-app一键登录实战:从云函数配置到前端调用的完整指南

本文详细介绍了uni-app中一键登录功能的实现方法,从DCloud控制台配置到云函数开发,再到前端调用全流程。通过一键登录,用户无需输入账号密码即可快速使用本机手机号登录,显著提升用户体验和登录转化率。文章还提供了常见问题排查和安全合规要点,帮助开发者高效完成集成。

weixin_29291573的博客 446

仓储控制系统(WCS)设计

WCS系统设计方法

cetclyb的博客 2万+

使用::std::vector作为管理动态数组的优先选择(zz)

在一些使用 MFC 的程序中,经常看到许多程序使用 CArray的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector。另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[] 等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector由于 MSDN 中关于 ::std::ve

vivianm的专栏 1727

Unity 2021 LTS安装避坑指南:从下载到第一个3D项目的完整流程

本文提供了一份详尽的Unity 2021 LTS安装与首个3D项目创建指南。内容涵盖从Unity Hub下载、版本选择、模块安装避坑,到编辑器界面解析、创建旋转立方体脚本的完整流程,旨在帮助初学者快速搭建稳定开发环境并上手核心操作。

mango的博客 313

使用std::vector作为管理动态数组的优先选择

本文来自: 乘风原创程序(http://www.qqcf.com) 详细出处参考:http://study.qqcf.com/web/530/119818.htm 摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray在一些使用 MFC 的程序中,经常看到许多程序使用 CArray的设计问题

946

F5高可用HA配置详情

F5 HA详细配置

weixin_45843944的博客 3026

Cartographer实现双雷达建图

TRAJECTORY_BUILDER_2D.num_accumulated_range_data = 2 --执行一次前端匹配需要的激光扫描分段数量。关于num_subdivisions_per_laser_scan。num_laser_scans =2,--订阅激光扫描话题数量。Frame改为 前雷达laser_1 后雷达laser_2。雷达的话题由scan改为,前scan_1 后scan_2。有些人说需要改成2,但我这里改成2反而不行,1才可以。

Studying…… 1261

转:使用::std::vector作为管理动态数组的优先选择

原文出处:http://www.cpphelp.net/issue/vector.html 摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray在一些使用 MFC 的程序中,经常看到许多程序使用 CArray的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::ve

E.G的专栏 632

ABAP RANGES及比较符号

RANGES *选项介绍: *1.SIGN 值为 I 和 E 。 I是包含, E是排除, 一般使用I。 2.OPTION 如果HIGH 为空 ,为单值选择 。有 EQ、NE、GT、LE、LT等逻辑操作 对于的 CP包含 NP是排除 如果HIGH 不为空 为区间选择 有BT,NB可选 *3.LOW 低值 *4.HIGH 高值 lt:less than 小于 le:less than or equal to 小于等于 eq:equal to 等于 ne:

Pegasus666的博客 5390

使用::std::vector作为管理动态数组的优先选择(转载)

<br />作者:wangtianxing<br /><br />提交者:eastvc 发布日期:2003-9-19 17:34:41<br />原文出处:http://www.cpphelp.net/issue/vector.html<br /><br /><br />摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。<br />在一些使用 MF

To be code... 697

4.6 鞅-一致可积,L1收敛(Durrett)答案

概率-理论与实例第五章 答案:6 鞅-一致可积,L1收敛

weixin_40614311的博客 1060

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

使用::std::vector<>作为管理动态数组的优先选择

使用::std::vector 作者:wangtianxing 提交者:eastvc 发布日期:2003-9-19 17:34:41 原文出处:http://www.cpphelp.net/issue/vector.html 摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray 在一

Mirage520的专栏 893

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

使用::std::vector作为管理动态数组的优先选择 作者原文出处:http://www.cpphelp.net/issue/vector.html

<br />使用::std::vector<>作为管理动态数组的优先选择:摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。 在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于 CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。 另外,

damon1118的专栏 856

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【C++】动态数组(vector)的使用

在 C++ 中,`std::vector` 是标准库提供的一个容器类,用于存储动态大小的元素序列(即"动态数组")。它提供了一系列成员函数和操作符,使得元素的访问、插入、删除等操作变得方便和高效。

AAADiao的博客 1万+

c++ 动态数组( std::vector)

vector是 C++ 提供的“自动化动态数组”,本质是用堆内存 + 封装管理 + 自动扩容,实现了内存安全与操作便利的平衡。

2402_88047672的博客 1316
上一篇: 关于浮躁
下一篇: [ubuntu]重装WinXP后恢复grub
HateCode
博客等级 码龄20年 25粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值