vector

ttlb 2003-09-21 03:55:55
数据结构课上讲了顺序表,我觉得跟标准库里的vector差不多,就自己做了一个vector,当然功能还很少,和标准vector差得远。希望大家能帮我看看,还有什么能改进的地方?哪方面的意见都行。多谢啦。
// Copyright: ttlb(Tang Tao) 2003
// Version: 1.0
// This program simulates the Standard Template Library vector
// As an exercise of Data Structure

#ifndef _TTLB_VECTOR_H
#define _TTLB_VECTOR_H

#include <cassert>
#include <memory>

namespace ttlb
{
template <typename T>
class vector
{
public:
explicit vector(int iLength = 1000)
: m_iSize(0), m_iLength(iLength)
{
if (0 == m_iLength)
m_ptArr = 0;
else
m_ptArr = new T[m_iLength];
}
explicit vector(T *const ptBegin, T *const ptEnd)
: m_iSize(0), m_iLength(1000)
{
if (0 == m_iLength)
m_ptArr = 0;
else
m_ptArr = new T[m_iLength];
for (T *ptIter = ptBegin; ptIter != ptEnd; ++ptIter)
{
push_back(*ptIter);
}
}
explicit vector(const vector &cVSrc)
{
m_iSize = cVSrc.m_iSize;
m_iLength = cVSrc.m_iLength;
m_ptArr = new T[cVSrc.m_iLength];
memcpy(m_ptArr, dVSrc.m_ptArr, sizeof(T) * m_iSize);
}
virtual ~vector()
{
delete[] m_ptArr;
}
int size() const
{ // Size of the vector
return m_iSize;
}
void push_back(const T &tSrc)
{ // Push an element at the back
if (is_full())
resize(2 * m_iLength);
m_ptArr[m_iSize] = tSrc;
++m_iSize;
}
T pop_back()
{ // Pop an element at the back
assert(!is_empty());
return m_ptArr[--m_iSize];
}
void resize(int iNewLength)
{ // Resize the vector to the specified size
assert(iNewLength >= m_iSize);
m_iLength = iNewLength;
T *ptNewArr = new T[m_iLength];
memcpy(ptNewArr, m_ptArr, sizeof(T) * m_iSize);
delete m_ptArr;
m_ptArr = ptNewArr;
}
bool is_empty() const
{ // Is the vector empty
return 0 == m_iSize ? true : false;
}
bool is_full() const
{ // Is the vector full
return m_iSize == m_iLength ? true : false;
}
T* const begin() const
{ // The first element
return m_ptArr;
}
T* const end() const
{ // The element after the last
return m_ptArr + m_iSize;
}

T& operator[] (int iNum)
{ // Pick an element by its index
assert(iNum < m_iSize);
return m_ptArr[iNum];
}

static class Iterator
{ // vector Iterator
public:
Iterator(T *ptVal)
: m_ptVal(ptVal)
{
}
Iterator& operator++ ()
{
++m_ptVal;
return *this;
}
Iterator operator++ (int)
{
Iterator iter = m_ptVal++;
return iter;
}
bool operator== (Iterator iter) const
{
return iter.m_ptVal == m_ptVal;
}
bool operator!= (Iterator iter) const
{
return iter.m_ptVal != m_ptVal;
}
T operator *() const
{ // Convert Iterator to T*
return *m_ptVal;
}
private:
T *m_ptVal; // Pointing to one element of the vector
};

private:
int m_iSize; // Number of elements
T *m_ptArr; // An array storing the elements
int m_iLength; // Max length
};
}

#endif // _TTLB_VECTOR_H
...全文
47 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
existsluawjb 2003-09-30
  • 打赏
  • 举报
回复
厉害,佩服
ttlb 2003-09-30
  • 打赏
  • 举报
回复
曾经以为能写出像模像样的代码,但今天看了一下stl,才知道自己的水平有多洼。唉!算了,以后老实一点,绝对不再浮躁,踏踏实实的打基础。
再一次感谢大家的支持。
wfwater 2003-09-27
  • 打赏
  • 举报
回复
强烈要求楼主加上注释。
ttlb 2003-09-27
  • 打赏
  • 举报
回复
还有,我的erase方法只是简单的修改m_iSize,也就是vector的元素个数,怎么才能把元素彻底erase掉呢?
ttlb 2003-09-27
  • 打赏
  • 举报
回复
由于时间原因,注释没加,不好意思啦
ttlb 2003-09-27
  • 打赏
  • 举报
