`
tansitongba
  • 浏览: 505037 次
文章分类
社区版块
存档分类
最新评论

.C++ primer第二次阅读学习笔记(第18章:特殊工具与技术:优化内存分配)

 
阅读更多

第十八章:特殊工具与技术:优化内存分配

C++类型分配是一个类型化操作:new特定类型分配内存,并在新分配的内存中构造对象。new表达式会为每个动态分配的类自动调用构造函数。但有些时候需要将内存分配与对象构造分开,因为对预先分配,但以后却不使用的对象进行构造很是浪费的。

内存分配和对象构造紧密纠缠,就像对象析构和内存回收一样。new表达式分配内存并在该内存中构造一个对象,delete表达式调用析构函数撤销对象,并将对像所用内存返还给系统。

C++提供两种方法,分配和释放未构造的的原始内存:

1allocator类,它提供可感知类型的内存分配。这个类使用allocate成员分配内存,使用deallocate成员释放内存。

2标准库中的operator new operator delete,它们分配和释放需要大小的原始的、未类型化的内存。

C++还提供不同的方法,在原始内存中构造和撤销对象。

1:allocator类定义了名为constructdestroy的成员,其操作正如它们的名字所指出的那样。construct成员在未构造的内存中调用复制构造函数初始化对象。destroy成员在对象上运行适当的析构函数。

2:定位new表达式。接受指定未构造内存的指针,并在该空间初始化一个对象或数组。

3:可以直接调用对象的析构函数来撤销对象。运行析构函数不释放对象所在的内存。

4:算法uninitialized_filluninitialized_copyfillcopy类似。只是它们在给定地址调用复制构造函数构造对象

现代C++一般应该使用allocator来分配内存。它更安全更灵活。但是在构造对象时用new表达式比allocator::construct成员更灵活。且有几种情况必须使用new

allocator类是一个模板,它将内存的分配和对象构造分开。当allocator对象分配内存的时候,它分配适当大小指定类型对象的空间。但是它分配的空间是未构造的,allocator的用户必须分别使用constructdestroy构造和析构对象。

allocator<T> a定义名为aallocator对象,它用于分配内存或构造T类型的对象。

allocate(n);分配原始的、未构造的内存,保存T类型的n个对象。

deallocate(p,n);释放内存,在类型为T*的指针p指向的地址,保存着n个对象,运行deallocate之前调用destroy是用户的责任。

a.contruct(p,t);T*类型指针p所指向的内存中构造一个新元素。运行T类型的复制构造函数t初始化该对象。

a.destroy(p);运行T*类型指针p所指向对象的析构函数。

uninitialized_fill(b,e,t);将由迭代器be标记的范围的对象,初始化为t的副本,它是用复制构造函数构造对象。

uninitiated_copy(b,e,b2)从迭代器be指出的输入范围,将元素复制到从迭代器b2开始的,未构造的原始内存中。该函数在目的地构造元素,而不是给它们赋值。假定b2指出的目的地址足以保存输入范围中元素的副本。

当使用new操作符时,实际发生了三个步骤:1:调用名为operator new的标准库函数。分配足够大的原始的未类型化的内存,以保存指定类型的对象。2:运行该类型的一个构造函数,用指定初始化式构造对象。3:返回指向新分配并构造对象的指针。

使用delete操作符时,发生两个步骤:1:对指向的对象运行析构函数。2:调用名为operator delete的标准库函数释放该对象的内存。

要注意分清new表达式和标准库的operator new函数。

Operator newoperator delete有两个不同的版本。每个版本支持相关的new表达式和delete表达式。

void *operator new(size_t);

vodi *operator new [](size_t);

voidoperator delete(void*);

voidoperator delete[](void*);

通常operator newoperator delete的设计意图,是供newdelete表达式使用,但是我们仍然可以使用它们获得未构造的内存。这与allocatorallocatedeallocate功能相同。如:

T* newelements=alloc.allocate(num);

operator new替代为:

T*newelements=static_cast<T*> (operator new[] (num*sizeof(T));

alloc.deallocate(elements,end-elements);

operator delete替换为:

operator delete[](elements);

注意operator newoperator deleteallocatedeallocate的区别就是:它们在void*的指针上进行操作。而allocatedeallocate类型为T的指针上进行操作。

allocator分配类型化的内存,无须转换因此allocator比直接使用operator newoperator更为类型安全。

定位new表达式在已分配的原始内存中初始化一个对象。它与new的其他版本的不同之处在于:它不分配内存。它的形式为:

new(地址)类型

new(地址)类型(初始化表)

初始化表是在构造新分配的对象时使用的。

Alloc.construct(first_free,t);

用定位new替代为:

new(first_free)T(t);

使用定位new表达式比使用allocator类的construct成员更灵活。因为定位new在初始化一个对象的时候,它可以使用任何构造函数,而construct函数总是使用复制构造函数

析构函数可以被显式调用,如p->~T();它调用类型T的析构函数,适当的清楚对象本身,但是没有释放对象所占内存。注意:调用operator delete不会运行析构函数,它只释放指定内存。

默认情况下new表达式通过由标准库定义的operator new版本分配内存,通过自定义的名为operator newoperator delete的成员函数,类可以管理应用于自身类型的内存。

编译器在看到类类型的newdelete表达式时,它查看该类是否有operator newoperator delete成员。如果该类定义或继承了自己的operator newoperator delete函数,则使用它们为对象分配和释放内存。否则调用标准库的版本。

自定义的operator newoperator delete默认为静态的。不必显式的声明为static,编译器默认将它们视为static函数。因为它们要么在构造对象之前使用,要么在撤销对象之后使用,因此,这些函数不依赖类的对象而存在。

classTest

{

public:

Test()

{

}

staticvoid *operatornew(size_tnum)//num表示要分配空间的字节数。

{

}

staticvoidoperator delete(void *s)//s为要删除的指针。

{

}

//或者

staticvoidoperator delete(void *s,size_tn) s为要删除的指针。ns所指向对象的字节。

{

}

};

当为operator delete提供size_t形参时,就由编译器用第一个形参所指对象的字节大小,自动初始化size_t形参。当类是某继承层次的一部分时,这是必需的。因为。指针既可以指向基类对象,又可以指向派生类对象。派生类的对象大小一般比基类对象要大,如果基类有virtual析构函数,则传给operator delete的大小,将根据被删除指针所指向对象的动态类型而变化。如果基类没有virtual析构函数,通过基类指针删除指向派生类对象的行为,是未定义的。

同样也可以定义成员operator new[]operator delete[]来管理类类型的数组。如果这些函数存在,编译器就是用它们代替标准库的版本。

当类定义了自己的operator newoperator delete,标准库的operator newoperator delete就被屏蔽。但是可以通过全局作用域操作符强制newdelete表达式使用全局的库函数。如:

T*p=::new T;

::delete p

以下为自定义Vector类:

接下来实现一个内存分配器基类。它预先分配一块原始内存,来保存未构造的对象。创建新元素时,可以在预先分配的内存中构造,释放元素时将它们放回预先分配对象的块中,而不是将内存实际返还给系统。这种策略常被成为维持一个自由列表。本例将自由列表实现为已分配但未构造的对象的链表。

有可能很多类都需要使用自由列表的分配策略。因此任何需要这种策略的类,都可以直接继承自这个类。因为这个类希望为任意类型服务,所以它被定义为类模板。

它有几个简单接口:重写的operator new成员函数:new 操作符被调用时调用此函数。它从自由列表中取走一个未构造的元素。因为new操作符的第二步会调用定位new操作符构造元素。Operator delete成员函数,它将要删除的指针指向的对象添加到自由列表中而不是返还给系统。

注意:此类只能用于包含在继承层次中的类型。因为它无法根据对象的实际类型,分配不同大小的对象。它的自由列表存储单一大小的对象。

使用allocator分配空间。它是静态的。自由列表头指针和其他一些变量都是被声明为static的成员变量,因为我们希望为所有相同类型的对象维持一个自由列表。代码实现过程中出现了链接错误,检查好久,最后才发现是staticallocator<T> alloc没有在类外定义一次。这要注意啊。

addToFreeList用于将一个指针指向的未构造或析构过的空间加入自由列表。

next指针指向此类的派生类对象。因为自由列表存储的就是派生类对象的链表。

在调试过程中operator new可以跟进,但是operator delete却无法跟进。但是测试证明该函数的在使用delete操作符的时候被调用了。具体什么原因不得而知。有谁知道请不吝赐教。!!

具体实现见代码:



分享到:
评论

相关推荐

    C++Primer读书笔记:C++概述.pdf

    C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。

    C++ primer plus第五版学习笔记

    C++ primer plus 第五版的个人学习笔记,仅供大家学习参考。

    C++primer第五版第二章习题答案.pdf

    C++primer第五版第二章习题答案.pdf

    C++ Primer 中文版(第四版)学习笔记~

    以上是C++ Primer第四版学习笔记中涉及的一些核心知识点。这些知识点涵盖了C++编程的基础语法、数据类型、容器、指针、内存管理以及控制结构等多个方面,对于初学者理解和掌握C++语言具有重要意义。希望这些内容能够...

    c++primer 学习笔记

    C++ Primer 是一本经典的C++学习书籍,涵盖了C++的基础知识和高级特性。这篇学习笔记主要涉及了C++编程的一些核心概念,包括程序结构、变量、基本类型、初始化与赋值、可读性、常量与引用、typedef、枚举以及标准库...

    c++primer第五版习题答案(第18章)

    第18章作为《C++ Primer》全书的一部分,通常包含了该书中特定的专题。习题答案是指针对这一章节练习题的具体解答。这些练习题被设计用来加深读者对于章节内容的理解和应用,包含编程实践、问题解析、算法设计等多...

    c++primer第五版习题答案(第17章)

    第17章通常会关注C++中的特殊工具和技术,比如了运行时类型识别(RTTI)、智能指针、标准库中的其他组件以及C++11的特性等。在这一章节中,读者可以通过习题来加深理解并掌握这些高级特性。习题通常会提供一个实际...

    c++primer第五版习题答案(第12章)

    但是,我可以根据《C++ Primer》第五版的内容结构以及一般C++的学习顺序,为读者提供第12章可能涵盖的知识点,并对C++11标准中的相关概念进行介绍。 第12章通常会涉及C++中的输入输出库(I/O库),这是C++编程中必...

    C++ Primer 第四版学习笔记.rar

    这份笔记以清晰、实用的方式阐述了C++ Primer第四版中的知识点,对于想要踏入或深化C++编程的读者来说,是一份非常有价值的参考资料。 C++ Primer是C++编程领域的一本经典教材,第四版更是结合了现代C++的特性进行...

    C++.Primer.第五版.中文版

    4. **类与对象**:C++的核心是面向对象编程,类是定义对象的蓝图,而对象是类的实例。书中详细讲解了如何定义类、构造函数、析构函数、成员函数、访问控制(public、private、protected)以及友元。 5. **模板**:...

    c++primer第五版习题答案(第13章)

    根据提供的文件信息,文件标题为“c++primer第五版习题答案(第13章)”,描述提到这是“c++primer第五版中文版课后答案(第13章)”的PDF格式文件,且这些答案来自书籍的习题集。同时,文件内容是通过OCR技术从纸质...

    《C++ Primer Plus第6版中文版》学习笔记(第十章)

    《C++ Primer Plus第6版中文版》学习笔记(第十章) 红字内容是有疑问或者没把握的。 绿字部分是比较重要,或者经过确认的

    《C++ Primer Plus第6版中文版》学习笔记(第七章)

    《C++ Primer Plus 第六版中文版》的学习笔记聚焦于第七章,主要讲解了C++中的函数,包括函数的分类、无返回值和有返回值的函数、返回值的类型限制、函数原型的重要性和作用,以及ANSI C与C++在函数原型上的差异。...

    C++Primer 第四版课后习题解答(第1~18章完整答案)完整版

    《C++ Primer 第四版》是一本经典的C++学习书籍,涵盖了从基础到高级的各种主题。这份课后习题解答提供了第1至18章的完整答案,旨在帮助读者深入理解书中的概念并提高编程技能。 第一章“快速入门”引导读者进入C++...

    c++ primer第4版学习笔记

    2. **指针与引用**:C++中的指针是内存地址的别名,而引用则是一种安全的指针替代品,一旦引用被初始化,就不能改变引用的对象。理解指针和引用是掌握C++内存管理的关键。 3. **函数**:函数是代码的重用单元,C++...

    C++primer中文第五版

    《C++ Primer 中文第五版》是一本深受程序员喜爱的C++学习书籍,它全面而深入地介绍了C++编程语言的各个方面。这本书以其清晰的解释、丰富的实例和实用的建议,成为了初学者和有经验的程序员提升C++技能的重要参考...

    C++primer笔记

    - **内存分配**:C++支持两种内存分配方式:静态分配和动态分配。 - **静态分配**:编译器在编译时确定内存需求,适用于大小固定的对象。 - **动态分配**:程序运行时根据需求动态分配内存,适用于大小不固定的...

    C++Primer(第4版)-课后习题答案.pdf

    《C++ Primer(第4版)》是一本深入学习C++编程语言的经典教材,其课后习题答案提供了丰富的实践练习和问题解析,帮助读者巩固并深化对C++语法、概念的理解。以下将针对各章节的主要知识点进行详细阐述: 1. 第一章...

    C++primer第四版源代码

    2. **指针与引用**:C++中的指针是内存地址的别名,而引用是另一个变量的别名,它们在内存管理中起到关键作用,尤其是在动态内存分配和函数参数传递时。 3. **类与对象**:C++的核心是面向对象编程,类是定义对象...

Global site tag (gtag.js) - Google Analytics