Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Tutorial 教程

Hello, world
Properties 屬性
Project Hierarchies 工程層次
Dependent Targets 依賴目標
Testing 測試
Static and shared libaries 靜態庫和共享庫
Conditions and alternatives 條件和選擇
Prebuilt targets 預構建目標

This section will guide you though the most basic features of Boost.Build V2. We will start with the 「Hello, world」 example, learn how to use libraries, and finish with testing and installing features.
本節將教給你一些 Boost.Build V2 中最基本的特性。我們將從 「Hello, world」 例子開始,學習如何使用庫,並以測試和安裝特性結束。

Hello, world

The simplest project that Boost.Build can construct is stored in example/hello/ directory. The project is described by a file called Jamroot that contains:
Boost.Build 可以構建的最簡單工程保存在 example/hello/ 目錄下。該工程由一個名為 Jamroot 的文件來描述,它包含:

exe hello : hello.cpp ;

Even with this simple setup, you can do some interesting things. First of all, just invoking bjam will build the hello executable by compiling and linking hello.cpp . By default, debug variant is built. Now, to build the release variant of hello, invoke
即使只是這麼簡單的設置,你也可以做一些有趣的事情。首先,只需調用 bjam 就可以通過編譯和鏈接 hello.cpp 來構建 hello 的可執行文件。缺省情況是構建調試版本。要構 hello 的發佈版本,請調用:

bjam release

Note that debug and release variants are created in different directories, so you can switch between variants or even build multiple variants at once, without any unnecessary recompilation. Let us extend the example by adding another line to our project's Jamroot:
注意,調試版本和發佈版本是在不同目錄下構建的,所以你可以在兩個版本間切換,或者一次構建多個版本,而不需要任何多餘的重編譯。讓我們來擴展這個例子,增加一行到我們的工程的 Jamroot 中:

exe hello2 : hello.cpp ;

Now let us build both the debug and release variants of our project again:
現在讓我們再次構建這個工程的調試和發佈版本:

bjam debug release

Note that two variants of hello2 are linked. Since we have already built both variants of hello, hello.cpp will not be recompiled; instead the existing object files will just be linked into the corresponding variants of hello2. Now let us remove all the built products:
注意,hello2 的兩個版本都被鏈接了。因為我們已經構建過 hello 的兩個版本,所以 hello.cpp 無須重編譯;而只須將已有的目標文件鏈接到 hello2 的相應版本中。現在我們來刪掉所有已構建的成品:

bjam --clean debug release

It is also possible to build or clean specific targets. The following two commands, respectively, build or clean only the debug version of hello2.
也可以只構建或清除指定的目標。以下兩個命令分別構建和清除 hello2 的調試版本。

bjam hello2
bjam --clean hello2

Properties 屬性

To portably represent aspects of target configuration such as debug and release variants, or single- and multi-threaded builds, Boost.Build uses features with associated values. For example, the debug-symbols feature can have a value of on or off. A property is just a (feature, value) pair. When a user initiates a build, Boost.Build automatically translates the requested properties into appropriate command-line flags for invoking toolset components like compilers and linkers.
為了可移植地表示目標配置的情況,如調試版本和發佈版本、或者單線程和多線程,Boost.Build 使用了 特性features,它帶有關聯的 值values。例如,特性 debug-symbols 可以具有 onoff 的值。屬性property 是一對 (特性,值)。當用戶初始化一個構建時,Boost.Build 自動地將給定的屬性翻譯為適合的命令行選項,以調用象編譯器和鏈接器這些工具集組件。

There are many built-in features that can be combined to produce arbitrary build configurations. The following command builds the project's release variant with inlining disabled and debug symbols enabled:
有多個內建的特性可以組合起來,生成不同的構建配置。以下命令行以禁止內聯和打開調試符號的配置來構建工程的 release 版本:

bjam release inlining=off debug-symbols=on

Properties on the command-line are specified with the syntax:
命令行上的屬性以這樣的語法指定:

feature-name=feature-value

The release and debug that we have seen in bjam invocations are just a shorthand way to specify values of the variant feature. For example, the command above could also have been written this way:
我們在 bjam 調用中曾經見過的 releasedebug 不過是一個指定 variant 特性的縮寫。例如,上述命令也可以寫成:

bjam variant=release inlining=off debug-symbols=on