回复
有时间一定好好钻研以下C++的内存管理。多谢yndfcd(YNDFCD),但我的下一个版本目前还没有加入对内存分配的检查,你最新的回复我还没看完呢:)。以后会逐渐完善的。
这次主要加入了erase,insert。另外我把排序的算法转移到了另外一个文件中,由于与vector关系不是很密切,所以没有列出。
另外,请问,iterator除了给用户使用外,在类的内部,也就是说我在编写vector代码的时候可不可以用?

// Copyright: ttlb(Tang Tao) 2003
// Version: 1.4
// This program simulates the Standard Template Library Vector
// As an exercise of Data Structure

#ifndef _TTLB_VECTOR_H
#define _TTLB_VECTOR_H

#include <memory>

namespace ttlb
{
template <typename T>
class Vector
{
public:
explicit Vector(int iLength = MIN_LENGTH);
explicit Vector(T *const ptBegin, T *const ptEnd);
Vector(const Vector &cvSrc);
const Vector& operator = (const Vector &cvSrc);
virtual ~Vector();
int size() const;
void push_back(const T &tSrc);
void pop_back();
void erase(int iFirst, int iNum = m_iSize);
void erase(T *ptFirst, T *ptLast);
void clear();
void insert_before(T tSrc, int iPos);
void insert_before(T tSrc, T *ptPos);
void resize(int iNewLength);
bool is_empty() const;
bool is_full() const;
T* const begin() const;
T* const end() const;
T& operator[] (int iNum);
static class Iterator
{ // Vector Iterator
public:
Iterator(T *ptVal)
: m_ptVal(ptVal)
{
}
Iterator& operator++ ()
{
++m_ptVal;
return *this;
}
Iterator operator++ (int)
{
Iterator iter = m_ptVal++;
return iter;
}
bool operator== (Iterator iter) const
{
return iter.m_ptVal == m_ptVal;
}
bool operator!= (Iterator iter) const
{
return iter.m_ptVal != m_ptVal;
}
T operator *() const
{ // Convert Iterator to T*
return *m_ptVal;
}
private:
T *m_ptVal; // Pointing to one element of the Vector
};

private:
int m_iSize; // Number of elements
int m_iLength; // Max length

T *m_ptArr; // An array storing the elements

static const int MIN_LENGTH = 100;
};

// member functions
template <typename T>
Vector<T>::Vector(int iLength = MIN_LENGTH)
: m_iSize(0), m_iLength(iLength)
{
if (100 > m_iLength)
{ // Vector must have at least MIN_LENGTH element
m_iLength = MIN_LENGTH;
}
m_ptArr = new T[m_iLength];
}

template <typename T>
Vector<T>::Vector(T *const ptBegin, T *const ptEnd)
: m_iSize(0), m_iLength(MIN_LENGTH)
{ // copy between ptBegin and ptEnd (>=ptBegin, <ptEnd)
m_ptArr = new T[m_iLength];
for (T *ptIter = ptBegin; ptIter != ptEnd; ++ptIter)
{
push_back(*ptIter);
}
}
template <typename T>
Vector<T>::Vector(const Vector &cvSrc)
: m_iSize(cvSrc.m_iSize), m_iLength(cvSrc.m_iLength)
{ // copy constructor
m_ptArr = new T[m_iLength];
memcpy(m_ptArr, dVSrc.m_ptArr, sizeof(T) * m_iSize);
}
template <typename T>
const Vector<T>& Vector<T>::operator = (const Vector &cvSrc)
{ // copy assignment
if (this == &cvSrc)
{ // check whether assign to itself
return *this;
}
delete[] m_ptArr;
m_iSize = cvSrc.size();
m_iLength = cvSrc.m_iLength;
m_ptArr = new T[m_iLength];
memcpy(m_ptArr, cvSrc.m_ptArr, sizeof(T) * m_iSize);
return *this;
}
template <typename T>
Vector<T>::~Vector()
{ // virtual to support derivation
delete[] m_ptArr;
}
template <typename T>
int Vector<T>::size() const
{ // Size of the Vector
return m_iSize;
}
template <typename T>
void Vector<T>::push_back(const T &tSrc)
{ // Push an element at the back
if (is_full())
resize(static_cast<int>(1.5 * m_iLength));
m_ptArr[m_iSize] = tSrc;
++m_iSize;
}
template <typename T>
void Vector<T>::pop_back()
{ // Pop an element at the back
if (!is_empty())
return m_ptArr[--m_iSize];
}
template <typename T>
void Vector<T>::erase(int iFirst, int iNum)
{
if (!is_empty() && iFirst < m_iSize && iFirst >= 0)
{
int iEraseNumber = m_iSize - iFirst < iNum ?
m_iSize - iFirst : iNum;
memcpy(begin() + iFirst, begin() + iFirst + iEraseNumber,
sizeof(T) * (m_iSize - iFirst - iEraseNumber));
m_iSize -= iEraseNumber;
}
}
template <typename T>
void Vector<T>::erase(T *ptFirst, T *ptLast)
{
if (!is_empty() && ptFirst < ptLast && ptFirst >= begin() && ptLast <= end())
{
T *iter = begin();
int iFirst = 0;
while (iter != ptFirst)
{
++iFirst;
++iter;
}
erase(iFirst, ptLast - ptFirst + 1);
}
}
template <typename T>
void Vector<T>::clear()
{
erase(0, m_iSize);
}
template <typename T>
void Vector<T>::insert_before(T tSrc, int iPos)
{
if (iPos >= 0 && iPos <= m_iSize)
{
if (is_full())
resize(2 * m_iLength);
int iNum = m_iSize - iPos;
memcpy(begin() + iPos + 1, begin() + iPos, sizeof(T) * iNum);
m_ptArr[iPos] = tSrc;
++m_iSize;
}
}
template <typename T>
void Vector<T>::insert_before(T tSrc, T *ptPos)
{
if (ptPos >= begin() && ptPos <= end())
{
T *iter = begin();
int iPos = 0;
while (iter != ptPos)
{
++iPos;
++iter;
}
insert_before(tSrc, iPos);
}
}
template <typename T>
void Vector<T>::resize(int iNewLength)
{ // Resize the Vector to the specified size
if (iNewLength >= m_iSize)
{
m_iLength = iNewLength;
T *ptNewArr = new T[m_iLength];
memcpy(ptNewArr, m_ptArr, sizeof(T) * m_iSize);
delete m_ptArr;
m_ptArr = ptNewArr;
}
}
template <typename T>
bool Vector<T>::is_empty() const
{ // Is the Vector empty
return 0 == m_iSize ? true : false;
}
template <typename T>
bool Vector<T>::is_full() const
{ // Is the Vector full
return m_iSize == m_iLength ? true : false;
}
template <typename T>
T* const Vector<T>::begin() const
{ // The first element
return m_ptArr;
}
template <typename T>
T* const Vector<T>::end() const
{ // The element after the last
return m_ptArr + m_iSize;
}

template <typename T>
T& Vector<T>::operator[] (int iNum)
{ // Pick an element by its index
return m_ptArr[iNum];
}

}

