Data Structures with C++ Using STL Chapter 5指针和动态内存---笔记
2012-11-28 11:48
726 查看
5.1
声明指针变量
数组名arr是arr[0]地址的指针常量, *arr == arr[0];
p++使p向前移动一个数组位置,语句p--使p向后移动一个数组位置。类似的表达式用于arr是无效的,因为arr是指针常量,不能被更新。
ptr->f()是(*ptr).f()的速记形式
5.2
当一条源代码语句声明变量或对象时,编译器创建一些信息,来指定变量或对象所占有内存量。编译过程会创建可执行文件,其中定义了每个变量和对象内存要求。我们说所有程序变量和对象是静态分配的,是因为编译器决定了它们的内存大小,在运行期间,程序可能需要额外的内存。由于程序员无法预测到所需内存的数量,因而不能将其包括在源代码的声明语句中,所以,编译器不能预先分配空间,认识到这个问题,运行时系统采用了堆,堆可以使程序得到附加内存资源。来自堆中内存称为动态内存,因为程序使用运行时指令分配和释放资源
如果堆没有足够的内存分配给请求的变量或对象,则new操作返回值NULL
new运算符能够为对象动态分配内存,然而程序员必须考虑构造函数,当程序为类型className的对象分配内存时,会调用构造函数。
new运算符为n个类型T的对象请求内存,这就是大小为n的动态数组
当T是基本类型时,动态数组中的元素没有初始化,然而,当T是类时,系统调用默认的构造函数来建立每个数组元素。当动态分配数组时,保留返回地址的指针可以像普通的数组名那样使用。
5.3
在程序的执行过程中,运行时系统不断分配和释放内存来服务于函数调用,创建并在以后销毁局部变量,等等,我们把这种运行时行为称为内存管理。对于静态分配的数据项,程序员必须加入指令来指导内存管理。如果程序运行结束,而不执行delete语句,则不会在堆中释放相应的内存。这种情况称为内存泄漏,因为操作丢失了内存资源。如果不断执行程序,所有可用的堆空间最终将都被认为在使用中。
类经常使用动态内存存储数据。我们把这些内称为动态类。在类的设计和实现中,程序员负责动态内存管理。为了管理内存,动态类通常应该包括为类实例分配动态内存的构造函数和对象被销毁时释放内存的析构函数,如果类允许对象间的赋值,其实现必须包括一个赋值运算符=的重载版本。
当某个类有动态分配内存的对象时,运行系统只是销毁对象,并不销毁任何相关的动态内存。为了有效的管理内存,需要在对象中释放动态数据,将其作为销毁对象过程的一部分,否则,内存驻留在堆中,而指向内存的指针不再有效。此内存不能被程序其余部分访问,这就产生了内存泄漏。C++语言提供了析构函数,当销毁对象时运行时系统调用析构函数。对于任意类,析构函数的原型为类名和字符~。
5.4
初始化是声明语句的一部分,用一个现存数据项的值去初始化新的变量和对象,赋值为把右边数据项的值赋值给左边的变量或对象。赋值和初始化都产生现有数据项的副本,对于分配动态数据的对象,类必须有专门的成员函数,复制构造函数和重载的赋值运算符,使运行时系统能够执行这些操作。
如果dynamicClass使用默认的赋值操作,语句objB = objA 将从objA到objB对数据逐字进行复制。指针objA.member2到指针objB.menber2的默认复制带来了问题,两个指针的变量在内存中指向同一个位置,这使得最初属于objB动态内存留在堆中,导致内存泄漏。如果objA被销毁且程序继续访问objB,将发生运行时错误。销毁objA调用了对象的析构函数,它释放objA.member2指向的数据,objB.member2也指向这些数据。objB中试图访问被释放数据的指令可能导致致命的应用程序错误
C++允许以成员函数形式重载复制运算符=
*this为对象自身,因为运算符=返回当前对象的引用,所有可以把两个或更多赋值语句连用
this指向当前对象的指针,*this是对象本身。
对象赋值需使用赋值构造函数,为了正确地处理分配动态内存的类,C++提供了复制构造函数来为新对象分配动态内存并初始化其数据成员。复制构造函数是一个构造函数,所以成员函数使用类名作为函数名。同一类型的一个对象是唯一的参数,而且函数没有返回值。dynamicClass中复制构造函数的原型为:
运行时系统调用复制构造函数来执行初始化和创建新对象,如果类不能提供一个显式的复制构造函数,则系统调用默认复制构造函数,默认版本只是简单的将数据成员从现有对象到新对象,对于动态类,默认版本可能产生错误。
复制构造函数的参数必须是引用参数。
5.5
miniVector类(STL向量类的简化版本)
5.6
在基于模板的矩阵类的设计中,我们在一个由向量组成的向量中存储元素,私有数据成员mat保留矩阵项目
当声明一个模板类型中嵌套一个模板类型时,程序员必须确保两个>符号不能连续出现,中间加空格,否则编译器会和>>混淆
声明指针变量
int *intptr; char *charptr;
int m=50,*intptr; intptr=&m; //&是取地址符号
数组名arr是arr[0]地址的指针常量, *arr == arr[0];
int *p p = arr; // p[1] = *(p+1) =arr[1]
p++使p向前移动一个数组位置,语句p--使p向后移动一个数组位置。类似的表达式用于arr是无效的,因为arr是指针常量,不能被更新。
ptr->f()是(*ptr).f()的速记形式
5.2
当一条源代码语句声明变量或对象时,编译器创建一些信息,来指定变量或对象所占有内存量。编译过程会创建可执行文件,其中定义了每个变量和对象内存要求。我们说所有程序变量和对象是静态分配的,是因为编译器决定了它们的内存大小,在运行期间,程序可能需要额外的内存。由于程序员无法预测到所需内存的数量,因而不能将其包括在源代码的声明语句中,所以,编译器不能预先分配空间,认识到这个问题,运行时系统采用了堆,堆可以使程序得到附加内存资源。来自堆中内存称为动态内存,因为程序使用运行时指令分配和释放资源
//为类型T的变量分配未初始化的内存 ptr = new T; //为类型T分配内存并初始化其值为initValue ptr = new T(initValue);
如果堆没有足够的内存分配给请求的变量或对象,则new操作返回值NULL
new运算符能够为对象动态分配内存,然而程序员必须考虑构造函数,当程序为类型className的对象分配内存时,会调用构造函数。
new运算符为n个类型T的对象请求内存,这就是大小为n的动态数组
T *ptr ptr = new T ; //ptr是n个元素数组的地址
当T是基本类型时,动态数组中的元素没有初始化,然而,当T是类时,系统调用默认的构造函数来建立每个数组元素。当动态分配数组时,保留返回地址的指针可以像普通的数组名那样使用。
ptr = new T; //分配动态内存 delete ptr; //释放分配的动态内存 //释放动态数组 arr = new T[ARRSIZE]; delete [] arr; //在delete和指针变量之间放一个中括号,系统释放所有最初分配给动态数组的内存
5.3
在程序的执行过程中,运行时系统不断分配和释放内存来服务于函数调用,创建并在以后销毁局部变量,等等,我们把这种运行时行为称为内存管理。对于静态分配的数据项,程序员必须加入指令来指导内存管理。如果程序运行结束,而不执行delete语句,则不会在堆中释放相应的内存。这种情况称为内存泄漏,因为操作丢失了内存资源。如果不断执行程序,所有可用的堆空间最终将都被认为在使用中。
类经常使用动态内存存储数据。我们把这些内称为动态类。在类的设计和实现中,程序员负责动态内存管理。为了管理内存,动态类通常应该包括为类实例分配动态内存的构造函数和对象被销毁时释放内存的析构函数,如果类允许对象间的赋值,其实现必须包括一个赋值运算符=的重载版本。
//带有参数的构造函数,参数用于初始化数据成员 //使用构造函数初始化表把数值m1赋值给member1 template <typename T> dynamicClass<T>::dynamicClass(const T& m1,const T& m2):member1(m1) { //分配动态内存 member2 = new T(m2); }
当某个类有动态分配内存的对象时,运行系统只是销毁对象,并不销毁任何相关的动态内存。为了有效的管理内存,需要在对象中释放动态数据,将其作为销毁对象过程的一部分,否则,内存驻留在堆中,而指向内存的指针不再有效。此内存不能被程序其余部分访问,这就产生了内存泄漏。C++语言提供了析构函数,当销毁对象时运行时系统调用析构函数。对于任意类,析构函数的原型为类名和字符~。
template <typename T> dynamicClass<T>::~dynamicClass() { delete member2; }
5.4
初始化是声明语句的一部分,用一个现存数据项的值去初始化新的变量和对象,赋值为把右边数据项的值赋值给左边的变量或对象。赋值和初始化都产生现有数据项的副本,对于分配动态数据的对象,类必须有专门的成员函数,复制构造函数和重载的赋值运算符,使运行时系统能够执行这些操作。
如果dynamicClass使用默认的赋值操作,语句objB = objA 将从objA到objB对数据逐字进行复制。指针objA.member2到指针objB.menber2的默认复制带来了问题,两个指针的变量在内存中指向同一个位置,这使得最初属于objB动态内存留在堆中,导致内存泄漏。如果objA被销毁且程序继续访问objB,将发生运行时错误。销毁objA调用了对象的析构函数,它释放objA.member2指向的数据,objB.member2也指向这些数据。objB中试图访问被释放数据的指令可能导致致命的应用程序错误
C++允许以成员函数形式重载复制运算符=
//重载赋值运算符,返回当前对象的指针 template <typename T> dynamicClass<T>& dynamicClass<T>::operator = (const dynamicClass<T>& rhs) { //从rhs复制静态数据成员到当前对象 member1 = rhs.member1; //动态内存的内存必须和rhs的内存一样 *member2 = *rhs.member2; return *this; }
*this为对象自身,因为运算符=返回当前对象的引用,所有可以把两个或更多赋值语句连用
objC = objB = objA;
this指向当前对象的指针,*this是对象本身。
对象赋值需使用赋值构造函数,为了正确地处理分配动态内存的类,C++提供了复制构造函数来为新对象分配动态内存并初始化其数据成员。复制构造函数是一个构造函数,所以成员函数使用类名作为函数名。同一类型的一个对象是唯一的参数,而且函数没有返回值。dynamicClass中复制构造函数的原型为:
//复制构造函数,初始化新对象,使其与obj有同样的数据 //使用构造函数初始化表,把数值obj。member1赋给member1 template <typename T> dynamicClass<T>::dynamicClass(const dynamicClass<T>& obj):member1(obj.member1) { //分配动态内存,并用值*obj.member2初始化动态内存 member2 = new T(*obj.member2); }
运行时系统调用复制构造函数来执行初始化和创建新对象,如果类不能提供一个显式的复制构造函数,则系统调用默认复制构造函数,默认版本只是简单的将数据成员从现有对象到新对象,对于动态类,默认版本可能产生错误。
复制构造函数的参数必须是引用参数。
5.5
miniVector类(STL向量类的简化版本)
template <typename T> class miniVector { public: miniVector(int size = 0); //构造函数 //后置条件:使用元素数目和容量分配数组,所有元素被初始化为T() //T()是类型T的默认值 miniVector(const miniVector<T> & obj); //复制构造函数 //后置条件:以obj为副本生成当前向量 ~miniVector(); //析构函数 //后置条件:动态数组被销毁 miniVector& opetator = (const miniVector<T> & rhs); //赋值运算符 //后置条件:当前向量与rhs有相同的数据 T& back(); //返回向量尾部的元素 //前提条件:向量非空。如果向量为空,则产生一个underflowError异常 const T& back() const; //常量版本 T& operator[] (int i); //用下标提供对元素的常量访问 //前提条件:0<=i<vSize.如果下标超出范围 //则产生一个indexRangeError异常 const T& operator[] (int i) const; //常量版本 void push_back(const T& item); //在向量尾部插入元素 void pop_back(); //删除向量尾部的元素 int size() const; //返回当前表的大小 bool empty() const; //如果向量为空,则返回true int capacity() const; //返回向量的当前容量 private: int vCapacity; //可用空间的大小 int vSize; //表中的元素个数 T *vArr; //动态数组 void reserve(int n,bool copy); //仅当n>vCapacity时被公用函数调用,扩展向量的容量到n个元素,如果copy == true,则将现有元素复制到新空间,删除旧的动态数组。如果内存分配失败,则产生memoryAllocationError异常
template <typename T> void miniVector<T>::reserve(int n,bool copy) { T *newArr; int i; //分配新的n个元素的动态数组 newArr = new T ; if(newArr == NULL) throw memoryAllocationError("miniVector reserve():memory allocation failure"); //如果copy为true,则从旧表复制元素到新表 if(copy) for(i = 0;i < vSize;i++) newArr[i] = vArr[I]; //如果vArr是NULL,向量最初为空,没有要删除的内存,否则,删除初始数组 if(vArr != NULL) delete [] vArr; //更新vCapacity和vArr vCapacity = n; vArr = newArr; }
//构造函数,初始化vSize和vCapacity,分配动态数组,它有vSize个参数 //用T()初始化此数组 template <typename T> miniVector<T>::miniVector(int size):vSize(0),vCapacity(0),vArr(NULL) { int i; if(size == 0) return; reserve(size,false); reserve(size,false); vSize = size; for(i=0; i < vSize; i++) vArr[i] = T(); }
//析构函数 template <typename T> miniVector<T>::~miniVector() { if(vArr != NULL) delete [] vArr; }
template <typename T> miniVector<T> & miniVector<T>::operator = (const miniVector<T> & rhs) { int i; //检查vCapacity,看是否必须分配新数组 if(vCapacity < rhs.vSize) //使当前对象的容量与rhs的大小相同,不要复制,因为将要替换旧值 reverse(rhs.vSize,false); //使当前对象与rhs由同样的大小 vSize = rhs.vSize; //从rhs.vArr复制数据项到vArr数组 for(i = 0;i < vSize;i++) vArr[i] = rhs.vArr[i]; return *this; }
template <typename T> void miniVector<T>::push_back(const T& item) { if(vSize == vCapacity) { if(vCapacity == 0) //如果容量为0,则设置容量为1,设置copy为false,因为不存在现有元素 reserve(1,false); else reserve(2*vCapacity,true); } vArr[vSize] = item; vSize++; }
template <typename T> void miniVector<T>::pop_back() { if(vSize == 0) throw underflowError("miniVector pop_back():vector is empty"); vSize --; }
template <typename T> T& miniVector<T>::back() { if(vSize == 0) throw underflowError("miniVector back(): vector empty"); return vArr[vSize -1]; }
//提供对数组的常规访问 template <typename T> T& miniVector<T>::operator[] (int i) { if(i < 0 || i >= vSize) throw indexRangeError("miniVector:index range error",i,vSize); return vArr[i]; }
5.6
在基于模板的矩阵类的设计中,我们在一个由向量组成的向量中存储元素,私有数据成员mat保留矩阵项目
vector<vector<T> >mat; //私有成员保存矩阵元素
当声明一个模板类型中嵌套一个模板类型时,程序员必须确保两个>符号不能连续出现,中间加空格,否则编译器会和>>混淆
相关文章推荐
- Data Structures with C++ Using STL Chapter 1数据结构入门---笔记
- Data Structures with C++ Using STL Chapter 3算法概述---笔记
- Data Structures with C++ Using STL Chapter 2对象设计技术---笔记
- Data Structures with C++ Using STL Chapter 4向量容器---笔记
- Data Structures with C++ Using STL Chapter 3算法概述---笔记
- Data Structures with C++ Using STL Chapter 3算法概述---笔记
- Data Structures with C++ Using STL Chapter 3算法概述---笔记
- Data Structures with C++ Using STLChapter 1数据结构入门---作业
- Data Structures and Other Objects Using C++ (Chapter 2) 学习笔记五
- Data Structures and Other Objects Using C++ (Chapter 2) 学习笔记四
- Data Structures and Other Objects Using C++ (Chapter 1) 学习笔记二
- Data Structures and Other Objects Using C++ (Chapter 1) 学习笔记三
- C++ 学习笔记(12)动态内存、智能指针、new和delete、动态数组、allocator
- Data Structures and Other Objects Using C++ (Chapter 1) 学习笔记一
- C++类中静态STL容器中的指针所指向的动态内存空间如何释放!
- C++基础学习笔记----第五课(动态内存分配、命名空间、强制类型转换)
- 浅谈C++普通指针和智能指针管理动态内存的陷阱
- C# 2012 step by step 学习笔记8 CHAPTER 9 Creating Value types with enumerations and Structures
- C++指针与动态内存的关系
- 基于Problem Solving with Algorithms and Data Structures using Python的学习记录(3)——Basic Data Structures