本期重点:迭代器,友元函数
vector(向量):C++中的一种数据结构,确切的说是一个类,它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的。
首先,我们可以自己实现一个字符串类型的容器。
class CMystring
{
public:
CMystring(char*ptr = NULL)
{
if (ptr == NULL)
{
mpStr = new char[1];
*mpStr = 0;
}
else
{
mpStr = new char[strlen(ptr) + 1];
strcpy(mpStr, ptr);
}
}
~CMystring()
{
delete[]mpStr;
mpStr = NULL;
}
CMystring(const CMystring&src)
{
mpStr = new char[strlen(src.mpStr) + 1];
strcpy(mpStr, src.mpStr);
}
CMystring& operator=(const CMystring&src)
{
if (this == &src)
{
return *this;
}
delete[]mpStr;
mpStr = new char[strlen(src.mpStr) + 1];
strcpy(mpStr, src.mpStr);
return *this;
}
bool operator>(const CMystring&src)
{
return strcmp(mpStr, src.mpStr) > 0;
}
bool operator<(const CMystring&src)
{
return strcmp(mpStr, src.mpStr) < 0;
}
bool operator==(const CMystring&src)
{
return strcmp(mpStr, src.mpStr) == 0;
}
char& operator[](int index)
{
return mpStr[index];
}
int length(){ return strlen(mpStr); }
char*c_str(){ return mpStr; }
//给当前容器类型CMyString实现迭代器
class iterator
{
public:
iterator(char*pos) :_ptr(pos){}
bool operator!=(const iterator&src)
{
return this->_ptr != src._ptr;
}
void operator++()
{
this->_ptr++;
}
char&operator*(){ return *_ptr; }
private:
char*_ptr;
};
// 返回首元素的迭代器对象
iterator begin(){ return iterator(mpStr); }
//返回末尾元素的迭代器对象
iterator end(){ return iterator(mpStr + length()); }
private:
char*mpStr;
friend ostream& operator<<(ostream&out, const CMystring& srv);
friend CMystring operator+(const CMystring&src, const CMystring&srv);
};
ostream& operator<<(ostream&out, const CMystring& srv)
{
out << srv.mpStr;
return out;
}
CMystring operator+(const CMystring&src, const CMystring&srv)
{
int size = strlen(src.mpStr) + strlen(srv.mpStr) + 1;
char *p = new char[size];
strcpy(p, src.mpStr);
strcat(p, srv.mpStr);
CMystring tmp(p);
delete[]p;
return tmp;
}
迭代器是一种检查容器内元素并遍历元素的数据类型。C++更趋向于使用迭代器而不是下标操作,因为标准库为每一种标准容器(如vector)定义了一种迭代器类型,而只有少数容器(如vector)支持下标操作访问容器元素。
通过上面的代码和定义,我们可以发现,其实迭代器就类似与我们所使用的数组的下标,就是因为有些容器不支持下标,所以才有了迭代器。
迭代器的失效问题:由于一些对容器的操作如删除元素或移动元素等会修改容器的内在状态,这会使得原本指向被移动元素的迭代器失效,也可能同时使其他迭代器失效。使用无效的迭代器是没有定义的,可能会导致和使用悬垂指针相同的问题。所以在使用迭代器编写程序时,需要特别留意哪些操作会使迭代器失效。使用无效迭代器会导致严重的运行时错误。
比如下面的代码:
此处如果直接使用erase()删除迭代器当前位置的一个节点,则容器的内部会发生改变。因为迭代器指向的容器发生了改变,所以迭代器也失效了。解决的方法是给迭代器重新赋值,刷新迭代器。//删除偶数 for (it = Vec.begin(); it != Vec.end();) { if (*it%2==0) { //Vec.erase(it); it = Vec.erase(it); } else { ++it; } }
或许有人会问了,此处我们这两个重载函数为什么要写在类外面呢 ?
因为我们需要支持混合型的算术操作,对所有的参数执行隐式类型转换。而实现该功能的方法就是:使operator*成为一个非成员函数。
但是问题也来了,我们既然把方法写在类外面了,那么我们怎么才可以访问类的私有成员呢?
看到上面的代码,我们不难发现,在该类的私有成员下面,我们把两个需要访问该私有成员的函数写了进去,并且前面添加了friend。这是什么意思呢?
通过查询,我们了解到这是友元函数,就是为了解决某些虽然不是类成员却想要访问类的所有成员的函数的问题。类授予它的友元特别的访问权。通常同一个开发者会出于技术和非技术的原因,控制类的友元和成员函数(否则当你想更新你的类时,还要征得其它部分的拥有者的同意)。
关于友元函数有几点我们需要注意:
1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;
3)友元函数不能直接访问类的成员,只能访问对象成员,
4)友元函数可以访问对象的私有成员,但普通函数不行;
5)调用友元函数时,在实际参数中需要指出要访问的对象,
6)类与类之间的友元关系不能继承。
7)一个类的成员函数也可以作为另一个类的友元,但必须先定义这个类。
如果想要更深层次的了解什么时候使用 成员函数,非成员函数 还是 非成员的友元函数,点击此处进入