#endif // _TTLB_VECTOR_H
yndfcd 2003-09-25
  • 打赏
  • 举报
回复
3. 假设全局operator new为类型x的对象分配内存成功,, x的operator new会再次调用标准set_new_handler来恢复最初的全局出错处理函数。最后返回分配成功的内存的指针。
c++是这么做的:


void * x::operator new(size_t size)
{
new_handler globalhandler = // 安装x的new_handler
std::set_new_handler(currenthandler);

void *memory;

try { // 尝试分配内存
memory = ::operator new(size);
}

catch (std::bad_alloc&) { // 恢复旧的new_handler
std::set_new_handler(globalhandler);
throw; // 抛出异常
}
std::set_new_handler(globalhandler); // 恢复旧的new_handler
return memory;
}

如果你对上面重复调用std::set_new_handler看不顺眼,可以参见条款m9来除去它们。

使用类x的内存分配处理功能时大致如下:


void nomorememory();// x的对象分配内存失败时调用的new_handler函数的声明

x::set_new_handler(nomorememory);
// 把nomorememory设置为x的
// new-handling函数
x *px1 = new x;
// 如内存分配失败,
// 调用nomorememory
string *ps = new string;
// 如内存分配失败,调用全局new-handling函数

x::set_new_handler(0);
// 设x的new-handling函数为空

x *px2 = new x;
// 如内存分配失败,立即抛出异常
// (类x没有new-handling函数)

你会注意到,处理以上类似情况,如果不考虑类的话,实现代码是一样的,这就很自然地想到在别的地方也能重用它们。正如条款41所说明的,继承和模板可以用来设计可重用代码。在这里,我们把两种方法结合起来使用,从而满足了你的要求。

