Open CASCADE学习|Foundation Classes

本手册介绍了如何使用Open CASCADE Technology(OCCT)基础类。它提供了有关基础类的基本文档。

基类提供各种通用服务,如自动动态内存管理(通过句柄操作对象)、集合、异常处理、通过下转换的泛型和插件创建。

基础类包括以下内容:

根类

根类是基本的数据类型和类,所有其他类都是在这些类型和类上构建的。它们提供:

•基本类型,如布尔、字符、整数或实数,

•安全处理动态创建的对象,确保自动删除未引用的对象(请参见Standard_Transient类),

•标准和自定义内存分配器,

•扩展运行时类型信息(RTTI)机制,有助于创建复杂程序,

•例外情况的管理,

•C++流的封装。根类主要在Standard包中实现。

字符串

字符串是处理基于UTF-8和UTF-16编码的动态大小的字符序列的类。字符串也可以通过句柄进行操作,从而被共享。字符串在TCollection包中实现。

集合

集合是处理动态大小的数据聚合的类。集合类是通用的,并且依赖于C++模板。

集合包括广泛的泛型类,如运行时大小的数组、列表、堆栈、队列、集合和哈希映射。集合在TCollection和NCollection包中实现。

标准对象集合

TColStd包提供了TCollection包中泛型类与Standard包中的对象或TCollection包中的字符串的常用实例化。

向量和矩阵

这些类提供了常用的数学算法和涉及向量和矩阵的基本计算(加法、乘法、换位、求逆等)。

基本几何类型

开放式CASCADE技术基本几何类型是基本几何和代数实体的STEP兼容实现。它们提供:

•基本几何形状的描述:

◦点,

◦矢量,

◦线

◦圆形和圆锥形,

◦平面和基本表面,

•通过轴或坐标系在空间或平面中定位这些形状,

•几何变换对这些形状的定义和应用:

◦平移

◦旋转

◦对称性

◦缩放变换

◦组合的转换

•用于代数计算的工具(坐标和矩阵)。

常见数学算法

开放式CASCADE技术常见的数学算法提供了最常用的数学算法的C++实现。其中包括:

•求解一组线性代数方程的算法,

•找到一个或多个自变量函数的最小值的算法,

•寻找一个或一组非线性方程根的算法,

•求平方矩阵的特征值和特征向量的算法。

异常

提供了常用异常类的层次结构,所有这些都基于异常的根类Standard_Failure。异常情况描述了在执行功能过程中可能出现的异常情况。随着异常的出现,程序执行的正常过程将被放弃。针对这种情况执行的操作称为异常处理。

这些是支持日期和时间信息的各种类。

应用程序服务

基础类还包括几个底层服务的实现,这些服务有助于使用Open CASCADE技术创建可定制的、用户友好的应用程序。其中包括:

•单位转换工具,提供处理数量和相关物理单位的统一机制:检查单位兼容性,执行不同单位之间的值转换等(见软件包UnitsAPI);

•表达式的基本解释器,有助于创建自定义脚本工具、表达式的通用定义等(请参阅包ExprIntrp);

•用于处理配置资源文件(请参阅软件包资源)和可自定义消息文件(请参见软件包消息)的工具,使其易于在应用程序中提供多语言支持;

•进度指示和用户中断界面,即使是低级算法也有可能以通用和方便的方式与用户通信。

基础

本章介绍基本服务,如库组织、持久性、数据类型、内存管理、带句柄编程、异常处理、下转换泛型和插件创建。

库组织

本章介绍了一些基本概念,这些概念不仅在基类中使用,而且在整个OCCT库中都使用。

模块和工具包

整个OCCT库被组织在一组模块中。第一个模块提供最基本的服务,并被所有其他模块使用,称为基础类,并在本手册中进行了描述。

每个模块主要由一个或多个工具包组成(尽管它也可以包含可执行文件、资源单元等)。从物理上讲,工具包由一个共享库(例如.so或.dll)表示。工具包由一或多个包构建。

