access模板_c++深入剖析之vector:Changing Size,模板 ,STL(容器、迭代器和算法)

Vector: Changing Size

问题:我们(人类)需要可以改变大小的abstractions(例如,可以改变元素数量的向量)。然而,在计算机内存中,所有的东西都必须有一个固定的大小,那么我们如何创建变化的幻觉呢?

给定vector v(n); // v.size()==n
我们可以通过三种方式改变它的大小
• Resize it
v.resize(10); // v now has 10 elements
• Add an element
v.push_back(7); // add an element with the value 7 to the end of v
// v.size() increases by 1
• Assign to it
v = v2; // v is now a copy of v2
// v.size() now equals v2.size()

Representing vector

如果你曾经resize或push_back,你可能会再做一次;
让我们考虑下怎么为未来的扩展保留一些自由空间吧~

class vector {
int sz;
double* elem;
int space; // number of elements plus “free space”
// (the number of “slots” for new elements)
public:
// …
};

5f4845c59939ea6e7b399546652cd6cc.png

vector::reserve()

•首先处理空间(分配);给定空间,其他的都很简单
注意:reserve()不会影响大小或元素值

void vector::reserve(int newalloc)
// make the vector have space for newalloc elements
{
if (newalloc<=space) return; // never decrease allocation
double* p = new double[newalloc]; // allocate new space
for (int i=0; i<sz; ++i) p[i]=elem[i]; // copy old elements
delete[ ] elem; // deallocate old space
elem = p;
space = newalloc;
}

•给定reserve(), resize()很简单
reserve()处理空间/分配
resize()处理元素值

void vector::resize(int newsize)
// make the vector have newsize elements
// initialize each new element with the default value 0.0
{
reserve(newsize); // make sure we have sufficient space
for(int i = sz; i<newsize; ++i) elem[i] = 0; // initialize new elements
sz = newsize;
}

vector::push_back()

•给定reserve(), push_back()很简单
reserve()处理空间/分配
push_back()只添加一个值

void vector::push_back(double d)
// increase vector size by one
// initialize the new element with d
{
if (sz==0) // no space: grab some
reserve(8);
else if (sz==space) // no more free space: get more space
reserve(2*space);
elem[sz] = d; // add d at end
++sz; // and increase the size (sz is the number of elements)
}

resize() & push_back()

class vector { // an almost real vector of doubles
int sz; // the size
double* elem; // a pointer to the elements
int space; // size+free_space
public:
// … constructors and destructors …
double& operator[ ](int n) { return elem[n]; } // access: return reference
int size() const { return sz; } // current size
void resize(int newsize); // grow
void push_back(double d); // add element
void reserve(int newalloc); // get more space
int capacity() const { return space; } // current available space
};

模板Templates

•但我们不只是想要双精度数值型向量•我们需要指定元素类型的向量

• vector<double>
• vector<int>
• vector<Month>
• vector<Record*> // vector of pointers
• vector<vector<Record>> // vector of vectors
• vector<char>

•我们必须使元素类型成为vector的参数
•向量必须能够采取内置类型和用户定义的类型作为元素类型
•这不是为编译器保留的魔法;我们可以定义自己的参数化类型,称为“模板”

Templates

•c++中通用编程的基础
有时被称为“参数多态性”
Parameterization of types (and functions) by types (and integers)
•无与伦比的灵活性和性能
用于需要性能的地方(例如,hard real time and numerics)
用于需要灵活性的地方(例如,c++标准库)

Template定义

template<class T, int N> class Buffer { /* … */ };
template<class T, int N> void fill(Buffer<T,N>& b) { /* … */ }

模板专门化(实例化)

// for a class template, you specify the template arguments:
Buffer<char,1024> buf; // for buf, T is char and N is 1024
// for a function template, the compiler deduces the template arguments:
fill(buf); // for fill(), T is char and N is 1024; that’s what buf has

使用元素类型参数化

// an almost real vector of Ts:
template<class T> class vector {
// …
};
vector<double> vd; // T is double
vector<int> vi; // T is int
vector<vector<int>> vvi; // T is vector<int>
// in which T is int
vector<char> vc; // T is char
vector<double*> vpd; // T is double*
vector<vector<double>*> vvpd; // T is vector<double>*
// in which T is double

本质上,vector<double>是

// an almost real vector of doubles:
class vector {
int sz; // the size
double* elem; // a pointer to the elements
int space; // size+free_space
public:
vector() : sz(0), elem(0), space(0) { } // default constructor
vector(const vector&); // copy constructor
vector& operator=(const vector&); // copy assignment
~vector() { delete[ ] elem; } // destructor
double& operator[ ] (int n) { return elem[n]; } // access: return
reference
int size() const { return sz; } // the current size
// …
};

