OCCT基础类库介绍: Foundation Classes - Basics
Basics
This chapter deals with basic services such as library organization, persistence, data types, memory management, programming with handles, exception handling, genericity by downcasting and plug-in creation.
基础
本章介绍库组织、持久性、数据类型、内存管理、句柄编程、异常处理,以及通过向下转型和插件创建实现的泛型特性等基础服务。
Library organization
This chapter introduces some basic concepts, which are used not only in Foundation Classes, but throughout the whole OCCT library.
库组织
本章介绍一些基本概念,这些概念不仅用于基础类库,还适用于整个 OCCT 库。
Modules and toolkits
The whole OCCT library is organized in a set of modules. The first module, providing most basic services and used by all other modules, is called Foundation Classes and described by this manual.
Every module consists primarily of one or several toolkits (though it can also contain executables, resource units etc.). Physically a toolkit is represented by a shared library (e.g. .so or .dll). The toolkit is built from one or several packages.
模块和工具包
整个 OCCT 库由一组模块组成。第一个模块提供最基本的服务,并被所有其他模块使用,称为基础类库,本手册将对此进行描述。
每个模块主要由一个或多个工具包组成(尽管也可能包含可执行文件、资源单元等)。从物理上讲,工具包由共享库(如 .so 或 .dll)表示,由一个或多个包构建而成。
Packages
A package groups together a number of classes which have semantic links. For example, a geometry package would contain Point, Line, and Circle classes. A package can also contain enumerations, exceptions and package methods (functions). In practice, a class name is prefixed with the name of its package e.g. Geom_Circle
. Data types described in a package may include one or more of the following data types:
- Enumerations
- Object classes
- Exceptions
- Pointers to other object classes
Inside a package, two data types cannot bear the same name.
Methods are either functions or procedures. Functions return an object, whereas procedures only communicate by passing arguments. In both cases, when the transmitted object is an instance manipulated by a handle, its identifier is passed. There are three categories of methods:
- Object constructor: Creates an instance of the described class. A class will have one or more object constructors with various different arguments or none.
- Instance method: Operates on the instance which owns it.
- Class method: Does not work on individual instances, only on the class itself.
包
包将具有语义关联的多个类组合在一起。例如,几何包可能包含点、线和圆类。包还可以包含枚举、异常和包方法(函数)。实际上,类名会以其包名作为前缀,例如 Geom_Circle
。包中描述的数据类型可能包括以下一种或多种:
- 枚举
- 对象类
- 异常
- 指向其他对象类的指针
在一个包内,两个数据类型不能具有相同的名称。
方法可以是函数或过程。函数返回一个对象,而过程仅通过传递参数进行通信。在这两种情况下,当传输的对象是通过句柄操作的实例时,会传递其标识符。方法分为三类:
- 对象构造函数:创建所述类的实例。一个类可以有一个或多个带不同参数的对象构造函数,也可以没有。
- 实例方法:对所属的实例进行操作。
- 类方法:不针对单个实例,仅对类本身进行操作。
Classes
The fundamental software component in object-oriented software development is the class. A class is the implementation of a data type. It defines its behavior (the services offered by its functions) and its representation (the data structure of the class – the fields, which store its data).
Classes fall into three categories:
- Ordinary classes.
- Abstract classes. An abstract class cannot be instantiated. The purpose of having such classes is to have a given behavior shared by a hierarchy of classes and dependent on the implementation of the descendants. This is a way of guaranteeing a certain base of inherited behavior common to all the classes based on a particular deferred class.
- Template classes. A template class offers a set of functional behaviors to manipulate other data types. Instantiation of a template class requires that a data type is given for its argument(s).
类
面向对象软件开发中的基本软件组件是类。类是数据类型的实现,它定义其行为(其函数提供的服务)和表示(类的数据结构——存储数据的字段)。
类分为三类:
- 普通类。
- 抽象类:抽象类不能实例化,其目的是让类层次结构共享特定行为,并依赖于子类的实现。这是一种确保所有基于特定延迟类的类共享某些继承行为基础的方式。
- 模板类:模板类提供一组操作其他数据类型的功能行为。模板类的实例化需要为其参数指定数据类型。
Inheritance
The purpose of inheritance is to reduce the development workload. The inheritance mechanism allows a new class to be declared already containing the characteristics of an existing class. This new class can then be rapidly specialized for the task in hand. This avoids the necessity of developing each component “from scratch”. For example, having already developed a class BankAccount you could quickly specialize new classes: SavingsAccount, LongTermDepositAccount, MoneyMarketAccount, RevolvingCreditAccount, etc…
The corollary of this is that when two or more classes inherit from a parent (or ancestor) class, all these classes guarantee as a minimum the behavior of their parent (or ancestor). For example, if the parent class BankAccount contains the method Print which tells it to print itself out, then all its descendant classes guarantee to offer the same service.
One way of ensuring the use of inheritance is to declare classes at the top of a hierarchy as being abstract. In such classes, the methods are not implemented. This forces the user to create a new class which redefines the methods. This is a way of guaranteeing a certain minimum of behavior among descendant classes.
继承
继承的目的是减少开发工作量。继承机制允许声明一个新类,该类已包含现有类的特征,然后可以快速针对当前任务对新类进行专门化,避免了“从零开始”开发每个组件的必要性。例如,已经开发了 BankAccount
类后,可以快速专门化新类:SavingsAccount
、LongTermDepositAccount
、MoneyMarketAccount
、RevolvingCreditAccount
等。
其推论是,当两个或多个类继承自一个父类(或祖先类)时,所有这些类至少保证其父类(或祖先类)的行为。例如,如果父类 BankAccount
包含 Print
方法(用于打印自身),则其所有子类都保证提供相同的服务。
确保使用继承的一种方法是将层次结构顶部的类声明为抽象类,这些类中的方法不实现,从而强制用户创建重新定义这些方法的新类,这是一种确保子类之间具有一定最小行为的方式。
Data Types
An object-oriented language structures a system around data types rather than around the actions carried out on this data. In this context, an object is an instance of a data type and its definition determines how it can be used. Each data type is implemented by one or more classes, which make up the basic elements of the system.
The data types in Open CASCADE Technology fall into two categories:
- Data types manipulated by handle (or reference)
- Data types manipulated by value
A data type is implemented as a class. The class not only defines its data representation and the methods available on instances, but it also suggests how the instance will be manipulated.
A variable of a type manipulated by value contains the instance itself.
A variable of a type manipulated by handle contains a reference to the instance. The first examples of types manipulated by values are the predefined primitive types: Boolean, Character, Integer, Real, etc.
A variable of a type manipulated by handle which is not attached to an object is said to be null. To reference an object, we instantiate the class with one of its constructors. For example, in C++:
Handle(MyClass) anObject = new MyClass();
In Open CASCADE Technology, the Handles are specific classes that are used to safely manipulate objects allocated in the dynamic memory by reference, providing reference counting mechanism and automatic destruction of the object when it is not referenced.
数据类型
面向对象语言围绕数据类型而非对数据执行的操作来构建系统。在这种情况下,对象是数据类型的实例,其定义决定了如何使用它。每个数据类型由一个或多个类实现,这些类构成系统的基本元素。
Open CASCADE 技术中的数据类型分为两类:
- 通过句柄(或引用)操作的数据类型
- 通过值操作的数据类型
数据类型作为类实现。类不仅定义其数据表示和实例可用的方法,还暗示实例将如何被操作:
- 通过值操作的类型的变量包含实例本身。
- 通过句柄操作的类型的变量包含对实例的引用。通过值操作的类型的第一个示例是预定义的基本类型:
Boolean
、Character
、Integer
、Real
等。 - 未附加到对象的句柄操作类型的变量称为空变量。要引用对象,需使用其构造函数之一实例化类。例如,在 C++ 中:
Handle(MyClass) anObject = new MyClass();
在 Open CASCADE 技术中,句柄是特定的类,用于通过引用安全地操作动态内存中分配的对象,提供引用计数机制,并在对象不再被引用时自动销毁对象。
Primitive Types
The primitive types are predefined in the language and they are manipulated by value.
- Standard_Boolean is used to represent logical data. It may have only two values: Standard_True and Standard_False.
- Standard_Character designates any ASCII character.
- Standard_ExtCharacter is an extended character.
- Standard_Integer is a whole number.
- Standard_Real denotes a real number (i.e. one with whole and a fractional part, either of which may be null).
- Standard_ShortReal is a real with a smaller choice of values and memory size.
- Standard_CString is used for literal constants.
- Standard_ExtString is an extended string.
- Standard_Address represents a byte address of undetermined size.
The services offered by each of these types are described in the Standard Package. The table below presents the equivalence existing between C++ fundamental types and OCCT primitive types.
Table 1: Equivalence between C++ Types and OCCT Primitive Types
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 |
The types with asterisk are pointers.
Reminder of the classes listed above:
- Standard_Integer: fundamental type representing 32-bit integers yielding negative, positive or null values. Integer is implemented as a typedef of the C++ int fundamental type. As such, the algebraic operations +, -, *, / as well as the ordering and equivalence relations <, <=, ==, !=, >=, > are defined on it.
- Standard_Real: fundamental type representing real numbers with finite precision and finite size. Real is implemented as a typedef of the C++ double (double precision) fundamental type. As such, the algebraic operations +, -, *, /, unary- and the ordering and equivalence relations <, <=, ==, !=, >=, > are defined on reals.
- Standard_ShortReal: fundamental type representing real numbers with finite precision and finite size. ShortReal is implemented as a typedef of the C++ float (single precision) fundamental type. As such, the algebraic operations +, -, *, /, unary- and the ordering and equivalence relations <, <=, ==, !=, >=, > are defined on reals.
- Standard_Boolean: fundamental type representing logical expressions. It has two values: false and true. Boolean is implemented as a typedef of the C++ bool fundamental type. As such, the algebraic operations and, or, xor and not as well as equivalence relations == and != are defined on Booleans.
- Standard_Character: fundamental type representing the UTF-8 character set. Character is implemented as a typedef of the C++ char fundamental type. As such, the ordering and equivalence relations <, <=, ==, !=, >=, > are defined on characters using the order of the ASCII chart (ex: A B).
- Standard_ExtCharacter: fundamental type representing the UTF-16 character set. It is a 16-bit character type. ExtCharacter is implemented as a typedef of the C++ char16_t fundamental type. As such, the ordering and equivalence relations <, <=, ==, !=, >=, > are defined on extended characters using the order of the UNICODE chart (ex: A B).
- Standard_CString: fundamental type representing string literals. A string literal is a sequence of UTF-8 (8 bits) code points enclosed in double quotes. CString is implemented as a typedef of the C++ char fundamental type.
- Standard_Address: fundamental type representing a generic pointer. Address is implemented as a typedef of the C++ void fundamental type.
- Standard_ExtString: fundamental type representing string literals as sequences of Unicode (16 bits) characters. ExtString is implemented as a typedef of the C++ char16_t fundamental type.
基本类型
基本类型在语言中预定义,并通过值操作:
Standard_Boolean
:表示逻辑数据,只有两个值:Standard_True
和Standard_False
。Standard_Character
:表示任何 ASCII 字符。Standard_ExtCharacter
:扩展字符。Standard_Integer
:整数。Standard_Real
:实数(即包含整数和小数部分,两者可能为零)。Standard_ShortReal
:值选择范围和内存大小较小的实数。Standard_CString
:用于文字常量。Standard_ExtString
:扩展字符串。Standard_Address
:表示大小未确定的字节地址。
每个类型提供的服务在 Standard 包中描述。下表列出了 C++ 基本类型与 OCCT 基本类型的对应关系:
C++ 类型 | OCCT 类型 |
---|---|
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 位整数(负值、正值或零)的基本类型,实现为 C++ int 基本类型的 typedef,定义了代数运算(+、-、*、/)以及排序和等价关系(<、<=、==、!=、>=、>)。
- Standard_Real:表示有限精度和有限大小实数的基本类型,实现为 C++ double(双精度)基本类型的 typedef,定义了代数运算(+、-、*、/、一元-)以及排序和等价关系。
- Standard_ShortReal:表示有限精度和有限大小实数的基本类型,实现为 C++ float(单精度)基本类型的 typedef,定义了代数运算和排序关系。
- Standard_Boolean:表示逻辑表达式的基本类型,有两个值:false 和 true,实现为 C++ bool 基本类型的 typedef,定义了与、或、异或、非运算以及等价关系(== 和 !=)。
- Standard_Character:表示 UTF-8 字符集的基本类型,实现为 C++ char 基本类型的 typedef,使用 ASCII 表顺序定义字符的排序和等价关系。
- Standard_ExtCharacter:表示 UTF-16 字符集的基本类型,是 16 位字符类型,实现为 C++ char16_t 基本类型的 typedef,使用 Unicode 表顺序定义扩展字符的排序和等价关系。
- Standard_CString:表示字符串文字的基本类型,字符串文字是用双引号括起来的 UTF-8(8 位)代码点序列,实现为 C++ char 基本类型的 typedef。
- Standard_Address:表示通用指针的基本类型,实现为 C++ void 基本类型的 typedef。
- Standard_ExtString:表示作为 Unicode(16 位)字符序列的字符串文字的基本类型,实现为 C++ char16_t 基本类型的 typedef。
Types manipulated by value
There are three categories of types which are manipulated by value:
- Primitive types
- Enumerated types
- Types defined by classes not inheriting from Standard_Transient, whether directly or not. Types which are manipulated by value behave in a more direct fashion than those manipulated by handle and thus can be expected to perform operations faster, but they cannot be stored independently in a file.
通过值操作的类型
通过值操作的类型分为三类:
- 基本类型
- 枚举类型
- 由不直接或间接继承自
Standard_Transient
的类定义的类型
通过值操作的类型比通过句柄操作的类型行为更直接,因此操作速度更快,但不能独立存储在文件中。
Types manipulated by reference (handle)
These are types defined by classes inheriting from the Standard_Transient class.
通过引用(句柄)操作的类型
这些是由继承自 Standard_Transient
类的类定义的类型。
When is it necessary to use a handle?
When you design an object, it can be difficult to choose how to manipulate that object: by value or by handle. The following ideas can help you to make up your mind:
- If your object may have a long lifetime within the application and you want to make multiple references to it, it would be preferable to manipulate this object with a handle. The memory for the object will be allocated on the heap. The handle which points to that memory is a light object which can be rapidly passed in argument. This avoids the penalty of copying a large object.
- If your object will have a limited lifetime, for example, used within a single algorithm, it would be preferable to manipulate this object by value, non-regarding its size, because this object is allocated on the stack and the allocation and de-allocation of memory is extremely rapid, which avoids the implicit calls to new and delete occasioned by allocation on the heap.
- Finally, if an object will be created only once during, but will exist throughout the lifetime of the application, the best choice may be a class manipulated by handle or a value declared as a global variable.
何时需要使用句柄?
设计对象时,选择通过值还是句柄操作可能很困难,以下思路可帮助决策:
- 如果对象在应用程序中生命周期较长,且需要多次引用,最好使用句柄操作,对象的内存将在堆上分配,指向该内存的句柄是轻量级对象,可以快速作为参数传递,避免复制大型对象的开销。
- 如果对象生命周期有限(例如在单个算法中使用),最好通过值操作,无论其大小如何,因为该对象在栈上分配,内存分配和释放极快,避免堆分配时隐式调用 new 和 delete。
- 如果对象在应用程序生命周期中仅创建一次并始终存在,最佳选择可能是通过句柄操作的类或声明为全局变量的值。
Programming with Handles
句柄编程
Handle Definition
A handle is OCCT implementation of a smart pointer. Several handles can reference the same object. Also, a single handle may reference several objects, but only one at a time. To have access to the object it refers to, the handle must be de-referenced just as with a C++ pointer.
句柄定义
句柄是 OCCT 对智能指针的实现,多个句柄可以引用同一个对象,单个句柄也可以引用多个对象(但一次只能引用一个)。要访问所引用的对象,必须像 C++ 指针一样对句柄进行解引用。
Organization of Classes
Class Standard_Transient
is a root of a big hierarchy of OCCT classes that are said to be operable by handles. It provides a reference counter field, inherited by all its descendant classes, that is used by associated Handle()
classes to track a number of handles pointing to this instance of the object.
Objects of classes derived (directly or indirectly) from Transient
, are normally allocated in dynamic memory using operator new, and manipulated by handle. Handle is defined as template class opencascade::handle<>
. Open CASCADE Technology provides preprocessor macro Handle()
that is historically used throughout OCCT code to name a handle:
Handle(Geom_Line) aLine; // "Handle(Geom_Line)" is expanded to "opencascade::handle<Geom_Line>"
In addition, for most OCCT classes additional typedef is defined for a handle, as the name of a class prefixed by Handle_
. For instance, the above example can be also coded as:
Handle_Geom_Line aLine; // "Handle_Geom_Line" is typedef to "opencascade::handle<Geom_Line>"
类的组织
Standard_Transient
类是 OCCT 类的大型层次结构的根,这些类称为可通过句柄操作的类。它提供一个引用计数字段,由其所有子类继承,相关的 Handle()
类使用该字段跟踪指向该对象实例的句柄数量。
从 Transient
直接或间接派生的类的对象通常使用 new 运算符在动态内存中分配,并通过句柄操作。句柄定义为模板类 opencascade::handle<>
。Open CASCADE 技术提供预处理宏 Handle()
,在 OCCT 代码中通常用于命名句柄:
Handle(Geom_Line) aLine; // "Handle(Geom_Line)" 展开为 "opencascade::handle<Geom_Line>"
此外,对于大多数 OCCT 类,还为句柄定义了附加 typedef,即类名前缀为 Handle_
。例如,上述示例也可以编写为:
Handle_Geom_Line aLine; // "Handle_Geom_Line" 是 "opencascade::handle<Geom_Line>" 的 typedef
Using a Handle
A handle is characterized by the object it references.
Before performing any operation on a transient object, you must declare the handle. For example, if Point and Line are two transient classes from the Geom package, you would write:
Handle(Geom_Point) p1, p2;
Declaring a handle creates a null handle that does not refer to any object. The handle may be checked to be null by its method IsNull()
. To nullify a handle, use method Nullify()
.
To initialize a handle, either a new object should be created or the value of another handle can be assigned to it, on condition that their types are compatible.
Note that handles should only be used for object sharing. For all local operations, it is advisable to use classes manipulated by values.
使用句柄
句柄的特征是其引用的对象。
在对瞬态对象执行任何操作之前,必须声明句柄。例如,如果 Point
和 Line
是 Geom 包中的两个瞬态类,可编写:
Handle(Geom_Point) p1, p2;
声明句柄会创建一个空句柄,不引用任何对象。可通过其 IsNull()
方法检查句柄是否为空,使用 Nullify()
方法将句柄置空。
要初始化句柄,应创建新对象或将另一个句柄的值分配给它(条件是它们的类型兼容)。
请注意,句柄仅应用于对象共享,对于所有本地操作,建议使用通过值操作的类。
Type Management
Open CASCADE Technology provides a means to describe the hierarchy of data types in a generic way, with a possibility to check the exact type of the given object at run-time (similarly to C++ RTTI).
To enable this feature, a class declaration should include the declaration of OCCT RTTI. Header Standard_Type.hxx provides two variants of preprocessor macros facilitating this:
Inline variant, which declares and defines RTTI methods by a single line of code:
#include <Geom_Surface.hxx>
class Appli_ExtSurface : public Geom_Surface
{
. . .
public:DEFINE_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)
};
Out-of line variant, which uses one macro in the declaration (normally in the header file), and another in the implementation (in C++ source):
In Appli_ExtSurface.hxx file:
#include <Geom_Surface.hxx>
class Appli_ExtSurface : public Geom_Surface
{
. . .
public:DEFINE_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)
};
In Appli_ExtSurface.cxx file:
#include <Appli_ExtSurface.hxx>
IMPLEMENT_STANDARD_RTTIEXT(Appli_ExtSurface,Geom_Surface)
These macros define method DynamicType()
that returns a type descriptor - handle to singleton instance of the class Standard_Type
describing the class. The type descriptor stores the name of the class and the descriptor of its parent class.
Note that while inline version is easier to use, for widely used classes this method may lead to bloating of binary code of dependent libraries, due to multiple instantiations of inline method.
To get the type descriptor for a given class type, use macro STANDARD_TYPE()
with the name of the class as argument.
Example of usage:
if (aCurve->IsKind(STANDARD_TYPE(Geom_Line))) // equivalent to "if (dynamic_cast<Geom_Line>(aCurve.get()) != 0)"
{
...
}
类型管理
Open CASCADE 技术提供了一种以通用方式描述数据类型层次结构的方法,并可以在运行时检查给定对象的确切类型(类似于 C++ RTTI)。
为启用此功能,类声明应包含 OCCT RTTI 的声明。头文件 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))) // 等效于 "if (dynamic_cast<Geom_Line>(aCurve.get()) != 0)"
{
...
}
Type Conformity
The type used in the declaration of a handle is the static type of the object, the type seen by the compiler. A handle can reference an object instantiated from a subclass of its static type. Thus, the dynamic type of an object (also called the actual type of an object) can be a descendant of the type which appears in the handle declaration through which it is manipulated.
Consider the class Geom_CartesianPoint
, a sub-class of Geom_Point
; the rule of type conformity can be illustrated as follows:
Handle(Geom_Point) aPnt1;
Handle(Geom_CartesianPoint) aPnt2;
aPnt2 = new Geom_CartesianPoint();
aPnt1 = aPnt2; // OK, the types are compatible
The compiler sees aPnt1
as a handle to Geom_Point
though the actual object referenced by aPnt1
is of the Geom_CartesianPoint
type.
类型一致性
句柄声明中使用的类型是对象的静态类型(编译器看到的类型),句柄可以引用从其静态类型的子类实例化的对象。因此,对象的动态类型(也称为对象的实际类型)可以是句柄声明中出现的类型的后代。
考虑 Geom_CartesianPoint
类(Geom_Point
的子类),类型一致性规则可举例如下:
Handle(Geom_Point) aPnt1;
Handle(Geom_CartesianPoint) aPnt2;
aPnt2 = new Geom_CartesianPoint();
aPnt1 = aPnt2; // 正确,类型兼容
编译器将 aPnt1
视为指向 Geom_Point
的句柄,尽管 aPnt1
引用的实际对象是 Geom_CartesianPoint
类型。
Explicit Type Conversion
According to the rule of type conformity, it is always possible to go up the class hierarchy through successive assignments of handles. On the other hand, assignment does not authorize you to go down the hierarchy. Consequently, an explicit type conversion of handles is required.
A handle can be converted explicitly into one of its sub-types if the actual type of the referenced object is a descendant of the object used to cast the handle. If this is not the case, the handle is nullified (explicit type conversion is sometimes called a “safe cast”). Consider the example below.
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
If conversion is not compatible with the actual type of the referenced object, the handle which was “cast” becomes null (and no exception is raised). So, if you require reliable services defined in a sub-class of the type seen by the handle (static type), write as follows:
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}
}
Downcasting is used particularly with collections of objects of different types; however, these objects should inherit from the same root class.
For example, with a sequence of transient objects TColStd_SequenceOfTransient
and two classes A and B that both inherit from Standard_Transient, you get the following syntax:
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
}
显式类型转换
根据类型一致性规则,始终可以通过句柄的连续赋值在类层次结构中向上移动,另一方面,赋值不允许在层次结构中向下移动,因此需要句柄的显式类型转换。
如果所引用对象的实际类型是用于转换句柄的对象的后代,则可以将句柄显式转换为其子类型之一。如果不是这种情况,句柄将被置空(显式类型转换有时称为“安全转换”)。考虑以下示例:
Handle(Geom_Point) aPnt1;
Handle(Geom_CartesianPoint) aPnt2, aPnt3;
aPnt2 = new Geom_CartesianPoint();
aPnt1 = aPnt2; // 正确,标准赋值
aPnt3 = Handle(Geom_CartesianPoint)::DownCast (aPnt1);
// 正确,aPnt1 的实际类型是 Geom_CartesianPoint,尽管句柄的静态类型是 Geom_Point
如果转换与所引用对象的实际类型不兼容,“转换”后的句柄将变为空(不引发异常)。因此,如果需要使用句柄(静态类型)所见类型的子类中定义的可靠服务,可按如下方式编写:
void MyFunction (const Handle(A) & a)
{Handle(B) b = Handle(B)::DownCast(a);if (! b.IsNull()) {// 如果类 B 继承自 A,则可以使用 “b”}else {// 类型不兼容}
}
向下转型特别适用于不同类型对象的集合,但这些对象应继承自同一个根类。
例如,对于瞬态对象序列 TColStd_SequenceOfTransient
和两个都继承自 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);
// 此处不能编写:
// a = t; // 错误!
// 因此进行向下转型:
a = Handle (A)::Downcast (t)
if (!a.IsNull())
{// 类型兼容,可以使用 a
}
else
{// 类型不兼容
}
Using Handles to Create Objects
To create an object which is manipulated by handle, declare the handle and initialize it with the standard C++ new operator, immediately followed by a call to the constructor. The constructor can be any of those specified in the source of the class from which the object is instanced.
Handle(Geom_CartesianPoint) aPnt;
aPnt = new Geom_CartesianPoint (0, 0, 0);
Unlike for a pointer, the delete operator does not work on a handle; the referenced object is automatically destroyed when no longer in use.
使用句柄创建对象
要创建通过句柄操作的对象,需声明句柄并使用标准 C++ new 运算符初始化它,随后立即调用构造函数。构造函数可以是对象实例化所在类的源代码中指定的任何构造函数。
Handle(Geom_CartesianPoint) aPnt;
aPnt = new Geom_CartesianPoint (0, 0, 0);
与指针不同,delete 运算符不适用于句柄,所引用的对象在不再使用时会自动销毁。
Invoking Methods
Once you have a handle to an object, you can use it like a pointer in C++. To invoke a method which acts on the referenced object, you translate this method by the standard arrow operator, or alternatively, by function call syntax when this is available.
To test or to modify the state of the handle, the method is translated by the dot operator. The example below illustrates how to access the coordinates of an (optionally initialized) point object:
Handle(Geom_CartesianPoint) aCentre;
Standard_Real x, y, z;
if (aCentre.IsNull())
{aCentre = new PGeom_CartesianPoint (0, 0, 0);
}
aCentre->Coord (x, y, z);
The example below illustrates how to access the type object of a Cartesian point:
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";
}
Standard_NullObject
exception will be raised if a field or a method of an object is accessed via a Null handle.
方法调用
一旦拥有对象的句柄,就可以像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 << "类型检查通过\n";
}
else
{std::cout << "类型检查失败\n";
}
如果通过空句柄访问对象的字段或方法,将引发Standard_NullObject
异常。
Invoking Class Methods
A class method is called like a static C++ function, i.e. it is called by the name of the class of which it is a member, followed by the “::” operator and the name of the method.
For example, we can find the maximum degree of a Bezier curve:
Standard_Integer aDegree = Geom_BezierCurve::MaxDegree();
调用类方法
类方法的调用方式类似于C++静态函数,即通过类名(作为成员)后接::
运算符和方法名来调用。
例如,查找贝塞尔曲线的最大次数:
Standard_Integer aDegree = Geom_BezierCurve::MaxDegree();
Handle deallocation
Before you delete an object, you must ensure it is no longer referenced. To reduce the programming load related to this management of object life, the delete function in Open CASCADE Technology is secured by a reference counter of classes manipulated by handle. A handle automatically deletes an object when it is no longer referenced. Normally you never call the delete operator explicitly on instances of subclasses of Standard_Transient
.
When a new handle to the same object is created, the reference counter is incremented. When the handle is destroyed, nullified, or reassigned to another object, that counter is decremented. The object is automatically deleted by the handle when reference counter becomes 0.
The principle of allocation can be seen in the example below.
...
{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 referencesif (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;
句柄释放
在删除对象之前,必须确保其不再被引用。为减少与对象生命周期管理相关的编程负担,Open CASCADE技术中的delete函数通过句柄操作类的引用计数器来确保安全。当句柄不再引用对象时,会自动删除对象。通常,永远不要对Standard_Transient
子类的实例显式调用delete运算符。
当创建指向同一对象的新句柄时,引用计数器会递增;当句柄被销毁、置空或重新分配给另一个对象时,计数器递减。当引用计数器变为0时,句柄会自动删除对象。
分配原理可通过以下示例说明:
...
{Handle(TColStd_HSequenceOfInteger) H1 = new TColStd_HSequenceOfInteger();// H1有1个引用,对应48字节内存{Handle(TColStd_HSequenceOfInteger) H2;H2 = H1; // H1有2个引用if (argc == 3){Handle(TColStd_HSequenceOfInteger) H3;H3 = H1;// 此处H1有3个引用...}// 此处H1有2个引用}// 此处H1有1个引用
}
// 此处H1无引用,关联的TColStd_HSequenceOfInteger对象被删除
可以通过以下定义轻松将句柄对象的引用转换为void*
:
void* aPointer;
Handle(Some_Class) aHandle;
// 此处仅复制指针
aPointer = &aHandle;
// 此处复制Handle对象
aHandle = *(Handle(Some_Class)*)aPointer;
Cycles
Cycles appear if two or more objects reference each other by handles (stored as fields). In this condition automatic destruction will not work.
Consider for example a graph, whose objects (primitives) have to know the graph object to which they belong, i.e. a primitive must have a reference to complete graph object. If both primitives and the graph are manipulated by handle and they refer to each other by keeping a handle as a field, the cycle appears.
The graph object will not be deleted when the last handle to it is destructed in the application, since there are handles to it stored inside its own data structure (primitives).
There are two approaches how to avoid such situation:
- Use C++ pointer for one kind of references, e.g. from a primitive to the graph
- Nullify one set of handles (e.g. handles to a graph in primitives) when a graph object needs to be destroyed
循环引用
如果两个或多个对象通过句柄(作为字段存储)相互引用,就会出现循环引用。在这种情况下,自动销毁将失效。
例如,考虑一个图结构,其对象(基元)需要知道所属的图对象,即基元必须引用完整的图对象。如果基元和图都通过句柄操作,并且通过将句柄作为字段相互引用,就会形成循环。
当应用程序中最后一个指向图对象的句柄被销毁时,图对象不会被删除,因为其自身数据结构(基元)中存储了指向它的句柄。
避免这种情况有两种方法:
- 对某类引用使用C++指针(如基元到图的引用);
- 当需要销毁图对象时,将一组句柄置空(如基元中指向图的句柄)。
Memory Management
In a work session, geometric modeling applications create and delete a considerable number of C++ objects allocated in the dynamic memory (heap). In this context, performance of standard functions for allocating and deallocating memory may be not sufficient. For this reason, Open CASCADE Technology employs a specialized memory manager implemented in the Standard package.
The Memory Manager is based on the following principles:
- small memory arrays are grouped into clusters and then recycled (clusters are never released to the system),
- large arrays are allocated and de-allocated through the standard functions of the system (the arrays are released to system when they are no longer used).
As a general rule, it is advisable to allocate memory through significant blocks. In this way, the user can work with blocks of contiguous data and it facilitates memory page manager processing.
内存管理
在工作会话中,几何建模应用程序会创建和删除大量在动态内存(堆)中分配的C++对象。在这种情况下,标准内存分配和释放函数的性能可能不足。因此,Open CASCADE技术采用了在Standard包中实现的专用内存管理器。
内存管理器基于以下原则:
- 小内存数组被分组到簇中,然后回收(簇永远不会释放给系统);
- 大数组通过系统的标准函数分配和释放(数组不再使用时释放给系统)。
一般来说,建议通过大块分配内存,这样用户可以处理连续数据块,并便于内存页管理器处理。
Usage of Memory Manager
To allocate memory in a C code with Open CASCADE Technology memory manager, simply use method Standard::Allocate()
instead of malloc()
and method Standard::Free()
instead of free()
. In addition, method Standard::Reallocate()
is provided to replace C function realloc()
.
In C++, operators new() and delete() for a class may be defined so as to allocate memory using Standard::Allocate()
and free it using Standard::Free()
. In that case all objects of that class and all inherited classes will be allocated using the OCCT memory manager.
Preprocessor macro DEFINE_STANDARD_ALLOC
provided by header Standard_DefineAlloc.hxx
defines new() and delete() in this way. It is used for all OCCT classes (apart from a few exceptions) which thus are allocated using the OCCT memory manager. Since operators new() and delete() are inherited, this is also true for any class derived from an OCCT class, for instance, for all classes derived from Standard_Transient
.
Note that it is possible (though not recommended unless really unavoidable) to redefine new() and delete() functions for a class inheriting Standard_Transient
. If that is done, the method Delete()
should be also redefined to apply operator delete to this pointer. This will ensure that appropriate delete() function will be called, even if the object is manipulated by a handle to a base class.
内存管理器的用法
在使用Open CASCADE技术内存管理器的C代码中,只需使用Standard::Allocate()
方法代替malloc()
,使用Standard::Free()
方法代替free()
。此外,还提供了Standard::Reallocate()
方法来替代C函数realloc()
。
在C++中,类的new()
和delete()
运算符可以定义为使用Standard::Allocate()
分配内存和使用Standard::Free()
释放内存。在这种情况下,该类的所有对象和所有继承类都将使用OCCT内存管理器分配。
头文件Standard_DefineAlloc.hxx
提供的预处理宏DEFINE_STANDARD_ALLOC
以此方式定义new()
和delete()
,它用于所有OCCT类(除少数例外),因此这些类使用OCCT内存管理器分配。由于new()
和delete()
运算符是继承的,因此任何从OCCT类派生的类(例如所有从Standard_Transient
派生的类)也是如此。
请注意,对于继承自Standard_Transient
的类,可以重新定义new()
和delete()
函数(尽管除非万不得已,否则不建议这样做)。如果这样做,还应重新定义Delete()
方法,以对该指针应用delete
运算符。这将确保即使对象通过基类的句柄操作,也会调用适当的delete()
函数。
How to configure the Memory Manager
The OCCT memory manager may be configured to apply different optimization techniques to different memory blocks (depending on their size), or even to avoid any optimization and use C functions malloc() and free() directly. The configuration is defined by numeric values of the following environment variables:
- MMGT_OPT:
- if set to 0 (default) every memory block is allocated in C memory heap directly (via malloc() and free() functions). In this case, all other options except for MMGT_CLEAR are ignored;
- if set to 1 the memory manager performs optimizations as described below;
- if set to 2, Intel ® TBB optimized memory manager is used.
- MMGT_CLEAR: if set to 1 (default), every allocated memory block is cleared by zeros; if set to 0, memory block is returned as it is.
- MMGT_CELLSIZE: defines the maximal size of blocks allocated in large pools of memory. Default is 200.
- MMGT_NBPAGES: defines the size of memory chunks allocated for small blocks in pages (operating-system dependent). Default is 1000.
- MMGT_THRESHOLD: defines the maximal size of blocks that are recycled internally instead of being returned to the heap. Default is 40000.
- MMGT_MMAP: when set to 1 (default), large memory blocks are allocated using memory mapping functions of the operating system; if set to 0, they will be allocated in the C heap by malloc().
如何配置内存管理器
OCCT内存管理器可以配置为对不同内存块应用不同的优化技术(取决于其大小),甚至可以避免任何优化,直接使用C函数malloc()
和free()
。配置由以下环境变量的数值定义:
- MMGT_OPT:
- 设置为0(默认)时,每个内存块直接在C内存堆中分配(通过
malloc()
和free()
函数),此时除MMGT_CLEAR
外的所有其他选项都被忽略; - 设置为1时,内存管理器执行如下所述的优化;
- 设置为2时,使用Intel® TBB优化的内存管理器。
- 设置为0(默认)时,每个内存块直接在C内存堆中分配(通过
- MMGT_CLEAR:设置为1(默认)时,每个分配的内存块用零清除;设置为0时,内存块按原样返回。
- MMGT_CELLSIZE:定义在大内存池中分配的块的最大大小,默认值为200。
- MMGT_NBPAGES:定义为页面中的小块分配的内存块大小(取决于操作系统),默认值为1000。
- MMGT_THRESHOLD:定义在内部回收而不是返回堆的块的最大大小,默认值为40000。
- MMGT_MMAP:设置为1(默认)时,大内存块使用操作系统的内存映射函数分配;设置为0时,通过
malloc()
在C堆中分配。
Optimization Techniques
When MMGT_OPT is set to 1, the following optimization techniques are used:
- Small blocks with a size less than MMGT_CELLSIZE, are not allocated separately. Instead, a large pools of memory are allocated (the size of each pool is MMGT_NBPAGES pages). Every new memory block is arranged in a spare place of the current pool. When the current memory pool is completely occupied, the next one is allocated, and so on.
In the current version memory pools are never returned to the system (until the process finishes). However, memory blocks that are released by the method Standard::Free() are remembered in the free lists and later reused when the next block of the same size is allocated (recycling).
- Medium-sized blocks, with a size greater than MMGT_CELLSIZE but less than MMGT_THRESHOLD, are allocated directly in the C heap (using malloc() and free()). When such blocks are released by the method Standard::Free() they are recycled just like small blocks.
However, unlike small blocks, the recycled medium blocks contained in the free lists (i.e. released by the program but held by the memory manager) can be returned to the heap by method Standard::Purge().
- Large blocks with a size greater than MMGT_THRESHOLD, including memory pools used for small blocks, are allocated depending on the value of MMGT_MMAP: if it is 0, these blocks are allocated in the C heap; otherwise they are allocated using operating-system specific functions managing memory mapped files. Large blocks are returned to the system immediately when Standard::Free() is called.
优化技术
当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()
时,大块会立即返回给系统。
Benefits and drawbacks
The major benefit of the OCCT memory manager is explained by its recycling of small and medium blocks that makes an application work much faster when it constantly allocates and frees multiple memory blocks of similar sizes. In practical situations, the real gain on the application performance may be up to 50%.
The associated drawback is that recycled memory is not returned to the operating system during program execution. This may lead to considerable memory consumption and even be misinterpreted as a memory leak. To minimize this effect it is necessary to call the method Standard::Purge after the completion of memory-intensive operations.
The overhead expenses induced by the OCCT memory manager are:
size of every allocated memory block is rounded up to 8 bytes (when MMGT_OPT is 0 (default), the rounding is defined by the CRT; the typical value for 32-bit platforms is 4 bytes)
additional 4 bytes (or 8 on 64-bit platforms) are allocated in the beginning of every memory block to hold its size (or address of the next free memory block when recycled in free list) only when MMGT_OPT is 1.
Note that these overheads may be greater or less than overheads induced by the C heap memory manager, so overall memory consumption may be greater in either optimized or standard modes, depending on circumstances.
As a general rule, it is advisable to allocate memory through significant blocks. In this way, you can work with blocks of contiguous data, and processing is facilitated for the memory page manager.
OCCT memory manager uses mutex to lock access to free lists, therefore it may have less performance than non-optimized mode in situations when different threads often make simultaneous calls to the memory manager. The reason is that modern implementations of malloc() and free() employ several allocation arenas and thus avoid delays waiting mutex release, which are possible in such situations.
优缺点
OCCT内存管理器的主要优点是回收中小块,当应用程序不断分配和释放多个相似大小的内存块时,这会使应用程序工作得更快。在实际情况中,应用程序性能的实际提升可能高达50%。
相关缺点是回收的内存在程序执行期间不会返回给操作系统,这可能导致大量内存消耗,甚至被误解为内存泄漏。为了最小化这种影响,需要在内存密集型操作完成后调用Standard::Purge
方法。
OCCT内存管理器引起的开销包括:
- 每个分配的内存块的大小向上取整为8字节(当
MMGT_OPT
为0(默认)时,取整由CRT定义;32位平台的典型值为4字节); - 仅当
MMGT_OPT
为1时,每个内存块的开头会额外分配4字节(64位平台为8字节)来存储其大小(或在空闲列表中回收时的下一个空闲内存块的地址)。
请注意,这些开销可能大于或小于C堆内存管理器引起的开销,因此根据情况,优化模式或标准模式下的总体内存消耗可能更大。
Exceptions
异常
Introduction
The behavior of any object is implemented by the methods, which were defined in its class declaration. The definition of these methods includes not only their signature (their programming interface) but also their domain of validity.
This domain is expressed by exceptions. Exceptions are raised under various error conditions to protect software quality.
Exception handling provides a means of transferring control from a given point in a program being executed to an exception handler associated with another point previously executed.
A method may raise an exception which interrupts its normal execution and transfers control to the handler catching this exception.
A hierarchy of commonly used exception classes is provided. The root class is Standard_Failure from the Standard package. So each exception inherits from Standard_Failure either directly or by inheriting from another exception. Exception classes list all exceptions, which can be raised by any OCCT function.
Open CASCADE Technology also provides support for converting system signals (such as access violation or division by zero) to exceptions, so that such situations can be safely handled with the same uniform approach.
However, in order to support this functionality on various platforms, some special methods and workarounds are used. Though the implementation details are hidden and handling of OCCT exceptions is done basically in the same way as with C++, some peculiarities of this approach shall be taken into account and some rules must be respected.
The following paragraphs describe recommended approaches for using exceptions when working with Open CASCADE Technology.
简介
任何对象的行为都由其类声明中定义的方法实现,这些方法的定义不仅包括其签名(编程接口),还包括其有效域。
该有效域由异常表示,异常在各种错误条件下引发,以保护软件质量。
异常处理提供了一种将控制从正在执行的程序中的给定点转移到与先前执行的另一点相关联的异常处理程序的方法。
方法可能引发异常,中断其正常执行并将控制转移到捕获该异常的处理程序。
系统提供了常用异常类的层次结构,根类是Standard包中的Standard_Failure
。因此,每个异常要么直接继承自Standard_Failure
,要么继承自另一个异常。异常类列出了任何OCCT函数可能引发的所有异常。
Raising an Exception
“C++ like” Syntax
The following example:
throw Standard_DomainError ("Cannot cope with this condition");
raises an exception of Standard_DomainError
type with the associated message “Cannot cope with this condition”, the message being optional. This exception may be caught by a handler of a Standard_DomainError type as follows:
try
{OCC_CATCH_SIGNALS// try block
}
catch (const Standard_DomainError& )
{// handle Standard_DomainError exceptions here
}
Regular usage
Exceptions should not be used as a programming technique, to replace a “goto” statement for example, but as a way to protect methods against misuse. The caller must make sure its condition is such that the method can cope with it.
Thus,
No exception should be raised during normal execution of an application.
A method which may raise an exception should be protected by other methods allowing the caller to check on the validity of the call.
For example, if you consider the TCollection_Array1 class used with:
Value function to extract an element;
Lower function to extract the lower bound of the array;
Upper function to extract the upper bound of the array.
then, the Value function may be implemented as follows:
Item TCollection_Array1::Value (Standard_Integer theIndex) const
{// where myR1 and myR2 are the lower and upper bounds of the arrayif (theIndex < myR1 || theIndex > myR2){throw Standard_OutOfRange ("Index out of range in TCollection_Array1::Value");}return myContents[theIndex];
}
Here validity of the index is first verified using the Lower and Upper functions in order to protect the call. Normally the caller ensures the index being in the valid range before calling Value(). In this case the above implementation of Value is not optimal since the test done in Value is time-consuming and redundant.
It is a widely used practice to include that kind of protections in a debug build of the program and exclude in release (optimized) build. To support this practice, the macros Raise_if() are provided for every OCCT exception class:
<ErrorTypeName>_Raise_if(condition, "Error message");
where ErrorTypeName is the exception type, condition is the logical expression leading to the raise of the exception, and Error message is the associated message.
The entire call may be removed by defining one of the preprocessor symbols No_Exception or No_ at compile-time:
#define No_Exception // remove all raises
Using this syntax, the Value function becomes:Item TCollection_Array1::Value (Standard_Integer theIndex) const
{Standard_OutOfRange_Raise_if(theIndex < myR1 || theIndex > myR2, "index out of range in TCollection_Array1::Value");return myContents[theIndex];
}
引发异常
类C++语法
以下示例:
throw Standard_DomainError ("无法处理此条件");
引发Standard_DomainError
类型的异常,并关联消息“无法处理此条件”(消息为可选)。此异常可由Standard_DomainError
类型的处理程序捕获,如下所示:
try
{OCC_CATCH_SIGNALS// try块
}
catch (const Standard_DomainError& )
{// 在此处理Standard_DomainError异常
}
异常处理
当引发异常时,控制会转移到调用栈中给定类型的最近处理程序,即:
- 最近进入且尚未退出的try块的处理程序;
- 类型与引发表达式匹配的处理程序。
如果满足以下条件,T异常类型的处理程序与E异常类型的引发表达式匹配:
- T和E是同一类型;
- T是E的超类型。
插件管理
插件分发
插件是可以动态加载到客户端应用程序中的组件,不需要直接链接到它。插件不绑定到其客户端,即插件只知道其连接机制的定义以及如何调用相应的服务。
插件可用于:
- 实现驱动程序机制,即根据当前事务动态更改驱动程序实现(例如,检索存储在应用程序另一个版本中的文档);
- 将处理资源限制为所需的最小值(例如,只要用户不需要,就不会在运行时加载任何应用程序服务);
- 促进模块化开发(应用程序可以提供基本功能,而一些高级功能将在可用时作为插件添加)。
插件通过全局通用标识符(GUID)标识,GUID包含小写字符,且不能以空格结尾。
一旦加载,对插件提供的服务的调用是直接的(客户端与插件使用相同的语言实现)。
C++插件实现
C++插件将服务实现为具有在抽象类中定义的函数的对象(此抽象类及其带有GUID的父类是客户端应用程序中实现的关于插件的唯一信息)。插件由一个可共享库组成,其中包含一个名为Factory的方法,用于创建C++对象(客户端无法实例化此对象,因为插件实现不可见)。基础类在Plugin包中提供了一个名为Load()的方法,使客户端能够通过库访问所需的服务。
该方法从环境变量CSF_PluginDefaults
找到的资源文件Plugin中读取可用插件及其位置的信息:
$CSF_PluginDefaults/Plugin
C++客户端插件实现
要调用插件提供的服务之一,可以使用请求服务的Standard_GUID
调用Plugin::Load()全局函数,如下所示:
Handle(FADriver_PartStorer)::DownCast(PlugIn::Load (yourStandardGUID));