包将许多具有语义链接的类分组在一起。例如,几何图形包将包含点、线和圆类。包还可以包含枚举、异常和包方法(函数)。在实践中,类名的前缀是其包的名称,例如Geom_Circle。包中描述的数据类型可以包括以下数据类型中的一个或多个:

•列举

•对象类

•异常情况

•指向其他对象类的指针在一个包中,两种数据类型不能具有相同的名称。

方法要么是函数,要么是过程。函数返回一个对象,而过程仅通过传递参数进行通信。在这两种情况下,当传输的对象是由句柄操纵的实例时,将传递其标识符。有三类方法:

•对象构造函数创建所描述类的实例。一个类将有一个或多个具有各种不同参数的对象构造函数,或者没有。

•实例方法对拥有它的实例进行操作。

•Class方法不适用于单个实例,只适用于类本身。

面向对象软件开发中的基本软件组件是类。类是数据类型的实现。它定义了它的行为(函数提供的服务)和表示(类的数据结构------存储数据的字段)。

类分为三类:

•普通类。

•抽象类。无法实例化抽象类。拥有这样的类的目的是使给定的行为由类的层次结构共享,并依赖于子类的实现。这是一种保证继承行为的特定基础的方法,该基础是基于特定延迟类的所有类所共有的。

•模板类。模板类提供了一组操作其他数据类型的函数行为。模板类的实例化要求为其参数给定数据类型。

继承

继承的目的是减少开发工作量。继承机制允许声明一个已经包含现有类特征的新类。然后,这个新类可以快速地专门用于手头的任务。这避免了"从头开始"开发每个组件的必要性。例如,已经开发了一个类BankAccount,您可以快速专门化新的类:SavingsAccount、LongTermDepositAccount、MoneyMarketAccount、RevolvingCreditAccount等。。。。

其必然结果是,当两个或多个类从父(或祖先)类继承时,所有这些类都至少保证其父(或始祖)的行为。例如,如果父类BankAccount包含方法Print,该方法告诉它打印自己,那么它的所有子类都保证提供相同的服务。

确保使用继承的一种方法是将层次结构顶部的类声明为抽象类。在这样的类中,方法是不实现的。这迫使用户创建一个新的类来重新定义方法。这是一种保证后代类之间行为的最低限度的方法。

数据类型

面向对象的语言围绕数据类型而不是围绕对这些数据执行的操作来构建系统。在这种情况下,对象是数据类型的实例,其定义决定了如何使用它。每个数据类型都由一个或多个类实现,这些类构成了系统的基本元素。

Open CASCADE Technology中的数据类型分为两类:

•通过句柄(或引用)操作的数据类型

•由值操纵的数据类型

数据类型被实现为一个类。该类不仅定义了其数据表示和实例上可用的方法,而且还建议了如何操作实例。

•由值操纵的类型的变量包含实例本身。

•句柄操作的类型的变量包含对实例的引用。由值操纵的类型的第一个示例是预定义的基元类型:布尔、字符、整数、实数等。

由句柄操作的类型的变量,如果未附加到对象,则称为null。为了引用一个对象,我们用它的一个构造函数实例化这个类。例如,在C++中:

Handle(MyClass) anObject = new MyClass();

在Open CASCADE Technology中,Handles是特定的类,用于通过引用安全地操作动态内存中分配的对象,提供引用计数机制,并在对象未被引用时自动销毁对象。

基本体类型

基元类型是在语言中预定义的,并由值进行操作。

•Standard_Boolean用于表示逻辑数据。它可能只有两个值:Standard_True和Standard_False。

•Standard_Character指定任何ASCII字符。

•Standard_ExtCharacter是一个扩展字符。

•Standard_Integer是一个整数。

•Standard_Real表示实数(即具有整数和小数部分的实数,其中任何一个都可能为空)。

•Standard_ShortReal是一个具有较小值和内存大小选择的实数。

•Standard_CString用于文字常量。

•Standard_ExtString是一个扩展字符串。

•Standard_Address表示大小不确定的字节地址。

标准包中描述了每种类型提供的服务。下表显示了C++基本类型和OCCT基元类型之间存在的等价性。