variant is so commonly-used that it has been given special status as an implicit feature— Boost.Build will deduce the its identity just from the name of one of its values.
variant 是如此常用,所以它被給予了作為隱式特性的特殊狀態 — Boost.Build 僅從它的值名就可以推斷出它的身份。

A complete description of features can be found in the section called 「Features and properties」.
有關"特性"的完整說明,請見 「特性與屬性」一節

Build Requests and Target Requirements 構建請求與目標要求

The set of properties specified on the command line constitute a build request—a description of the desired properties for building the requested targets (or, if no targets were explicitly requested, the project in the current directory). The actual properties used for building targets are typically a combination of the build request and properties derived from the project's Jamroot (and its other Jamfiles, as described in the section called 「Project Hierarchies」). For example, the locations of #included header files are normally not specified on the command-line, but described in Jamfiles as target requirements and automatically combined with the build request for those targets. Multithread-enabled compilation is another example of a typical target requirement. The Jamfile fragment below illustrates how these requirements might be specified.
在命令行中指定的一組屬性組成了 構建請求 — 用於構建目標(或者,如果沒有顯式給定目標,即當前目錄中的工程)的應有屬性的說明。用於構建目標的實際 屬性是構建請求和從工程的 Jamroot (及其它 Jamfiles,如 「工程層次」一節 中所說的)繼承得到的屬性的合併。例如,#included 頭文件的位置一般不在命令行中指定,卻會在 Jamfiles 中以 目標要求 描述,它會被自動與目標的構建請求合併。多線程編譯是目標要求的另一個典型例子。以下 Jamfile 片段示範了這些要求可以如何指定:

exe hello
: hello.cpp
: <include>boost <threading>multi
;

When hello is built, the two requirements specified above will always be present. If the build request given on the bjam command-line explictly contradicts a target's requirements, the target requirements usually override (or, in the case of 「free」」 features like <include>, [6] augments) the build request.
hello 被構建,以上指定的兩個要求總是會被應用。如果在 bjam 命令行上給出的構建請求與某個目標要求相矛盾,則目標要求通常會覆蓋(或者加入到,像 <include> 這樣的"自由"特性[6] )構建請求。

[Tip] Tip 提示

The value of the <include> feature is relative to the location of Jamroot where it is used.

<include> 特性的值是相對於所用的 Jamroot 文件的位置的。

Project Attributes 工程屬性

If we want the same requirements for our other target, hello2 , we could simply duplicate them. However, as projects grow, that approach leads to a great deal of repeated boilerplate in Jamfiles. Fortunately, there's a better way. Each project can specify a set of attributes, including requirements:
如果我們對於另一個目標 hello2 也有相同的要求,我們只需要複製它們就可以了。不過,隨著工程的發展,這一方法會導致在 Jamfiles 中出現大量重複。幸好,我們有更好的方法。每個工程都可以指定一組 屬性attributes,包括目標要求:

project
: requirements <include>/home/ghost/Work/boost <threading>multi
;

exe hello : hello.cpp ;
exe hello2 : hello.cpp ;

The effect would be as if we specified the same requirement for both hello and hello2.
其作用就像我們為 hellohello2 指定相同的目標要求一樣。

Project Hierarchies 工程層次

So far we have only considered examples with one project —a. with one user-written Boost.Jam file, Jamroot). A typical large codebase would be composed of many projects organized into a tree. The top of the tree is called the project root. Every subproject is defined by a file called Jamfile in a descendant directory of the project root. The parent project of a subproject is defined by the nearest Jamfile or Jamroot file in an ancestor directory. For example, in the following directory layout:
迄今為止,我們只討論了帶有一個工程的例子 — 只帶有一個用戶編寫的 Boost.Jam 文件 Jamroot。一個典型的大型代碼基將包含許多工程,它們組成一棵樹。樹的頂端被稱為 工程的根。每一個子工程由工程根的一個子目錄中的名為 Jamfile 的文件來定義。一個子工程的父工程由其父目錄中最接近的 JamfileJamroot 文件來定義。例如,在以下目錄佈局中:

top/
|
+-- Jamroot
|
+-- app/
| |
| +-- Jamfile
| `-- app.cpp
|
`-- util/
|
+-- foo/
. |
. +-- Jamfile
. `-- bar.cpp

the project root is top/. The projects in top/app/ and top/util/foo/ are immediate children of the root project.
工程的根是 top/. 在 top/app/top/util/foo/ 中的工程根工程的直接子工程。

[Note] Note 說明

When we refer to a 「Jamfile,」 set in normal type, we mean a file called either Jamfile or Jamroot. When we need to be more specific, the filename will be set as 「Jamfile」 or 「Jamroot.」

當我們以普通方式使用 「Jamfile」 時,是指某個名為 JamfileJamroot 的文件。當我們需要更明確時,則將文件名寫為 「Jamfile」 或 「Jamroot.」

Projects inherit all attributes (such as requirements) from their parents. Inherited requirements are combined with any requirements specified by the subproject. For example, if top/Jamroot has
工程從它們的父工程繼承所有屬性(如目標要求)。繼承的要求與子工程指定的要求相合併。例如,如果 top/Jamroot 在目標要求中有:

<include>/home/ghost/local

in its requirements, then all of its subprojects will have it in their requirements, too. Of course, any project can add include paths to those specified by its parents. [7] More details can be found in the section called 「Projects」.
則它的所有子工程也都具有這一要求。當然,任一工程都可以在父工程的基礎上增加頭文件包含路徑[7]。更多的細節請見 「工程」一節

Invoking bjam without explicitly specifying any targets on the command line builds the project rooted in the current directory. Building a project does not automatically cause its subprojects to be built unless the parent project's Jamfile explicitly requests it. In our example, top/Jamroot might contain:
在命令行不指定任何目標地調用 bjam,將構建當前目錄中的工程。構建一個工程不會自動引起其子工程的構建,除非父工程的 Jamfile 中明確要求。在我們的例子中,top/Jamroot 可能包含有:

build-project app ;

which would cause the project in top/app/ to be built whenever the project in top/ is built. However, targets in top/util/foo/ will be built only if they are needed by targets in top/ or top/app/.
這將導致在構建 top/ 中的工程時,構建 top/app/ 中的工程。但是,在 top/util/foo/ 中的目標則僅當被 top/top/app/ 中的目標所需要時才構建。

Dependent Targets 依賴目標

When a building a target X depends on first building another target Y (such as a library that must be linked with X), Y is called a dependency of X and X is termed a dependent of Y
如果目標 X 的構建依賴於另一個目標 Y 的首先構建(如一個必須與 X 鏈接的庫),則 Y 被稱為 X依賴物X 則被稱為 依賴於 Y

To get a feeling of target dependencies, let's continue the above example and see how top/app/Jamfile can use libraries from top/util/foo. If top/util/foo/Jamfile contains
為了獲得對目標依賴關係的感覺,我們來繼續前面的例子,看看 top/app/Jamfile 可以如何使用來自於 top/util/foo 的庫。如果 top/util/foo/Jamfile 包含有:

lib bar : bar.cpp ;

then to use this library in top/app/Jamfile, we can write:
我們要使用這個庫,我們可以在 top/app/Jamfile 中寫:

exe app : app.cpp ../util/foo//bar ;

While app.cpp refers to a regular source file, ../util/foo//bar is a reference to another target: a library bar declared in the Jamfile at ../util/foo.
其中 app.cpp 表示一個普通的源文件,../util/foo//bar 則表示另一個目標:在 ../util/foo 的 Jamfile 中聲明的 bar 庫。

[Tip] Tip 提示

Some other build system have special syntax for listing dependent libraries, for example LIBS variable. In Boost.Build, you just add the library to the list of sources.
其它一些構建系統有特殊的語法來列出依賴庫,例如 LIBS 變量。在 Boost.Build 中,你只需要將庫增加到源的列表中。

Suppose we build app with:
假設我們要構建 app

bjam app optimization=full define=USE_ASM

Which properties will be used to build foo? The answer is that some features are propagated—Boost.Build attempts to use dependencies with the same value of propagated features. The <optimization> feature is propagated, so both app and foo will be compiled with full optimization. But <define> is not propagated: its value will be added as-is to the compiler flags for a.cpp, but won't affect foo.
哪些屬性會用來構建 foo 呢? 答案是,有些特性是 被傳播的 — Boost.Build 嘗試以被傳播特性的相同值使用依賴物。特性 <optimization> 就是被傳播的,所以 appfoo 都會以全優化方式編譯。而 <define> 不是被傳播的:它的值會被作為 a.cpp 的一個編譯選項加入,但不會影響 foo.

Let's improve this project further. The library probably has some headers that must be used when compiling app.cpp. We could manually add the necessary #include paths to app 's requirements as values of the <include> feature, but then this work will be repeated for all programs that use foo. A better solution is to modify util/foo/Jamfile in this way:
讓我們來改進一下這個工程。這個庫可能有一些頭文件在編譯 app.cpp 時要使用。我們可以手工將所需的 #include 路徑作為 <include> 特性的值加到 app 的要求中,但是這個工作稍後可能要對所有用到 foo 的程序都重複一次。一個更好的解決方法是,按以下方法修改 util/foo/Jamfile

project
: usage-requirements <include>.
;

lib foo : foo.cpp ;

Usage requirements are applied not to the target being declared but to its dependants. In this case, <include>. will be applied to all targets that directly depend on foo.
"用法要求"不是應用到被聲明的目標上,而是應用到依賴於它的目標上。在這個例子中,<include> 將被應用到所有直接依賴於 foo 的目標上。

Another improvement is using symbolic identifiers to refer to the library, as opposed to Jamfile location. In a large project, a library can be used by many targets, and if they all use Jamfile location, a change in directory organization entails much work. The solution is to use project ids—symbolic names not tied to directory layout. First, we need to assign a project id by adding this code to Jamroot:
另一個改進是,使用符號標識符來表示一個庫,而不是用 Jamfile 的位置。在一個大型工程中,一個庫可以被多個目標使用,如果它們總是使用 Jamfile 位置來表示,那麼對目錄組織的修改將會帶來大量工作。解決方法是,使用工程 ids—符號名而不是依靠目錄佈局。首先,我們需要往 Jamroot 增加以下代碼來賦值一個工程 id:

use-project /library-example/foo : util/foo ;

Second, we modify app/Jamfile to use the project id:
然後,我們修改 app/Jamfile 以使用這個工程 id:

exe app : app.cpp /library-example/foo//bar ;

The /library-example/foo//bar syntax is used to refer to the target bar in the project with id /library-example/foo. We've achieved our goal—if the library is moved to a different directory, only Jamroot must be modified. Note that project ids are global—two Jamfiles are not allowed to assign the same project id to different directories.
語法 /library-example/foo//bar 是用於在工程中以 id /library-example/foo 表示目標 bar。我們已經實現了我們的目標—如果庫被移動到其它目錄,只需要修改 Jamroot。注意,工程 ids 是全局的—不允許兩個 Jamfiles 將相同的工程 id 賦給不同的目錄。

[Tip] Tip 提示

If you want all applications in some project to link to a certain library, you can avoid having to specify it directly the sources of every target by using the <library> property. For example, if /boost/filesystem//fs should be linked to all applications in your project, you can add <library>/boost/filesystem//fs to the project's requirements, like this:

如果你想讓一些工程中的所有應用程序都鏈接到某個特定的庫,你可以無須直接在每個目標中使用 <library> 特性來指定它。例如,如果 /boost/filesystem//fs 要被鏈接到你的工程中的所有應用程序,你可以將 <library>/boost/filesystem//fs 加到工程的要求中,如下:

project
: requirements <source>/boost/filesystem//fs
;

Testing 測試

Static and shared libaries 靜態庫和共享庫

Libraries can be either static, which means they are included in executable files that use them, or shared (a.k.a. dynamic), which are only referred to from executables, and must be available at run time. Boost.Build can create and use both kinds.
庫可以是 靜態的,即被包含在使用它們的可執行文件中,或者是 共享的 (又稱為 動態的),即在可執行文件中只有引用,在運行期才必須可用。對於這兩種類型,Boost.Build 可以創建和使用。

The kind of library produced from a lib target is determined by the value of the link feature. Default value is shared, and to build a static library, the value should be static. You can request a static build either on the command line:
一個 lib 目標所產生的庫的類型由特性 link 的值決定。缺省值是 shared, 要構建一個靜態庫,則其值應為 static。你可以有兩種方法請求構建一個靜態庫,在命令行上:

bjam link=static

或者在庫的要求中:

lib l : l.cpp : <link>static ;

We can also use the <link> property to express linking requirements on a per-target basis. For example, if a particular executable can be correctly built only with the static version of a library, we can qualify the executable's target reference to the library as follows:
我們也可以使用特性 <link> 來表示每個目標的鏈接要求。例如,如果某個可執行文件只能用某個庫的靜態版本來構建,我們可以限定可執行文件的 目標引用 到這個庫,如下:

exe important : main.cpp helpers/<link>static ;

No matter what arguments are specified on the bjam command line, important will only be linked with the static version of helpers.
無論在 bjam 命令行中指定什麼參數,important 都只會與 helpers 的靜態版本鏈接。

Specifying properties in target references is especially useful if you use a library defined in some other project (one you can't change) but you still want static (or dynamic) linking to that library in all cases. If that library is used by many targets, you could use target references everywhere:
如果你使用了一個在其它工程(一個你不能修改的工程)中定義的庫,而你還是想在任何情況下都進行靜態(或動態)鏈接,那麼在目標要求中指定特性就非常有用了。如果那個庫被多個目標使用,你可以在任意地方使用目標引用:

exe e1 : e1.cpp /other_project//bar/<link>static ;
exe e10 : e10.cpp /other_project//bar/<link>static ;

but that's far from being convenient. A better approach is to introduce a level of indirection. Create a local alias target that refers to the static (or dynamic) version of foo:
但這樣很不方便。更好的方法是引用一個間接層。創建一個局部別名目標來表示 foo 的靜態(或動態)版本:

alias foo : /other_project//bar/<link>static ;
exe e1 : e1.cpp foo ;
exe e10 : e10.cpp foo ;

The alias rule is specifically used to rename a reference to a target and possibly change the properties.
規則 alias 特別適用於修改一個目標引用的名字,也可以修改特性。

[Tip] Tip 提示

When one library uses another, you put the second library in the source list of the first. For example:
當一個庫使用另一個庫時,你要將第二個庫放在第一個庫的源列表中。例如:

lib utils : utils.cpp /boost/filesystem//fs ;
lib core : core.cpp utils ;
exe app : app.cpp core ;

This works no matter what kind of linking is used. When core is built as a shared library, it is linked directly into utils. Static libraries can't link to other libraries, so when core is built as a static library, its dependency on utils is passed along to core's dependents, causing app to be linked with both core and utils .
無論使用哪種鏈接,這都可以。當 core 被構建為一個共享庫時,它被直接鏈入到 utils 中。靜態庫不能鏈接到其它庫,所以當 core 被構建為靜態庫時,它對 utils 的依賴關係被傳遞到依賴於 core 的目標,這使得 app 同時與 coreutils 鏈接。

[Note] Note 說明

(Note for non-UNIX system). Typically, shared libraries must be installed to a directory in the dynamic linker's search path. Otherwise, applications that use shared libraries can't be started. On Windows, the dynamic linker's search path is given by the PATH environment variable. This restriction is lifted when you use Boost.Build testing facilities—the PATH variable will be automatically adjusted before running the executable.
(對非-UNIX 系統的說明)。通常,共享庫必須安裝在動態鏈接器的查找路徑中的目錄下。否則,使用共享庫的應用程序將不能啟動。在 Windows 上,動態鏈接器的查找路徑由環境變量 PATH 給出。這一限制在你使用 Boost.Build 測試工具時會消失—變量 PATH 會在運行可執行程序前被自動調整。

Conditions and alternatives 條件與選擇

Sometimes, particular relationships need to be maintained among a target's build properties. For example, you might want to set specific #define when a library is built as shared, or when a target's release variant is built. This can be achieved using conditional requirements.
有時候,某些特殊關係需要根據目標的構建屬性來維護。例如,你可能想在某個庫被構建為共享庫時,或者在構建一個目標的 release 版本時,設置特定的 #define。這可以通過 條件要求 來實現。

lib network : network.cpp
: <link>shared:<define>NEWORK_LIB_SHARED <variant>release:<define>EXTRA_FAST ;

In the example above, whenever network is built with <link>shared, <define>NEWORK_LIB_SHARED will be in its properties, too. Also, whenever its release variant is built, <define>EXTRA_FAST will appear in its properties.
在上例中,當 network<link>shared 構建時,<define>NEWORK_LIB_SHARED 將會在它的屬性中。同樣,當構建它的發佈版本時,<define>EXTRA_FAST 將會出現在它的屬性中。

Sometimes the ways a target is built are so different that describing them using conditional requirements would be hard. For example, imagine that a library actually uses different source files depending on the toolset used to build it. We can express this situation using target alternatives:
有時候一個目標的構建方法差異很大,很難用條件要求來描述。例如,想像一個根據構建工具集不同而使用不同源文件的庫。我們可以用 目標選擇 來表示這種情形:

lib demangler : dummy_demangler.cpp ;                      # alternative 1
lib demangler : demangler_gcc.cpp : <toolset>gcc ; # alternative 2
lib demangler : demangler_msvc.cpp : <toolset>msvc ; # alternative 3

When building demangler, Boost.Build will compare requirements for each alternative with build properties to find the best match. For example, when building with <toolset>gcc alternative 2, will be selected, and when building with <toolset>msvc alternative 3 will be selected. In all other cases, the most generic alternative 1 will be built.
在構建 demangler 時,Boost.Build 將每種選擇與構建屬性進行比較,找出最佳匹配。例如,在使用 <toolset>gcc 進行構建時,選擇 alternative 2,而在使用 <toolset>msvc 進行構建時,則選擇 alternative 3。在其它情況下,最通用的 alternative 1 將被構建。

Prebuilt targets 預構建目標

To link to libraries whose build instructions aren't given in a Jamfile, you need to create lib targets with an appropriate file property. Target alternatives can be used to associate multiple library files with a single conceptual target. For example:
要鏈接到那些構建指令沒有在 Jamfile 中給定的庫時,你需要以一個適當的 file 屬性創建 lib 目標。可以使用目標選擇來將多個庫文件關聯至一個概念上的目標。例如:

# util/lib2/Jamfile
lib lib2
:
: <file>lib2_release.a <variant>release
;

lib lib2
:
: <file>lib2_debug.a <variant>debug
;

This example defines two alternatives for lib2, and for each one names a prebuilt file. Naturally, there are no sources. Instead, the <file> feature is used to specify the file name.
這個例子為 lib2 定義了兩個選擇,為每個分別指定預構建文件。這裡自然是沒有源文件的。而是使用 <file> 特性來指定文件名。

Once a prebuilt target has been declared, it can be used just like any other target:
一旦聲明了一個預構建目標,它就可以像其它任意目標一樣使用:

exe app : app.cpp ../util/lib2//lib2 ;

As with any target, the alternative selected depends on the properties propagated from lib2's dependants. If we build the release and debug versions of app will be linked with lib2_release.a and lib2_debug.a , respectively.
和其它目標一樣,目標的選擇是根據從依賴於 lib2 的目標所傳播來的屬性進行的。如果我們構建 app 的發佈版本和調試版本,則它們分別與 lib2_release.alib2_debug.a 鏈接。

System libraries—those that are automatically found by the toolset by searching through some set of predetermined paths—should be declared almost like regular ones:
系統庫—它們由工具集在一組預定義的路徑中自動查找—應該像普通的庫一樣聲明:

lib pythonlib : : <name>python22 ;

We again don't specify any sources, but give a name that should be passed to the compiler. If the gcc toolset were used to link an executable target to pythonlib, -lpython22 would appear in the command line (other compilers may use different options).
我們不用指定任何源文件,但是要給定一個傳遞給編譯器的 name。如果使用 gcc 工具集來鏈接一個可執行目標到 pythonlib,則 -lpython22 將出現在命令行中(其它編譯器可能使用不同的選項)。

We can also specify where the toolset should look for the library:
我們也可以指定工具在哪裡查找該庫:

lib pythonlib : : <name>python22 <search>/opt/lib ;

And, of course, target alternatives can be used in the usual way:
當然還有,目標選擇可以按普通方法使用:

lib pythonlib : : <name>python22 <variant>release ;
lib pythonlib : : <name>python22_d <variant>debug ;

A more advanced use of prebuilt targets is described in the section called 「Targets in site-config.jam」.
有關預構建目標的更進一步使用,在 「site-config.jam 中的目標」一節 中說明。



[7] Many features will be overridden, rather than added-to, in subprojects. See the section called 「Feature Attributes」 for more information
在子工程中的許多特性都是覆寫的,而不是增加的。更多信息請見 「特性屬性」一節


PrevUpHomeNext