你只要创建一个“混合风格”(mixin-style)的基类,这种基类允许子类继承它某一特定的功能——这里指的是建立一个类的new-handler的功能。之所以设计一个基类,是为了让所有的子类可以继承set_new_handler和operator new功能,而设计模板是为了使每个子类有不同的currenthandler数据成员。这听起来很复杂,不过你会看到代码其实很熟悉。区别只不过是它现在可以被任何类重用了。


template<class t> // 提供类set_new_handler支持的
class newhandlersupport { // 混合风格”的基类
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);

private:
static new_handler currenthandler;
};

template<class t>
new_handler newhandlersupport<t>::set_new_handler(new_handler p)
{
new_handler oldhandler = currenthandler;
currenthandler = p;
return oldhandler;
}

template<class t>
void * newhandlersupport<t>::operator new(size_t size)
{
new_handler globalhandler =
std::set_new_handler(currenthandler);
void *memory;
try {
memory = ::operator new(size);
}
catch (std::bad_alloc&) {
std::set_new_handler(globalhandler);
throw;
}

std::set_new_handler(globalhandler);
return memory;
}
// this sets each currenthandler to 0

template<class t>
new_handler newhandlersupport<t>::currenthandler;
有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承:
// note inheritance from mixin base class template. (see
// my article on counting objects for information on why
// private inheritance might be preferable here.)
class x: public newhandlersupport<x> {

... // as before, but no declarations for
}; // set_new_handler or operator new


使用x的时候依然不用理会它幕后在做些什么;老代码依然工作。这很好!那些你常不去理会的东西往往是最可信赖的。

使用set_new_handler是处理内存不够情况下一种方便,简单的方法。这比把每个new都包装在try模块里当然好多了。而且,newhandlersupport这样的模板使得向任何类增加一个特定的new-handler变得更简单。“混合风格”的继承不可避免地将话题引入到多继承上去,在转到这个话题前,你一定要先阅读条款43。

1993年前,c++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new(以及operator new[]——见条款8)以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象:


class widget { ... };

widget *pw1 = new widget;// 分配失败抛出std::bad_alloc if

if (pw1 == 0) ... // 这个检查一定失败
widget *pw2 = new (nothrow) widget; // 若分配失败返回0

if (pw2 == 0) ... // 这个检查可能会成功

不管是用“正规”(即抛出异常)形式的new还是“无抛出”形式的new,重要的是你必须为内存分配失败做好准备。最简单的方法是使用set_new_handler,因为它对两种形式都有用


节选自Effective C++
yndfcd 2003-09-25
  • 打赏
  • 举报
回复
operator new在无法完成内存分配请求时会抛出异常(以前的做法一般是返回0,一些旧一点的编译器还这么做。你愿意的话也可以把你的编译器设置成这样。关于这个话题我将推迟到本条款的结尾处讨论)。大家都知道,处理内存不够所产生的异常真可以算得上是个道德上的行为,但实际做起来又会象刀架在脖子上那样痛苦。所以,你有时会不去管它,也许一直没去管它。但你心里一定还是深深地隐藏着一种罪恶感:万一new真的产生了异常怎么办?

你会很自然地想到处理这种情况的一种方法,即回到以前的老路上去,使用预处理。例如,c的一种常用的做法是,定义一个类型无关的宏来分配内存并检查分配是否成功。对于c++来说,这个宏看起来可能象这样:


#define new(ptr, type) \
try { (ptr) = new type; } \
catch (std::bad_alloc&) { assert(0); }

(“慢!std::bad_alloc是做什么的?”你会问。bad_alloc是operator new不能满足内存分配请求时抛出的异常类型,std是bad_alloc所在的名字空间(见条款28)的名称。“好!”你会继续问,“assert又有什么用?”如果你看看标准c头文件<assert.h>(或与它相等价的用到了名字空间的版本<cassert>,见条款49),就会发现assert是个宏。这个宏检查传给它的表达式是否非零,如果不是非零值,就会发出一条出错信息并调用abort。assert只是在没定义标准宏ndebug的时候,即在调试状态下才这么做。在产品发布状态下,即定义了ndebug的时候,assert什么也不做,相当于一条空语句。所以你只能在调试时才能检查断言(assertion))。