本质上,vector<T>是

// an almost real vector of Ts:
template<class T> class vector { // read “for all types T” (just like in math)
int sz; // the size
T* elem; // a pointer to the elements
int space; // size+free_space
public:
vector() : sz{0}, elem{0}, space{0}; // default constructor
vector(const vector&); // copy constructor
vector& operator=(const vector&); // copy assignment
vector(const vector&&); // move constructor
vector& operator=(vector&&); // move assignment
~vector() { delete[ ] elem; } // destructor
// …
};

改进

// an almost real vector of Ts:
template<class T> class vector { // read “for all types T” (just like in math)
int sz; // the size
T* elem; // a pointer to the elements
int space; // size+free_space
public:
// … constructors and destructors …
T& operator[ ] (int n) { return elem[n]; } // access: return reference
int size() const { return sz; } // the current size
void resize(int newsize); // grow
void push_back(double d); // add element
void reserve(int newalloc); // get more space
int capacity() const { return space; } // current available space
// …
};

•问题(“天下没有免费的午餐”)
•错误诊断能力差
通常非常差(但在c++ 11中越来越好;更好的在c++ 14)
•延迟错误消息
经常在link time
•所有模板必须在每个翻译单元中完全定义
将模板定义放在头文件中

推荐
•使用基于模板的库 例如c++标准库 •例如,vector,sort()
•最初,自己只编写非常简单的模板,直到获得更多经验

Range checking

// an almost real vector of Ts:
struct out_of_range { /* … */ };
template<class T> class vector {
// …
T& operator[ ](int n); // access
// …
};
template<class T> T& vector<T>::operator[ ](int n)
{
if (n<0 || sz<=n) throw out_of_range();
return elem[n];
}


void fill_vec(vector<int>& v, int n) // initialize v with factorials
{
for (int i=0; i<n; ++i) v.push_back(factorial(i));
}
int main()
{
vector<int> v;
try {
fill_vec(v,10);
for (int i=0; i<=v.size(); ++i)
cout << "v[" << i << "]==" << v[i] << 'n';
}
catch (out_of_range) { // we’ll get here (why?)
cout << "out of range error";
return 1;
}
}

异常处理•我们使用异常来报告错误
•我们必须确保使用异常时
不引入新的错误来源
不会使我们的代码变得复杂
不会导致资源泄漏

STL(容器、迭代器和算法)

STL - c++标准库的容器和算法部分

常见的任务
•将数据收集到容器中
•组织数据
•打印
•快速访问
•检索数据项
通过索引(例如,获取第n个元素)
通过值(例如,获取值为“Chocolate”的第一个元素)
通过属性(例如,获取“年龄<64”的第一个元素)
•添加数据
•删除数据
•排序和搜索
•简单的数字操作

观察

我们可以(已经)编写非常相似的程序
它们与所使用的数据类型无关
使用int和使用double没有什么不同

ideals

我们希望编写通用的编程任务,这样我们就不必在每次找到新的数据存储方式或稍微不同的数据解释方式时都要重新执行这些工作
•在向量中查找值与在列表或数组中查找值并没有什么不同
•查找忽略大小写的字符串与查找不忽略大小写的字符串并没有什么不同
•用精确值绘制实验数据的图形与用四舍五入值绘制数据的图形并没有什么不同
•复制一个文件与复制一个向量并没有什么不同
代码要
•易于阅读
•容易修改
•Regular
•短
•快
统一的数据访问
•独立于存储方式
•独立于其类型
类型安全的数据访问
数据的轻松遍历
紧凑的数据存储
快速
•数据检索
•数据添加
•删除数据
最常见算法的标准版本
•复制,查找,搜索,排序,求和,…

例子
•对字符串向量排序
•在电话簿中找到一个号码,给出一个名字
•找到最高温度
•找到所有大于800的值
•找到第一个出现的值17
•按单元编号对遥测记录进行分类
•根据时间戳对遥测记录进行分类
•找到第一个大于“Petersen”的值?
•看到的最大数量是多少?
•找出两个序列之间的第一个差异
•计算两个序列元素的成对乘积
•一个月里每天的最高温度是多少?
•十大畅销书是什么?
•“c++”的条目是什么(比如,在谷歌中)?
•元素的和是多少?

STL

•ISO c++标准库的一部分
•主要非数值
•只有4种标准算法专门做计算
累加,内积, partial_sum,邻接差分
处理文本数据和数值数据 如字符串
处理代码和数据的组织——内置类型、用户定义类型和数据结构
•优化磁盘访问是其最初的用途之一——绩效一直是一个关键问题

STL