表1:C++类型和OCCT基元类型之间的等价性

|---------------|--------------------|
| C++ Types | OCCT Types |
| int | Standard_Integer |
| double | Standard_Real |
| float | Standard_ShortReal |
| bool | Standard_Boolean |
| char | Standard_Character |
| char16_t | Standard_Utf16Char |
| char* | Standard_CString |
| void* | Standard_Address |
| char16_t* | Standard_ExtString |

•带星号的类型是指针。

上面列出的课程提醒:

•Standard_Integer:表示产生负值、正值或零值的32位整数的基本类型。Integer被实现为C++int基本类型的typedef。因此,代数运算+,-,*,/以及排序和等价关系<,<=,==,!=,>=,>定义在上面。

•Standard_Real:表示具有有限精度和有限大小的实数的基本类型。Real被实现为C++双精度(double precision)基本类型的typedef。因此,代数运算+,-,*,/,一元-以及排序和等价关系<,<=,==,!=,>=,>是在reals上定义的。

•Standard_ShortReal:表示具有有限精度和有限大小的实数的基本类型。ShortReal被实现为C++浮点(单精度)基本类型的typedef。因此,代数运算+,-,*,/,一元-以及排序和等价关系<,<=,==,!=,>=,>是在reals上定义的。

•Standard_Boolean:表示逻辑表达式的基本类型。它有两个值:false和true。Boolean被实现为C++布尔基本类型的typedef。因此,代数运算和,或,xor和not以及等价关系==和!=在布尔上定义。

•Standard_Character:表示UTF-8字符集的基本类型。字符被实现为C++字符基本类型的typedef。因此,排序和等价关系<,<=,==,!=,>=,>使用ASCII图表的顺序在字符上定义(例如:A B)。

•Standard_ExtCharacter:表示UTF-16字符集的基本类型。它是一种16位字符类型。ExtCharacter被实现为C++char16_t基本类型的typedef。因此,排序和等价关系<,<=,==,!=,>=,>使用UNICODE图表的顺序在扩展字符上定义(例如:A B)。

•Standard_CString:表示字符串文字的基本类型。字符串文字是用双引号括起来的UTF-8(8位)代码点序列。CString被实现为C++字符基本类型的typedef。

•Standard_Address:表示通用指针的基本类型。地址被实现为C++void基本类型的typedef。

•Standard_ExtString:将字符串文字表示为Unicode(16位)字符序列的基本类型。ExtString被实现为C++char16_t基本类型的typedef。

由值操纵的类型

有三类类型由值操纵:

•基本类型

•枚举类型

•由非从Standard_Transient继承的类定义的类型,无论是否直接继承。由值操纵的类型的行为方式比由句柄操纵的类型更直接,因此可以更快地执行操作,但它们不能独立存储在文件中。

通过引用(句柄)操作的类型

这些类型是由继承自Standard_Transient类的类定义的。

什么时候需要使用句柄?

设计对象时,很难选择如何操作该对象:按值或按句柄。以下想法可以帮助你下定决心:

•如果您的对象在应用程序中的使用寿命可能很长,并且您希望对其进行多次引用,则最好使用句柄来操作此对象。对象的内存将在堆上分配。指向该内存的句柄是一个可以在参数中快速传递的轻对象。这样就避免了复制大对象的代价。

•例如,如果您的对象的生存期有限,在单个算法中使用,则最好按值操作此对象,而不考虑其大小,因为此对象是在堆栈上分配的,内存的分配和取消分配非常迅速,这避免了堆上分配所引起的对新对象和删除对象的隐式调用。

•最后,如果一个对象在过程中只创建一次,但在应用程序的整个生命周期中都存在,那么最好的选择可能是由句柄操作的类或声明为全局变量的值。

使用句柄编程

句柄定义

句柄是智能指针的OCCT实现。多个句柄可以引用同一个对象。此外,单个句柄可以引用多个对象,但一次只能引用一个。要访问它所引用的对象,必须像使用C++指针一样取消对句柄的引用。