new宏不但有着上面所说的通病,即用assert去检查可能发生在已发布程序里的状态(然而任何时候都可能发生内存不够的情况),同时,它还在c++里有另外一个缺陷:它没有考虑到new有各种各样的使用方式。例如,想创建类型t对象,一般有三种常见的语法形式,你必须对每种形式可能产生的异常都要进行处理:


new t;
new t(constructor arguments);
new t[size];

这里对问题大大进行了简化,因为有人还会自定义(重载)operator new,所以程序里会包含任意个使用new的语法形式。

那么,怎么办?如果想用一个很简单的出错处理方法,可以这么做:当内存分配请求不能满足时,调用你预先指定的一个出错处理函数。这个方法基于一个常规,即当operator new不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数——一般称为new-handler函数。(operator new实际工作起来要复杂一些,详见条款8)

指定出错处理函数时要用到set_new_handler函数,它在头文件<new>里大致是象下面这样定义的:


typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

可以看到,new_handler是一个自定义的函数指针类型,它指向一个没有输入参数也没有返回值的函数。set_new_handler则是一个输入并返回new_handler类型的函数。

set_new_handler的输入参数是operator new分配内存失败时要调用的出错处理函数的指针,返回值是set_new_handler没调用之前就已经在起作用的旧的出错处理函数的指针。

可以象下面这样使用set_new_handler:


// function to call if operator new can't allocate enough memory
void nomorememory()
{
cerr << "unable to satisfy request for memory\n";
abort();
}

int main()
{
set_new_handler(nomorememory);
int *pbigdataarray = new int[100000000];

...

}

假如operator new不能为100,000,000个整数分配空间,nomorememory将会被调用,程序发出一条出错信息后终止。这就比简单地让系统内核产生错误信息来结束程序要好。(顺便考虑一下,假如cerr在写错误信息的过程中要动态分配内存,那将会发生什么...)

operator new不能满足内存分配请求时,new-handler函数不只调用一次,而是不断重复,直至找到足够的内存。实现重复调用的代码在条款8里可以看到,这里我用描述性的的语言来说明:一个设计得好的new-handler函数必须实现下面功能中的一种。
·产生更多的可用内存。这将使operator new下一次分配内存的尝试有可能获得成功。实施这一策略的一个方法是:在程序启动时分配一个大的内存块,然后在第一次调用new-handler时释放。释放时伴随着一些对用户的警告信息,如内存数量太少,下次请求可能会失败,除非又有更多的可用空间。
·安装另一个不同的new-handler函数。如果当前的new-handler函数不能产生更多的可用内存,可能它会知道另一个new-handler函数可以提供更多的资源。这样的话,当前的new-handler可以安装另一个new-handler来取代它(通过调用set_new_handler)。下一次operator new调用new-handler时,会使用最近安装的那个。(这一策略的另一个变通办法是让new-handler可以改变它自己的运行行为,那么下次调用时,它将做不同的事。方法是使new-handler可以修改那些影响它自身行为的静态或全局数据。)
·卸除new-handler。也就是传递空指针给set_new_handler。没有安装new-handler,operator new分配内存不成功时就会抛出一个标准的std::bad_alloc类型的异常。
·抛出std::bad_alloc或从std::bad_alloc继承的其他类型的异常。这样的异常不会被operator new捕捉,所以它们会被送到最初进行内存请求的地方。(抛出别的不同类型的异常会违反operator new异常规范。规范中的缺省行为是调用abort,所以new-handler要抛出一个异常时,一定要确信它是从std::bad_alloc继承来的。想更多地了解异常规范,参见条款m14。)
·没有返回。典型做法是调用abort或exit。abort/exit可以在标准c库中找到(还有标准c++库,参见条款49)。

上面的选择给了你实现new-handler函数极大的灵活性。

处理内存分配失败的情况时采取什么方法,取决于要分配的对象的类:


class x {
public:
static void
outofmemory();

...

};

class y {
public:
static void outofmemory();

...

};

x* p1 = new x; // 若分配成功,调用x::outofmemory
y* p2 = new y; // 若分配不成功,调用y::outofmemory

c++不支持专门针对于类的new-handler函数,而且也不需要。你可以自己来实现它,只要在每个类中提供自己版本的set_new_handler和operator new。类的set_new_handler可以为类指定new-handler(就象标准的set_new_handler指定全局new-handler一样)。类的operator new则保证为类的对象分配内存时用类的new-handler取代全局new-handler。