•由Alex Stepanov设计
•总体目标:对概念(思想、算法)有最全面、最有效、最灵活的表达
在代码中分别表示不同的概念
在有意义的地方自由组合概念
•总体目标是让编程“像数学一样”
•甚至“好的编程就是数学”
适用于整数,浮点数,多项式,…

c45853819a4de9853734a22211bfed63.png

•关注点分离 Separation of concerns

-算法操纵数据,但不知道容器
-容器存储数据,但不知道算法
-算法和容器通过迭代器进行交互 每个容器都有自己的迭代器类型

STL

•一个ISO c++标准框架,由10个容器和60个算法通过迭代器连接
其他组织提供更多STL风格的容器和算法
•Boost.org,微软,SGI,……
•可能是目前最知名、使用最广泛的泛型编程示例

基本模型

一对迭代器定义一个序列
• The beginning (points to the first element – if any)
• The end (points to the one-beyond-the-last element)

6875354d109a3ddfcc05f489e6bec1bd.png

•迭代器是一种支持“迭代操作”的类型。
进入下一个元素
*获取value
==这个迭代器与那个迭代器指向相同的元素吗?
•一些迭代器支持更多的操作(例如--、+和[])

容器(以不同方式hold序列)

7480c17004f88497cc94cda89ee933f1.png

最简单的算法:find()

// Find the first element that equals a value
template<class In, class T>
In find(In first, In last, const T& val)
{
while (first!=last && *first != val) ++first;
return first;
}
void f(vector<int>& v, int x)// find an int in a vector
{
vector<int>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* we found x */ }
// …
}

我们可以忽略容器之间的差异

find ()对于元素类型和容器类型都是通用的

void f(vector<int>& v, int x) // works for vector of ints
{
vector<int>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* we found x */ }
// …
}
void f(list<string>& v, string x) // works for list of strings
{
list<string>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* we found x */ }
// …
}
void f(set<double>& v, double x) // works for set of doubles
{
set<double>::iterator p = find(v.begin(),v.end(),x);
if (p!=v.end()) { /* we found x */ }
// …
}

简单算法:find_if()

找到第一个匹配条件的元素(predicate) •在这里,predicate接受一个参数并返回一个bool

template<class In, class Pred>
In find_if(In first, In last, Pred pred)
{
while (first!=last && !pred(*first)) ++first;
return first;
}
void f(vector<int>& v)
{
vector<int>::iterator p = find_if(v.begin(),v.end,Odd());
if (p!=v.end()) { /* we found an odd number */ }
// …
}

predicate

•一个predicate(一个参数)是一个函数或一个函数对象,它接受一个参数并返回一个bool
例如:odd函数或函数对象

bool odd(int i) { return i%2; } // % is the remainder (modulo)
operator
odd(7); // call odd: is 7 odd?

or

struct Odd {
bool operator()(int i) const { return i%2; }
};
Odd odd; // make an object odd of type Odd
odd(7); // call odd: is 7 odd?

vector

template<class T> class vector {
T* elements;
// …
using value_type = T;
using iterator = ???; // the type of an iterator is implementation defined
// and it (usefully) varies (e.g. range checked iterators)
// a vector iterator could be a pointer to an element
using const_iterator = ???;
iterator begin(); // points to first element
const_iterator begin() const;
iterator end(); // points to one beyond the last element
const_iterator end() const;
iterator erase(iterator p); // remove element pointed to by p
iterator insert(iterator p, const T& v); // insert a new element v before p
};

list

template<class T> class list {
Link* elements;
// …
using value_type = T;
using iterator = ???; // the type of an iterator is implementation defined
// and it (usefully) varies (e.g. range checked iterators)
// a list iterator could be a pointer to a link node
using const_iterator = ???;
iterator begin(); // points to first element
const_iterator begin() const;
iterator end(); // points to one beyond the last element
const_iterator end() const;
iterator erase(iterator p); // remove element pointed to by p
iterator insert(iterator p, const T& v); // insert a new element v before p
};

向量vs列表

•默认情况下,使用向量
你需要一个不这样做的理由
您可以“增长”一个向量(例如,使用push_back())
您可以在向量中插入()和删除()
向量元素是紧凑存储和连续的
对于小元素的小向量,所有操作都是快速的

•与列表相比
如果不希望元素移动,可以使用列表
你可以“增长”一个列表(例如,使用push_back()和push_front())
您可以在列表中插入()和删除()
列表元素是单独分配的

注意:有更多的容器,例如•map•unordered_map

一些有用的标准头文件

• <iostream> I/O streams, cout, cin, …
• <fstream> file streams
• <algorithm> sort, copy, …
• <numeric> accumulate, inner_product, …
• <functional> function objects
• <string>
• <vector>
• <map>
• <unordered_map> hash table
• <list>
• <set>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值