类组织

类Standard_Transient是OCCT类的大层次结构的根,据说这些类可由句柄操作。它提供了一个引用计数器字段,由其所有子类继承,关联的Handle()类使用该字段来跟踪指向该对象实例的多个句柄。

从Transient(直接或间接)派生的类的对象,通常使用运算符new在动态内存中分配,并由句柄进行操作。句柄被定义为模板类opencascade::Handle<>。Open CASCADE Technology提供了预处理器宏Handle(),它在OCCT代码中历来用于命名句柄:

Handle(Geom_Line) aLine; // "Handle(Geom_Line)" is expanded to "opencascade::handle<Geom_Line>"

此外,对于大多数OCCT类,为句柄定义了额外的typedef,作为以handle_为前缀的类的名称。例如,上述示例也可以被编码为:

Handle_Geom_Line aLine; // "Handle_Geom_Line" is typedef to "opencascade::handle<Geom_Line>"

使用句柄

句柄的特征在于它引用的对象。

在对瞬态对象执行任何操作之前,必须声明句柄。例如,如果"点"和"线"是Geom包中的两个瞬态类,则应编写:

Handle(Geom_Point) p1, p2;

声明句柄会创建一个不引用任何对象的null句柄。句柄可以通过其方法IsNull()检查为null。若要使句柄无效,请使用方法nullify()。

若要初始化句柄,应创建一个新对象,或者可以将另一个句柄的值分配给它,条件是它们的类型是兼容的。

请注意,句柄应仅用于对象共享。对于所有本地操作,建议使用由值操纵的类。

类型管理

Open CASCADE技术提供了一种以通用方式描述数据类型层次结构的方法,并有可能在运行时检查给定对象的确切类型(类似于C++RTTI)。

要启用此功能,类声明应该包括OCCT RTTI的声明。Header Standard_Type.hxx提供了两种预处理器宏变体,有助于实现这一点:

•内联变体,通过一行代码声明和定义RTTI方法:

  • #include <Geom_Surface.hxx>

class Appli_ExtSurface : public Geom_Surface

{

. . .

public:

DEFINE_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)

};

•越界变量,它在声明中使用一个宏(通常在头文件中),在实现中使用另一个(在C++源代码中):

在Appli_ExtSurface.hxx文件中:

  • #include <Geom_Surface.hxx>

class Appli_ExtSurface : public Geom_Surface

{

. . .

public:

DEFINE_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)

};

在Appli_ExtSurface.cxx文件中:

  • #include <Appli_ExtSurface.hxx>

IMPLEMENT_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)

这些宏定义了DynamicType()方法,该方法向描述类的类Standard_type的单例实例返回类型描述符句柄。类型描述符存储类的名称及其父类的描述符。

请注意,虽然内联版本更容易使用,但对于广泛使用的类,由于内联方法的多次实例化,该方法可能会导致依赖库的二进制代码膨胀。

要获取给定类类型的类型描述符,请使用宏STANDARD_type(),并将类名作为参数。

用法示例:

if (aCurve->IsKind(STANDARD_TYPE(Geom_Line))) // equivalent to "if (dynamic_cast<Geom_Line>(aCurve.get()) != 0)"

{

...

}

类型符合性

句柄声明中使用的类型是对象的静态类型,即编译器看到的类型。句柄可以引用从其静态类型的子类实例化的对象。因此,对象的动态类型(也称为对象的实际类型)可以是出现在句柄声明中的类型的后代,通过句柄声明可以对其进行操作。

考虑类Geom_CartesianPoint,它是Geom_Point的一个子类;类型一致性的规则可以说明如下:

Handle(Geom_Point) aPnt1;

Handle(Geom_CartesianPoint) aPnt2;

aPnt2 = new Geom_CartesianPoint();

aPnt1 = aPnt2; // OK, the types are compatible

编译器将aPnt1视为Geom_Point的句柄,尽管aPnt1引用的实际对象属于Geom_CartesianPoint类型。

显式类型转换