假设处理类x内存分配失败的情况。因为operator new对类型x的对象分配内存失败时,每次都必须调用出错处理函数,所以要在类里声明一个new_handler类型的静态成员。那么类x看起来会象这样:


class x {
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);

private:
static new_handler currenthandler;
};

类的静态成员必须在类外定义。因为想借用静态对象的缺省初始化值0,所以定义x::currenthandler时没有去初始化。


new_handler x::currenthandler; //缺省设置currenthandler为0(即null)
类x中的set_new_handler函数会保存传给它的任何指针,并返回在调用它之前所保存的任何指针。这正是标准版本的set_new_handler所做的:


new_handler x::set_new_handler(new_handler p)
{
new_handler oldhandler = currenthandler;
currenthandler = p;
return oldhandler;
}

最后看看x的operator new所做的:
1. 调用标准set_new_handler函数,输入参数为x的出错处理函数。这使得x的new-handler函数成为全局new-handler函数。注意下面的代码中,用了"::"符号显式地引用std空间(标准set_new_handler函数就存在于std空间)。

2. 调用全局operator new分配内存。如果第一次分配失败,全局operator new会调用x的new-handler,因为它刚刚(见1.)被安装成为全局new-handler。如果全局operator new最终未能分配到内存,它抛出std::bad_alloc异常,x的operator new会捕捉到它。x的operator new然后恢复最初被取代的全局new-handler函数,最后以抛出异常返回。

yumenman 2003-09-25
  • 打赏
  • 举报
回复
mark
ttlb 2003-09-25
  • 打赏
  • 举报
回复
up
loveghb 2003-09-24
  • 打赏
  • 举报
回复
就是啊。
异常处理我也不太懂,因为一般分配内存异常是因为没有内存了,你还能怎么处理?
在硬盘上划个空间?或者是整理一下内存?
可能吧。

希望高手来解答!
ttlb 2003-09-24
  • 打赏
  • 举报
回复
我记得当new无法分配内存的时候,自己就会抛出一个异常,是不是这样?如果是,那assert还有没有意义?
yndfcd 2003-09-24
  • 打赏
  • 举报
回复
T* pNewArr;
assert(pNewArr = new T[m_iSize]);
memcpy(pNewArr,...);
ttlb 2003-09-24
  • 打赏
  • 举报
回复
多谢yndfcd(YNDFCD)的提醒,但请问如何检查内存分配是否成功?以前从没检查过
yndfcd 2003-09-24
  • 打赏
  • 举报
回复
还有一个忘说了,这段代码最大的问题就是没有检查分配内存是否成功。
yndfcd 2003-09-24
  • 打赏
  • 举报
回复
template <typename T>
void vector<T>::resize(int iNewLength)
{ // Resize the vector to the specified size
if (iNewLength >= m_iSize)
{
m_iLength = iNewLength;
T *ptNewArr = new T[m_iLength];
memcpy(ptNewArr, m_ptArr, sizeof(T) * m_iSize);
delete m_ptArr;
m_ptArr = ptNewArr;
}
}
此函数最好前面加一句

if(iNewLength < MIN_LENGTH) return;
同时
delete m_ptArr;->delete [] m_ptArr;
ttlb 2003-09-24
  • 打赏
  • 举报
回复
// 去掉了所有的异常
// 修改了operator= 的返回值

// Copyright: ttlb(Tang Tao) 2003
// Version: 1.3
// This program simulates the Standard Template Library vector
// As an exercise of Data Structure

#ifndef _TTLB_VECTOR_H
#define _TTLB_VECTOR_H

#include <memory>

