admin 管理员组文章数量: 887021
2024年1月10日发(作者:inrush current)
.
英文翻译
英语原文:
. Introducing Classes
The only remaining feature we need to understand before solving our bookstore
problem is how to write a data structure to represent our transaction data. In C++ we define
our own data structure by defining a class. The class mechanism is one of the most
important features in C++. In fact, a primary focus of the design of C++ is to make it
possible to define class types that behave as naturally as the built-in types themselves. The
library types that we've seen already, such as istream and ostream, are all defined as
classesthat is,they are not strictly speaking part of the language.
Complete understanding of the class mechanism requires mastering a lot of
information. Fortunately, it is possible to use a class that someone else has written without
knowing how to define a class ourselves. In this section, we'll describe a simple class that
we canuse in solving our bookstore problem. We'll implement this class in the subsequent
chapters as we learn more about types,expressions, statements, and functionsall of which
are used in defining classes.
To use a class we need to know three things:
What is its name?
Where is it defined?
What operations does it support?
For our bookstore problem, we'll assume that the class is named Sales_item and that it
is defined in a header named Sales_item.h.
The Sales_item Class
The purpose of the Sales_item class is to store an ISBN and keep track of the number
of copies sold, the revenue, and average sales price for that book. How these data are stored
or computed is not our concern. To use a class, we need not know anything about how it is
implemented. Instead, what we need to know is what operations the class provides.
As we've seen, when we use library facilities such as IO, we must include the
associated headers. Similarly, for our own classes, we must make the definitions associated
with the class available to the compiler. We do so in much the same way. Typically, we put
the class definition into a file. Any program that wants to use our class must include that
file.
Conventionally, class types are stored in a file with a name that, like the name of a
program source file, has two parts: a file name and a file suffix. Usually the file name is the
same as the class defined in the header. The suffix usually is .h, but some programmers
use .H, .hpp, or .hxx. Compilers usually aren't picky about header file names, but IDEs
sometimes are. We'll assume that our class is defined in a file named Sales_item.h.
Operations on Sales_item Objects
1 / 12
.
Every class defines a type. The type name is the same as the name of the class. Hence,
our Sales_item class defines a type named
Sales_item. As with the built-in types, we can define a variable of a class type. When we
write "Sales_item item" we are saying that item is an object of type Sales_item. We often
contract the phrase "an object of type Sales_item" to"aSales_ item object" or even more
simply to "a Sales_item."
In addition to being able to define variables of type Sales_item, we can perform the
following operations on Sales_item objects:
Use the addition operator, +, to add two Sales_items,
Use the input operator, << to read a Sales_item object,
Use the output operator, >> to write a Sales_item object,
Use the assignment operator, =, to assign one Sales_item object to another,
Call the same_isbn function to determine if two Sales_items refer to the same book.
Classes are central to most C++ programs: Classes let us define our own types that are
customizedfor the problems we need to solve, resulting in applications that are easier to
write and -designed class types can be as easy to use as the built-in types.
A class defines data and function members: The data members store the state
associated with objectsof the class type, and the functions perform operations that give
meaning to the data. Classeslet us separate implementation and interface. The interface
specifies the operations that the classsupports. Only the implementor of the class need
know or care about the details of the implementation. This separation reduces the
bookkeeping aspects that make programming tedious anderror-prone.
Class types often are referred to as abstract data types. An abstract data type treats the
data
the classd oes, rather than always having to be aware of how the class operates. Abstract
data types arefundamental to both object-oriented and generic programming.
Data abstraction is a programming
separation of interfaceand implementation. The class designer must worry about how a
class is implemented, but programmersthat use the class need not know about these details.
Instead, programmers who use a type need to know only the type's interface; they can think
abstractly about what the type does rather than concretely about how the type works.
Encapsulation is a term that describes the technique of combining lower-level
elements to forma new, higher-level entity. A function is one form of encapsulation: The
detailed actions performedby the function are encapsulated in the larger entity that is the
function itself. Encapsulated elements hide the details of their implementationwe may call
a function but have no access to the statements that it executes. In the same way, a class is
an encapsulated entity: It represents an aggregation of several members, and most
If we think about the library vector type, it is an example of both data abstraction and
2 / 12
.
encapsulation. It is abstract in that to use it, we think about its interfaceabout the operations
that it can perform. It is encapsulated because we have no access to the details of how the
type is representated nor to any of its implementation artifacts. An array, on the other hand,
is similar in concept to a vector but is neither abstract nor encapsulated. We manipulate an
array directly by accessing the memory in which the array is stored.
Not all types need to be abstract. The library pair class is a good example of a useful,
well-designed class that is concrete rather than abstract. A concrete class is a class that
exposes, rather than hides, its implementation.
Some classes, such as pair, really have no abstract interface. The pair type exists to
bundle two data members into a single object. There is no need or advantage to hiding the
data members. Hiding the members in a class like pair would only complicate the use of
the type.
Even so, such types often have member functions. In particular, it is a good idea for
any class that has data members of built-in or compound type to define constructor to
initialize those members. The user of the class could initialize or assign to the data
members but it is less error-prone for the class to do so.
Programmers tend to think about the people who will run their applications as "users."
Applicationsare designed for and evolve in response to feedback from those who ultimately
"use" the applications. Classes are thought of in a similar way: A class designer designs and
implements a class for "users" of that class. In this case, the "user" is a programmer, not the
ultimate user of the application.
Authors of successful applications do a good job of understanding and implementing
the needs ofthe application's users. Similarly, well-designed, useful classes are designed
with a close attention to the needs of the users of the class.
In another way, the division between class designer and class user reflects the division
betweenusers of an application and the designers and implementors of the application.
Users care only if the application meets their needs in a cost-effective way. Similarly, users
of a class care only about its interface. Good class designers define a class interface that is
intuitive and easy to use. Users care about the implementation only in so far as the
implementation affects their use of the class. If the implementation is too slow or puts
burdens on users of the class, then the users must care. In well-designed classes, only the
class designer worries about the implementation.
In simple applications, the user of a class and the designer of the class might be one
and the same person. Even in such cases, it is useful to keep the roles distinct. When
designing the interface to a class, the class designer should think about how easy it will be
to use the class. When using the class, the designer shouldn't think about how the class
works.
When referring to a "user," the context makes it clear which kind of user is meant. If
we speak of "user code" or the "user" of the Sales_item class, we mean a programmer who
3 / 12
.
is using a class in writing an application. If we speak of the "user" of the bookstore
application, we mean the manager of the store who is running the application.
Data abstraction and encapsulation provide two important advantages:
internals are protected from inadvertent user-level errors, which might corrupt
the state of the object.
class implementation may evolve over time in response to changing
requirements or bug reports without requiring change in user-level code.
By defining data members only in the private section of the class, the class author is
free to make changes in the data. If the implementation changes, only the class code needs
to be examined to see what affect the change may have. If data are public, then any
function that directly accesses the data members of the old representation might be broken.
It would be necessary to locate and rewrite all those portions of code that relied on the old
pesentation before the program could be used again.
Similarly, if the internal state of the class is private, then changes to the member data
can happen in only a limited number of places. The data is protected from mistakes that
users might introduce. If there is a bug that corrupts the object's state, the places to look for
the bug are localized: When data are private, only a member function could be responsible
for the error. The search for the mistake is limited, greatly easing the problems of
maintenance and program correctness.
If the data are private and if the interface to the member functions does not change,
then user functions that manipulate class objects require no change.
Because changing a class definition in a header file effectively changes the text of
every source file that includes that header, code that uses a class must be recompiled when
the class changes.
Classes are the most fundamental feature in C++. Classes let us define new types that
are tailored to our own applications, making our programs shorter and easier to modify.
Data abstractionthe ability to define both data and function membersand
encapsulationthe ability to protect class members from general accessare fundamental to
classes. Member functions define the interface to the class. We encapsulate the class by
making the data and functions used by the implementation of a class private.
Classes may define constructors, which are special member functions that control how
objects of the class are initialized. Constructors may be verloaded. Every constructor
should initialize every data member. Constructors should use a constructor initializer list to
initialize the data members. Initializer lists are lists of namevalue pairs where the name is a
member and the value is an initial value for that member.
Classes may grant access to their nonpublic members to other classes or functions. A
class grants access by making the class or function a friend.
Classes may also define mutable or static members. A mutable member is a data
member that is never const; its value may be changed inside a const member function. A
4 / 12
.
static member can be either function or data; static members exist independently of the
objects of the class type.
Copy Control
Each type, whether a built-in or class type, defines the meaning of a
set of operations on objects of that type. We can add two int values, run size on a vector,
and so on. These operations define what can be done with objects of the given type.
Each type also defines what happens when objects of the type are created.
Initialization of objects of class type is defined by constructors. Types also control what
happens when objects of the type are copied, assigned, or destroyed. Classes control these
actions through special member functions: the copy constructor, the assignment operator,
and the destructor. This chapter covers these operations.
When we define a new type, we specifyexplicitly or implicitlywhat happens when
objects of that type are copied, assigned, and destroyed. We do so by defining special
members: the copy constructor, the assignment operator, and the destructor. If we do not
explicitly define the copy constructor or the assignment operator, the compiler will
The copy constructor is a special constructor that has a single parameter that is a
we define a new object and initialize it from an object of the same type. It is used implicitly
when we pass or return objects of that type to or from functions.
Collectively, the copy constructor, assignment operator, and destructor are referred to
as copy control. The compiler automatically implements these operations, but the class may
define its own versions.
Copy control is an essential part of defining any C++ class. Programmers new to C++
are often confused by having to define what happens when objects are
copied, assigned, or destroyed. This confusion is compounded because if we do not
explicitly define these operations, the compiler defines them for usalthough they might not
behave as we intend.
Often the compiler-synthesized copy-control functions are finethey do exactly the
work that needs to be done. But for some classes, relying on the default definitions leads to
disaster. Frequently,the most difficult part of implementing the copy-control operations is
recognizing when we need to override the default versions. One especially common case
that requires the class to define its own the copy-control members is if the class has a
pointer member.
The Copy Constructor
The constructor that takes a single parameter that is a
object of the class type itself is called the copy constructor. Like the default constructor, the
copy constructor can be implicitly invoked by the compiler. The copy constructor is used
to:
5 / 12
.
itly or implicitly initialize one object from another of the same type;
an object to pass it as an argument to a function;
an object to return it from a function;
lize the elements in a sequential container;
lize elements in an array from a list of element initializers.
Forms of Object Definition
Recall that C++ supports two forms of initialization
-initialization uses the = symbol, and direct-initialization places the initializer in
parentheses.
The copy and direct forms of initialization, when applied to objects of class type, are
subtly different. Direct-initialization directly invokes the constructor matched by the
arguments. Copy-initialization always involves the copy constructor. Copy-initialization
first uses the indicated constructor to create a temporary object
uses the copy constructor to copy that temporary into the one we are creating:
string null_book = "9-999-99999-9"; // copy-initialization
string dots<10, '.'>; // direct-initialization
string empty_copy = string<>; // copy-initialization
string empty_direct; // direct-initialization
For objects of class type, copy-initialization can be used only when specifying a single
argument or when we explicitly build a temporary object to copy.
When dots is created, the string constructor that takes a count and a character is called
and directly initializes the members in dots. To create null_book, the compiler first creates
a temporary by invoking the string constructor that takes a C-style character string
parameter. The compiler then uses the string copy constructor to initialize null_book as a
copy of that temporary.
The initialization of empty_copy and empty_direct both call the string default
constructor. In the first case, the default constructor creates a temporary object, which is
then used by the copy constructor to initialize empty_copy. In the second case, the default
constructor is run directly on empty_direct.
The copy form of initialization is primarily supported for compatibility with C usage.
When it can do so, the compiler is permitted
constructor and create the object directly.
Usually the difference between direct- or copy-initialization is at most a matter of
low-level optimization. However, for types that do not support copying, or when using a
constructor that is nonexplicit
ifstream file1<"filename">; // ok: direct initialization
ifstream file2 = "filename"; // error: copy constructor is private
// This initialization is okay only if
// the Sales_item
6 / 12
.
Sales_item item = string<"9-999-99999-9">;
The initialization of file1 is fine. The ifstream class defines a constructor that can be
called with a C-style string. That constructor is used to initialize file1.
The seemingly equivalent initialization of file2 uses copy-initialization. That
definition is not okay. We cannot copy objects of the IO types
cannot use copy-initialization on objects of these types.
Whether the initialization of item is okay depends on which version of our Sales_item
class we are using. Some versions define the constructor that takes a string as explicit. If
the constructor is explicit, then the initialization fails. If the constructor is not explicit, then
the initialization is fine.
If a class does not define one or more of these operations, the compiler will define
them automatically. The synthesized operations perform memberwise initialization,
assignment, or destruction: Taking each member in turn, the synthesized operation does
whatever is appropriate to the member's type to copy, assign, or destroy that member. If the
member is a class type, the synthesized operation calls the corresponding operation for that
class destructor, etc.>. If the member is a built-in type or a pointer, the member is copied or assigned directly; the destructor does nothing to destroy members of built-in or pointer type. If the member is an array, the elements in the array are copied, assigned, or destroyed in a manner appropriate to the element type. 中文译文 类的简介 解决书店问题之前,还需要弄明白如何编写数据结构来表示交易数据.C++ 中我们通过定义类来定义自己的数据结构.类机制是 C++ 中最重要的特征之一.事实上,C++ 设计的主要焦点就是使所定义的类类型的行为可以像内置类型一样自然.我们前面已看到的像 istream 和 ostream 这样的库类型,都是定义为类的,也就是说,它们严格说来不是语言的一部分. 完全理解类机制需要掌握很多内容.所幸我们可以使用他人写的类而无需掌握如何定义自己的类.在这一节,我们将描述一个用于解决书店问题的简单类.当我们学习了更多关于类型、表达式、语句和函数的知识〔所有这些在类定义中都将用到〕后,将会在后面的章节实现这个类. 使用类时我们需要回答三个问题: 类的名字是什么? 它在哪里定义? 它支持什么操作? 对于书店问题,我们假定类命名为 Sales_item 且类定义在命名为 Sales_item.h 的头文件中. Sales_item 类 7 / 12 . Sales_item 类的目的是存储 ISBN 并保存该书的销售册数、销售收入和平均售价.我们不关心如何存储或计算这些数据.使用类时我们不需要知道这个类是怎样实现的,相反,我们需要知道的是该类提供什么操作. 正如我们所看到的,使用像 IO 一样的库工具,必须包含相关的头文件.类似地,对于自定义的类,必须使得编译器可以访问和类相关的定义.这几乎可以采用同样的方式.一般来说,我们将类定义放入一个文件中,要使用该类的任何程序都必须包含这个文件. 依据惯例,类类型存储在一个文件中,其文件名如同程序的源文件名一样,由文件名和文件后缀两部分组成.通常文件名和定义在头文件中的类名是一样的.通常后缀是 .h,但也有一些程序员用 .H、.hpp 或 .hxx.编译器通常并不挑剔头文件名,但 IDE 有时会.假设我们的类定义在名为 Sale_item.h 的文件中. Sales_item 对象上的操作 每个类定义一种类型,类型名与类名相同.因此,我们的 Sales_item 类定义了一种命名为 Sales_item 的类型.像使用内置类型一样,可以定义类类型的变量.当写下" Sales_item item",就表示 item 是类型 Sales_item 的一个对象.通常将"类型 Sales_item 的一个对象〞简称为"一个 Sales_item 对象〞,或者更简单地简称为"一个 Sales_item〞. 除了可以定义 Sales_item 类型的变量,我们还可以执行 Sales_item 对象的以下操作: 使用加法操作符,+,将两个 Sales_item 相加; 使用输入操作符,<<,来读取一个 Sales_item 对象; 使用输出操作符,>>,来输出一个 Sales_item 对象; 使用赋值操作符,=,将一个 Sales_item 对象赋值给另一个 Sales_item 对象; 调用 same_isbn 函数确定两个 Sales_item 是否指同一本书. 在大多数 C++ 程序中,类都是至关重要的:我们能够使用类来定义为要解决的问题定制的数据类型,从而得到更加易于编写和理解的应用程序.设计良好的类类型可以像内置类型一样容易使用. 类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态,而函数成员则负责执行赋予数据意义的操作.通过类我们能够将实现和接口分离,用接口指定类所支持的操作,而实现的细节只需类的实现者了解或关心.这种分离可以减少使编程冗长乏味和容易出错的那些繁琐工作. 类类型常被称为抽象数据类型〔abstract data types〕.抽象数据类型将数据〔即状态〕和作用于状态的操作视为一个单元.我们可以抽象地考虑类该做什么,而无须知道何去完成这些操作.抽象数据类型是面向对象编程和泛型编程的基础. 数据抽象是一种依赖于接口和实现分离的编程〔和设计〕技术.类设计者必须关心类是如何实现的,但使用该类的程序员不必了解这些细节.相反,使用一个类型的程序员仅需了解类型的接口,他们可以抽象地考虑该类型做什么,而不必具体地考虑该类型如何工作. 8 / 12 . 封装是一项低层次的元素组合起来的形成新的、高层次实体珠技术.函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中.被封装的元素隐藏了它们的实现细节——可以调用一个函数但不能访问它所执行的语句.同样地,类也是一个封装的实体:它代表若干成员的聚焦,大多数〔良好设计的〕类类型隐藏了实现该类型的成员. 标准库类型 vector 同时具备数据抽象和封装的特性.在使用方面它是抽象的,只需考虑它的接口,即它能执行的操作.它又是封装的,因为我们既无法了解该类型如何表示的细节,也无法访问其任意的实现制品.另一方面,数组在概念上类似于 vector,但既不是抽象的,也不是封装的.可以通过访问存放数组的内存来直接操纵数组. 并非所有类型都必须是抽象的.标准库中的 pair 类就是一个实用的、设计良好的具体类而不是抽象类.具体类会暴露而非隐藏其实现细节. 一些类,例如 pair,确实没有抽象接口.pair 类型只是将两个数据成员捆绑成单个对象.在这种情况下,隐藏数据成员没有必要也没有明显的好处.在像 pair 这样的类中隐藏数据成员只会造成类型使用的复杂化. 尽管如此,这样的类型通常还是有成员函数.特别地,如果类具有内置类型或复合类型数据成员,那么定义构造函数来初始化这些成员就是一个好主意.类的使用都也可以初始化或赋值数据成员,但由类来做更不易出错. 程序员经常会将运行应用程序的人看作"用户〞.应用程序为最终"使用〞它的用户而设计,并响应用户的反馈而完善.类也类似:类的设计者为类的"用户〞设计并实现类.在这种情况下,"用户〞是程序员,而不是应用程序的最终用户. 成功的应用程序的创建者会很好地理解和实现用户的需求.同样地,良好设计的、实用的类,其设计也要贴近类用户的需求. 另一方面,类的设计者与实现者之间的区别,也反映了应用程序的用户与设计和实现者之间的区分.用户只关心应用程序能否以合理的费用满足他们的需求.同样地,类的使用者只关心它的接口.好的类设计者会定义直观和易用的类接口,而使用者只关心类中影响他们使用的部分实现.如果类的实现速度太慢或给类的使用者加上负担,则必然引起使用者的关注.在良好设计的类中,只有类的设计者会关心实现. 在简单的应用程序中,类的使用者和设计者也许是同一个人.即使在这种情况下,保持角色区分也是有益的.设计类的接口时,设计者应该考虑的是如何方便类的使用;使用类的时候,设计者就不应该考虑类如何工作. 提到"用户〞时,应该由上下文清楚地标明所指的是哪类用户.如果提到"用户代码〞或 Sales_item 类的〞用户",指的就是使用类编写应用程序的程序员.如果提到书店应用程序的〞用户",那么指的是运行应用程序的书店管理人员. 数据抽象和封装提供了两个重要优点: 1.避免类内部出现无意的、可能破坏对象状态的用户级错误. 2.随时间推移可以根据需求改变或缺陷〔bug〕报告来完美类实现,而无须改变用户级代码. 仅在类的私有部分定义数据成员,类的设计者就可以自由地修改数据.如果实现9 / 12 . 改变了,那么只需检查类代码来了解此变化可能造成的影响.如果数据为仅有的,则任何直接访问原有数据成员的函数都可能遭到破坏.在程序可重新使用之前,有必要定位和重写依赖原有表示的那部分代码. 同样地,如果类的内部状态是私有的,则数据成员的改变只可能在有限的地方发生.避免数据中出现用户可能引入的错误.如果有缺陷会破坏对象的状态,就在局部位置搜寻缺陷:如果数据是私有的,那么只有成员函数可能对该错误负责.对错误的搜寻是有限的,从而大大方便了程序的维护和修正. 如果数据是私有的并且没有改变成员函数的接口,则操纵类对象的用户函数无须改变. 改变头文件中的类定义可有效地改变包含该头文件的每个源文件的程序文本,所以,当类发生改变时,使用该类的代码必须重新编译. 类是 C++ 中最基本的特征,允许定义新的类型以适应应用程序的需要,同时使程序更短且更易于修改. 数据抽象是指定义数据和函数成员的能力,而封装是指从常规访问中保护类成员的能力,它们都是类的基础.成员函数定义类的接口.通过将类的实现所用到的数据和函数设置为 private 来封装类. 类可以定义构造函数,它们是特殊的成员函数,控制如何初始化类的对象.可以重载构造函数.每个构造函数就初始化每个数据成员.初始化列表包含的是名—值对,其中的名是一个成员,而值则是该成员的初始值. 类可以将对其非 public 成员的访问权授予其他类或函数,并通过将其他的类或函数设为友元来授予其访问权. 类也可以定义 mutable 或 static 成员.mutable 成员永远都不能为 const;它的值可以在 const 成员函数中修改.static 成员可以是函数或数据,独立于类类型的对象而存在. 拷贝控制 每种类型,无论是内置类型还是类类型,都对该类型对象的一组〔可能为空的〕操作的含义进行了定义.比如,我们可以将两个 int 值相加,运行 vector 对象的 size 操作,等等.这些操作定义了用给定类型的对象可以完成什么任务. 每种类型还定义了创建该类型的对象时会发生什么——构造函数定义了该类类型对象的初始化.类型还能控制复制、赋值或撤销该类型的对象时会发生什么——类通过特殊的成员函数:复制构造函数、赋值操作符和析构函数来控制这些行为.本章将介绍这些操作. 当定义一个新类型的时候,需要显式或隐式地指定复制、赋值和撤销该类型的对象时会发生什么——这是通过定义特殊成员:复制构造函数、赋值操作符和析构函数来达到的.如果没有显式定义复制构造函数或赋值操作符,编译器〔通常〕会为我们定义. 拷贝构造函数是一种特殊构造函数,具有单个形参,该形参〔常用 const 修饰〕是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数.当将该类型的对象传递给函数或函数返回该类型的对象时,10 / 12 . 将隐式使用复制构造函数. 复制构造函数、赋值操作符和析构函数总称为复制控制.编译器自动实现这些操作,但类也可以定义自己的版本. 复制控制是定义任意 C++ 类必不可少的部分.初学 C++ 的程序员常对必须定义在复制、赋值或撤销对象时发生什么感到困惑.因为如果我们没有显式定义这些操作,编译器将为我们定义它们〔尽管它们也许不像我们期望的那样工作〕,这往往使初学者更加困惑. 通常,编译器合成的复制控制函数是非常精练的——它们只做必需的工作.但对某些类而言,依赖于默认定义会导致灾难.实现复制控制操作最困难的部分,往往在于识别何时需要覆盖默认版本.有一种特别常见的情况需要类定义自己的复制控制成员的:类具有指针成员. 拷贝构造函数 只有单个形参,而且该形参是对本类类型对象的引用〔常用 const 修饰〕,这样的构造函数称为复制构造函数.与默认构造函数一样,复制构造函数可由编译器隐式调用.复制构造函数可用于: 1.根据另一个同类型的对象显式或隐式初始化一个对象. 2.复制一个对象,将它作为实参传给一个函数. 3.从函数返回时复制一个对象. 4.初始化顺序容器中的元素. 5.根据元素初始化式列表初始化数组元素. 对象的定义形式 回忆一下,C++ 支持两种初始化形式〔第 节〕:直接初始化和复制初始化.复制初始化使用 = 符号,而直接初始化将初始化式放在圆括号中. 当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数.复制初始化首先使用指定构造函数创建一个临时对象〔第 节〕,然后用复制构造函数将那个临时对象复制到正在创建的对象: string null_book = "9-999-99999-9"; // copy-initialization string dots<10, '.'>; // direct-initialization string empty_copy = string<>; // copy-initialization string empty_direct; // direct-initialization 对于类类型对象,只有指定单个实参或显式创建一个临时对象用于复制时,才使用复制初始化. 创建 dots 时,调用参数为一个数量和一个字符的 string 构造函数并直接初始化 dots 的成员.创建 null_book 时,编译器首先调用接受一个 C 风格字符串形参的 string 构造函数,创建一个临时对象,然后,编译器使用 string 复制构造函数将 null_book 初始化为那个临时对象的副本. empty_copy 和 empty_direct 的初始化都调用默认构造函数.对前者初始化时,默认构造函数函数创建一个临时对象,然后复制构造函数用该对象初始化 empty_copy.对后者初始化时,直接运行 empty_direct 的默认构造函数. 11 / 12 . 支持初始化的复制形式主要是为了与 C 的用法兼容.当情况许可时,可以允许编译器跳过复制构造函数直接创建对象,但编译器没有义务这样做. 通常直接初始化和复制初始化仅在低级别上存在差异.然而,对于不支持复制的类型,或者使用非 explicit构造函数〔第 节〕的进修,它们有本质区别: ifstream file1<"filename">; // ok: direct initialization ifstream file2 = "filename"; // error: copy constructor is private // This initialization is okay only if // the Sales_item Sales_item item = string<"9-999-99999-9">; file1 的初始化是正确的.ifstream 类定义了一个可用 C 风格字符串调用的构造函数,使用该构造函数初始化 file1. 看上去等效的 file2 初始化使用复制初始化,但该定义不正确.由于不能复制 IO 类型的对象〔第 8.1 节〕,所以不能对那些类型的对象使用复制初始化. item 的初始化是否正确,取决于正在使用哪个版本的 Sales_item 类.某些版本将参数为一个 string 的构造函数定义为 explicit.如果构造函数是显式的,则初始化失败;如果构造函数不是显式的,则初始化成功. 如果类没有定义这些操作中的一个或多个,编译器将自动定义它们.合成操作执行逐个成员初始化、赋值或撤销:合成操作依次取得每个成员,根据成员类型进行成员的复制、赋值或撤销.如果成员为类类型的,合成操作调用该类的相应操作〔即,复制构造函数调用成员的复制构造函数,析构函数调用成员的析构函数,等等〕.如果成员为内置类型或指针,则直接复制或赋值,析构函数对撤销内置类型或指针类型的成员没有影响.如果成员为数组,则根据元素类型以适当方式复制、赋值或撤销数组中的元素. 12 / 12
版权声明:本文标题:毕业论文5000字英文文献翻译(c++) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1704883038h465617.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论