根据类型一致性规则,总是可以通过连续分配句柄来向上提升类层次结构。另一方面,分配并不授权您向下层次结构。因此,需要对句柄进行显式类型转换。

如果引用对象的实际类型是用于强制转换句柄的对象的后代,则句柄可以显式转换为其子类型之一。如果不是这种情况,则句柄无效(显式类型转换有时被称为"安全强制转换")。考虑下面的例子。

Handle(Geom_Point) aPnt1;

Handle(Geom_CartesianPoint) aPnt2, aPnt3;

aPnt2 = new Geom_CartesianPoint();

aPnt1 = aPnt2; // OK, standard assignment

aPnt3 = Handle(Geom_CartesianPoint)::DownCast (aPnt1);

// OK, the actual type of aPnt1 is Geom_CartesianPoint, although the static type of the handle is Geom_Point

如果转换与被引用对象的实际类型不兼容,则"强制转换"的句柄将变为null(并且不会引发异常)。因此,如果您需要在句柄所见类型(静态类型)的子类中定义的可靠服务,请如下所示:

void MyFunction (const Handle(A) & a)

{

Handle(B) b = Handle(B)::DownCast(a);

if (! b.IsNull()) {

// we can use "b" if class B inherits from A

}

else {

// the types are incompatible

}

}

下行广播特别用于不同类型的对象的集合;但是,这些对象应该继承自同一个根类。

例如,使用一系列瞬态对象TColStd_SequeOfTransient和两个都继承自Standard_transient的类a和B,可以获得以下语法:

Handle(A) a;

Handle(B) b;

Handle(Standard_Transient) t;

TColStd_SequenceOfTransient aSeq;

a = new A();

aSeq.Append (a);

b = new B();

aSeq.Append (b);

t = aSeq.Value (1);

// here, you cannot write:

// a = t; // ERROR !

// so you downcast:

a = Handle (A)::Downcast (t)

if (!a.IsNull())

{

// types are compatible, you can use a

}

else

{

// the types are incompatible

}

使用控制柄创建对象

要创建一个由句柄操作的对象,请声明句柄并使用标准C++new运算符对其进行初始化,然后立即调用构造函数。构造函数可以是在实例化对象的类的源中指定的任何构造函数。

Handle(Geom_CartesianPoint) aPnt;

aPnt = new Geom_CartesianPoint (0, 0, 0);

与指针不同,delete运算符不适用于句柄;引用的对象在不再使用时会自动销毁。

调用方法

一旦有了对象的句柄,就可以像C++中的指针一样使用它。要调用作用于被引用对象的方法,可以通过标准箭头运算符转换此方法,或者在可用时通过函数调用语法转换此方法。

为了测试或修改句柄的状态,该方法由点运算符转换。以下示例说明了如何访问(可选初始化的)点对象的坐标:

Handle(Geom_CartesianPoint) aCentre;

Standard_Real x, y, z;

if (aCentre.IsNull())

{

aCentre = new PGeom_CartesianPoint (0, 0, 0);

}

aCentre->Coord (x, y, z);

以下示例说明了如何访问笛卡尔点的类型对象:

Handle(Standard_Transient) aPnt = new Geom_CartesianPoint (0., 0., 0.);

if (aPnt->DynamicType() == STANDARD_TYPE(Geom_CartesianPoint))

{

std::cout << "Type check OK\n";

}

else

{

std::cout << "Type check FAILED\n";

}

如果通过Null句柄访问对象的字段或方法,将引发Standard_NullObject异常。

调用类方法

类方法的调用类似于静态C++函数,即它是由它所属的类的名称调用的,后面跟着"::"运算符和方法的名称。

例如,我们可以找到贝塞尔曲线的最大阶数:

Standard_Integer aDegree = Geom_BezierCurve::MaxDegree();

处理解除分配

删除对象之前,必须确保不再引用该对象。为了减少与对象生命管理相关的编程负载,Open CASCADE Technology中的删除功能由句柄操作的类的引用计数器来保护。当对象不再被引用时,控制柄会自动删除该对象。通常,您永远不会在Standard_Transient的子类实例上显式调用delete运算符。