namespace ttlb
{
template <typename T>
class vector
{
public:
explicit vector(int iLength = MIN_LENGTH);
explicit vector(T *const ptBegin, T *const ptEnd);
explicit vector(const vector &cvSrc);
vector& operator = (const vector &cvSrc);
virtual ~vector();
int size() const;
void push_back(const T &tSrc);
void pop_back();
void resize(int iNewLength);
bool is_empty() const;
bool is_full() const;
T* const begin() const;
T* const end() const;
T& operator[] (int iNum);
static class Iterator
{ // vector Iterator
public:
Iterator(T *ptVal)
: m_ptVal(ptVal)
{
}
Iterator& operator++ ()
{
++m_ptVal;
return *this;
}
Iterator operator++ (int)
{
Iterator iter = m_ptVal++;
return iter;
}
bool operator== (Iterator iter) const
{
return iter.m_ptVal == m_ptVal;
}
bool operator!= (Iterator iter) const
{
return iter.m_ptVal != m_ptVal;
}
T operator *() const
{ // Convert Iterator to T*
return *m_ptVal;
}
private:
T *m_ptVal; // Pointing to one element of the vector
};

private:
int m_iSize; // Number of elements
int m_iLength; // Max length

T *m_ptArr; // An array storing the elements

static const int MIN_LENGTH = 100;
};

// member functions
template <typename T>
vector<T>::vector(int iLength = MIN_LENGTH)
: m_iSize(0), m_iLength(iLength)
{
if (100 > m_iLength)
{ // vector must have at least MIN_LENGTH element
m_iLength = MIN_LENGTH;
}
m_ptArr = new T[m_iLength];
}

template <typename T>
vector<T>::vector(T *const ptBegin, T *const ptEnd)
: m_iSize(0), m_iLength(MIN_LENGTH)
{ // copy between ptBegin and ptEnd (>=ptBegin, <ptEnd)
m_ptArr = new T[m_iLength];
for (T *ptIter = ptBegin; ptIter != ptEnd; ++ptIter)
{
push_back(*ptIter);
}
}
template <typename T>
vector<T>::vector(const vector &cvSrc)
: m_iSize(cvSrc.m_iSize), m_iLength(cvSrc.m_iLength)
{ // copy constructor
m_ptArr = new T[m_iLength];
memcpy(m_ptArr, dVSrc.m_ptArr, sizeof(T) * m_iSize);
}
template <typename T>
vector<T>& vector<T>::operator = (const vector &cvSrc)
{ // copy assignment
if (this == &cvSrc)
{ // check whether assign to itself
return *this;
}
delete[] m_ptVal;
m_iSize = cvSrc.size();
m_iLength = cvSrc.m_iLength;
m_ptArr = new T[];
memcpy(m_ptArr, cvSrc.m_ptArr, sizeof(T) * m_iSize);
return *this;
}
template <typename T>
vector<T>::~vector()
{ // virtual to support derivation
delete[] m_ptArr;
}
template <typename T>
int vector<T>::size() const
{ // Size of the vector
return m_iSize;
}
template <typename T>
void vector<T>::push_back(const T &tSrc)
{ // Push an element at the back
if (is_full())
resize(static_cast<int>(1.5 * m_iLength));
m_ptArr[m_iSize] = tSrc;
++m_iSize;
}
template <typename T>
void vector<T>::pop_back()
{ // Pop an element at the back
if (!is_empty())
return m_ptArr[--m_iSize];
}
template <typename T>
void vector<T>::resize(int iNewLength)
{ // Resize the vector to the specified size
if (iNewLength >= m_iSize)
{
m_iLength = iNewLength;
T *ptNewArr = new T[m_iLength];
memcpy(ptNewArr, m_ptArr, sizeof(T) * m_iSize);
delete m_ptArr;
m_ptArr = ptNewArr;
}
}
template <typename T>
bool vector<T>::is_empty() const
{ // Is the vector empty
return 0 == m_iSize ? true : false;
}
template <typename T>
bool vector<T>::is_full() const
{ // Is the vector full
return m_iSize == m_iLength ? true : false;
}
template <typename T>
T* const vector<T>::begin() const
{ // The first element
return m_ptArr;
}
template <typename T>
T* const vector<T>::end() const
{ // The element after the last
return m_ptArr + m_iSize;
}

template <typename T>
T& vector<T>::operator[] (int iNum)
{ // Pick an element by its index
return m_ptArr[iNum];
}
}
#endif // _TTLB_VECTOR_H
潘李亮 2003-09-22
  • 打赏
  • 举报
回复
我现在是很佩服也很羡慕这样的人,一是有毅力,二是有时间.我现在是忙的会转.绝对没有时间写这些东西.努力啊.
我现在的业余时间全力研究图形学.有空大家一起来讨论啊.
ttlb 2003-09-22
  • 打赏
  • 举报
回复
Andy84920(你也不懂)
should adds exception dealing?

异常处理我刚刚接触,用起来也不大明白。有什么建议吗?
ttlb 2003-09-22
  • 打赏
  • 举报
回复
To:Andy84920(你也不懂) Wolf0403(完美废人):
我用的vc71,msdn上说还不支持separate compilation,e文的,我也看不大明白,不知道是不是真的。请问我怎样才能实现.h .cpp?分开
加载更多回复(19)

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