当创建同一对象的新句柄时,引用计数器将递增。当句柄被销毁、无效或重新分配给另一个对象时,该计数器会递减。当引用计数器变为0时,句柄会自动删除该对象。

分配的原则可以在下面的例子中看到。

...

{

Handle(TColStd_HSequenceOfInteger) H1 = new TColStd_HSequenceOfInteger();

// H1 has one reference and corresponds to 48 bytes of memory

{

Handle(TColStd_HSequenceOfInteger) H2;

H2 = H1; // H1 has two references

if (argc == 3)

{

Handle(TColStd_HSequenceOfInteger) H3;

H3 = H1;

// Here, H1 has three references

...

}

// Here, H1 has two references

}

// Here, H1 has 1 reference

}

// Here, H1 has no reference and the referred TColStd_HSequenceOfInteger object is deleted.

You can easily cast a reference to the handle object to *void**by defining the following:

void* aPointer;

Handle(Some_Class) aHandle;

// Here only a pointer will be copied

aPointer = &aHandle;

// Here the Handle object will be copied

aHandle = *(Handle(Some_Class)*)aPointer;

周期

如果两个或多个对象通过句柄(存储为字段)相互引用,则会出现循环。在这种情况下,自动销毁将不起作用。

例如,考虑一个图,其对象(基元)必须知道它们所属的图对象,即基元必须具有完整图对象的引用。如果基本体和图形都由句柄操纵,并且它们通过将句柄保持为字段来相互引用,则会出现循环。

当图形对象的最后一个句柄在应用程序中被破坏时,它不会被删除,因为它的句柄存储在它自己的数据结构(基元)中。

有两种方法可以避免这种情况:

•将C++指针用于一种引用,例如从基元到图

•当图形对象需要销毁时,将一组句柄(例如,基元中图形的句柄)置空

内存管理

在工作会话中,几何建模应用程序创建和删除动态内存(堆)中分配的大量C++对象。在这种情况下,用于分配和释放内存的标准函数的性能可能不够。因此,Open CASCADE Technology采用了在Standard包中实现的专用内存管理器。

内存管理器基于以下原则:

•小型存储器阵列被分组为集群,然后被回收(集群永远不会释放到系统),

•大型阵列通过系统的标准功能进行分配和取消分配(阵列不再使用时会释放给系统)。

一般来说,建议通过有效块来分配内存。通过这种方式,用户可以处理连续数据块,这有助于内存页面管理器的处理。

内存管理器的使用

要使用Open CASCADE Technology内存管理器在C代码中分配内存,只需使用方法Standard::allocate()代替malloc(),使用方法Standard::Free()代替Free()。此外,还提供了方法Standard::Reallocate()来替换C函数realloc()。

在C++中,可以定义类的运算符new()和delete(),以便使用Standard::allocate()分配内存,并使用Standard:()释放内存。在这种情况下,该类的所有对象和所有继承的类都将使用OCCT内存管理器进行分配。

标头STANDARD_DefineAlloc.hxx提供的预处理器宏DEFINE_STANDARD_ALLOC以这种方式定义new()和delete()。它用于所有OCCT类(除了少数例外),因此这些类是使用OCCT内存管理器分配的。由于运算符new()和delete()是继承的,因此对于从OCCT类派生的任何类也是如此,例如,对于从Standard_Transient派生的所有类也是如此。

请注意,为继承Standard_Transient的类重新定义new()和delete()函数是可能的(尽管不建议使用,除非确实不可避免)。如果这样做了,还应该重新定义方法Delete(),以便将运算符Delete应用于该指针。这将确保调用适当的delete()函数,即使对象是由基类的句柄操作的。

如何配置内存管理器

OCCT内存管理器可以被配置为将不同的优化技术应用于不同的内存块(取决于它们的大小),或者甚至避免任何优化并直接使用C函数malloc()和free()。配置由以下环境变量的数值定义:

•MMGT_OPT:◦如果设置为0(默认值),则直接在C内存堆中分配每个内存块(通过malloc()和free()函数)。在这种情况下,将忽略除MMGT_CLEAR之外的所有其他选项;

◦如果设置为1,则存储器管理器执行如下所述的优化;

◦如果设置为2,则使用"英特尔&#xAE;TBB优化内存管理器"。

•MMGT_CLEAR:如果设置为1(默认值),则每个分配的内存块都被清零;如果设置为0,则按原样返回内存块。

•MMGT_CELLSIZE:定义在大型内存池中分配的块的最大大小。默认值为200。

•MMGT_NBPAGES:定义为页面中的小块分配的内存块的大小(取决于操作系统)。默认值为1000。

•MMGT_THRESHOLD:定义在内部回收而不是返回堆的块的最大大小。默认值为40000。

•MMGT_MMAP:当设置为1(默认)时,使用操作系统的内存映射功能分配大内存块;如果设置为0,它们将由malloc()在C堆中分配。

优化技术

当MMGT_OPT设置为1时,将使用以下优化技术:

•大小小于MMGT_CELLSIZE的小块不会单独分配。相反,分配了大量内存池(每个池的大小为MMGT_NBPAGES页)。每个新的内存块都被安排在当前池的一个备用位置。当当前内存池被完全占用时,将分配下一个内存池,依此类推。

在当前版本中,内存池永远不会返回到系统(直到进程结束)。但是,通过方法Standard::Free()释放的内存块会被记住在空闲列表中,然后在分配下一个相同大小的块时重用(回收)。

•中等大小的块,其大小大于MMGT_CELLSIZE但小于MMGT_THRESHOLD,直接在C堆中分配(使用malloc()和free())。当这些块通过方法Standard::Free()释放时,它们就像小块一样被回收。

但是,与小块不同,空闲列表中包含的回收介质块(即由程序释放但由内存管理器持有)可以通过方法Standard::Purge()返回到堆中。

•大小大于MMGT_THRESHOLD的大块,包括用于小块的内存池,根据MMGT_MMAP的值进行分配:如果为0,则这些块在C堆中分配;否则,它们是使用管理内存映射文件的操作系统特定功能来分配的。当调用Standard::Free()时,大块会立即返回到系统。

优点和缺点

OCCT内存管理器的主要优点是它可以回收中小型块,当应用程序不断分配和释放多个大小相似的内存块时,它可以使应用程序更快地工作。在实际情况下,应用程序性能的实际增益可能高达50%。

相关的缺点是在程序执行期间回收的内存不会返回到操作系统。这可能会导致相当大的内存消耗,甚至被误解为内存泄漏。为了最大限度地减少这种影响,有必要在完成内存密集型操作后调用方法Standard::Purge。

OCCT内存管理器引起的管理费用包括:

•每个分配的内存块的大小四舍五入至8字节(当MMGT_OPT为0(默认值)时,四舍五舍五入由CRT定义;32位平台的典型值为4字节)

•只有当MMGT_OPT为1时,才会在每个内存块的开头分配额外的4个字节(或64位平台上的8个字节),以保持其大小(或在空闲列表中回收时下一个空闲内存块的地址)。

请注意,这些开销可能大于或小于C堆内存管理器引起的开销,因此在优化模式或标准模式下,总体内存消耗可能更大,具体取决于环境。

一般来说,建议通过有效块来分配内存。通过这种方式,您可以处理连续数据块,并且便于内存页面管理器进行处理。

OCCT内存管理器使用互斥锁来锁定对空闲列表的访问,因此在不同线程经常同时调用内存管理器的情况下,它的性能可能低于非优化模式。原因是malloc()和free()的现代实现使用了几个分配领域,从而避免了等待互斥释放的延迟,这在这种情况下是可能的。

相关推荐
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__4 小时前
Web APIs学习 (操作DOM BOM)
学习
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos6 小时前
c++---------数据类型
java·jvm·c++
数据的世界016 小时前
.NET开发人员学习书籍推荐
学习·.net
四口鲸鱼爱吃盐6 小时前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
十年一梦实验室6 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