Class Constructors and Destructors [类的构造函数和析构函数]
- 1. Declaring and Defining Constructors (声明和定义构造函数)
- 2. Using Constructors (使用构造函数)
- 3. Default Constructors (默认构造函数)
- 4. Destructors (析构函数)
- 5. Improving the `Stock` Class (改进 Stock 类)
- 5.1. The Header File (头文件)
- 5.2. The Implementation File (实现文件)
- 5.3. A Client File (客户文件)
- 5.4. Program Notes (程序说明)
- 5.5. C++11 List Initialization (C++11 列表初始化)
- 5.6. `const` Member Functions (`const` 成员函数)
- 5.7. Constructors and Destructors in Review (构造函数和析构函数小结)
- References
There are certain standard functions, called constructors
and destructors
, that you should normally provide for a class
.
类的构造函数和析构函数是类的标准函数。
int year = 2022; // Valid initialization
struct Thing
{
char * ptr;
int num;
};
Thing forever = {"widget", 99}; // Valid initialization
Stock strong = {"NVIDIA Corporation", 200, 50.25}; // Compilation failed
The reason you can’t initialize a Stock
object this way is because the data parts have private
access status, which means a program cannot access the data members directly. As you’ve seen, the only way a program can access the data members is through a member function. Therefore, you need to devise an appropriate member function if you’re to succeed in initializing an object. You could initialize a class
object as just shown if you made the data members public
instead of private
, but making the data public
goes against one of the main justifications for using classes: data hiding.
数据部分的访问状态是私有的,程序不能直接访问数据成员。程序只能通过成员函数来访问数据成员,因此需要设计合适的成员函数,才能成功地将对象初始化。如果使数据成员成为公有,而不是私有,就可以按刚才介绍的方法初始化类对象,但使数据成为公有的违背了类的一个主要初衷:数据隐藏 。
In general, it’s best that all objects be initialized when they are created.
一般来说,最好是在创建对象时对它进行初始化。
C++ provides for special member functions, called class constructors, especially for constructing new objects and assigning values to their data members. The name is the same as the class name. For example, a possible constructor for the Stock
class is a member function called Stock()
. The constructor prototype and header have an interesting property: Although the constructor has no return value, it’s not declared type void
. In fact, a constructor has no declared type.
C++ 提供了一个特殊的成员函数 - 类构造函数,专门用于构造新对象、将值赋给它们的数据成员。类构造函数的名称与类名相同。例如,Stock
类一个可能的构造函数是名为 Stock()
的成员函数。构造函数的原型和函数头没有返回值,但没有被声明为 void
类型。实际上,构造函数没有声明类型。
1. Declaring and Defining Constructors (声明和定义构造函数)
// constructor prototype with some default arguments
Stock(const std::string &company, const long num = 0, const double price = 0.0);
The first argument is a pointer to the string
that is used to initialize the company string
member.The num
and price
arguments provide values for the shares_
and share_val_
members. Note that there is no return type. The prototype goes in the public section of the class declaration.
第一个参数是指向字符串的指针,该字符串用于初始化成员 company_
。num
and price
参数为 shares_
and share_val_
成员提供值。注意,没有返回类型。 原型位于类声明的公有部分。
Stock::Stock(const std::string &company, const long num, const double price) {
std::cout << "Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): " << company << "\n";
company_ = company;
if (num < 0) {
std::cout << "Number of shares_ can't be negative; " << company_ << " shares_ set to 0.\n";
shares_ = 0;
}
else {
shares_ = num;
}
share_val_ = price;
SetTot();
}
A program automatically invokes the constructor when it declares an object.
程序声明对象时,将自动调用构造函数。
1. Member Names and Parameter Names
The constructor arguments don’t represent the class members; they represent values that are assigned to the class members. Thus, they must have distinct names.
构造函数的参数表示的不是类成员,而是赋给类成员的值。因此,参数名尽量与类成员名不同。
One common coding practice to help avoid such confusion is to use an m_
prefix to identify data member names (一种常见的做法是在数据成员名中使用 m_
前缀):
class Stock
{
private:
string m_company;
long m_shares;
...
Another common practice is to use an underbar suffix for member names (另一种常见的做法是,在成员名中使用后缀 _
):
class Stock
{
private:
string company_;
long shares_;
...
2. Using Constructors (使用构造函数)
C++ provides two ways to initialize an object by using a constructor.
C++ 提供了两种使用构造函数来初始化对象的方式。
The first is to call the constructor explicitly (显式地调用构造函数):
Stock yong = Stock("Advanced Micro Devices, Inc.", 250, 1.25);
This sets the company member of the yong
object to the string “Advanced Micro Devices, Inc.”, the shares_
member to 250, and so on.
The second way is to call the constructor implicitly (隐式地调用构造函数):
Stock qiang("Intel Corporation", 50, 2.5);
This more compact form is equivalent to the following explicit call (这种格式更紧凑,它与下面的显式调用等价):
Stock cheng = Stock("Apple Inc.", 50, 2.5));
C++ uses a class constructor whenever you create an object of that class, even when you use new
for dynamic memory allocation.
每次创建类对象 (甚至使用 new
动态分配内存) 时,C++ 都使用类构造函数。
Here’s how to use the constructor with new
:
Stock *tien_ptr = new Stock("Samsung Electronics H.K. Co., Ltd.", 18, 19.0);
This statement creates a Stock
object, initializes it to the values provided by the arguments, and assigns the address of the object to the tien_ptr
pointer. In this case, the object doesn’t have a name, but you can use the pointer to manage the object.
这条语句创建一个 Stock
对象,将其初始化为参数提供的值,并将该对象的地址赋给 tien_ptr
指针。在这种情况下,对象没有名称,但可以使用指针来管理该对象。
Constructors are used differently from the other class methods.
构造函数的使用方式不同于其他类方法。
Normally, you use an object to invoke a method:
qiang.show(); // qiang object invokes show() method
However, you can’t use an object to invoke a constructor because until the constructor finishes its work of making the object, there is no object. Rather than being invoked by an object, the constructor is used to create the object.
但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象,而不能通过对象来调用。
3. Default Constructors (默认构造函数)
A default constructor
is a constructor that is used to create an object when you don’t provide explicit initialization values.
默认构造函数是在未提供显式初始值时,用来创建对象的构造函数。
That is, it’s a constructor used for declarations like this:
Stock qiang; // uses the default constructor
The reason this statement works is that if you fail to provide any constructors, C++ automatically supplies a default constructor. It’s an implicit version of a default constructor, and it does nothing.
如果没有提供任何构造函数,则 C++ 将自动提供默认构造函数。它是默认构造函数的隐式版本,不做任何工作。
For the Stock
class, the default constructor would look like this (默认构造函数可能如下):
Stock::Stock() { }
The net result is that the qiang
object is created with its members uninitialized, just as the following creates val
without providing a value for val
.
因此将创建 qiang
对象,但不初始化其成员,这和下面的语句创建 val
,但没有提供值给它一样。
int val;
The fact that the default constructor has no arguments reflects the fact that no values appear in the declaration.
默认构造函数没有参数,因为声明中不包含值。
A curious fact about default constructors is that the compiler provides one only if you don’t define any constructors. After you define any constructor for a class, the responsibility for providing a default constructor for that class passes from the compiler to you.
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,程序员就必须为它提供默认构造函数。
If you provide a nondefault constructor, such as Stock(const std::string &company, const long num = 0, const double price = 0.0);
, and don’t provide your own version of a default constructor, then a declaration like this becomes an error.
如果提供了非默认构造函数 Stock(const std::string &company, const long num = 0, const double price = 0.0);
,但没有提供默认构造函数,则下面的声明将出错。
Stock stock1; // not possible with current constructor
The reason for this behavior is that you might want to make it impossible to create uninitialized objects. If, however, you wish to create objects without explicit initialization, you must define your own default constructor.This is a constructor that takes no arguments. You can define a default constructor two ways. One is to provide default values for all the arguments to the existing constructor.
这样做的原因可能是想禁止创建未初始化的对象。然而,如果要创建对象,而不显式地初始化,则必须定义一个不接受任何参数的默认构造函数。定义默认构造函数的方式有两种,第一种是给已有构造函数的所有参数提供默认值。
Stock(const std::string &company = "Error", const long num = 0, const double price = 0.0);
The second is to use function overloading to define a second constructor, one that has no arguments.
另一种方式是通过函数重载来定义另一个构造函数 - 一个没有参数的构造函数。
Stock();
You can have only one default constructor, so be sure that you don’t do both. Actually, you should usually initialize objects in order to ensure that all members begin with known, reasonable values. Thus, a user-provided default constructor typically provides implicit initialization for all member values.
由于只能有一个默认构造函数,因此不要同时采用这两种方式。实际上,通常应初始化所有的对象,以确保所有成员一开始就有已知的合理值。因此,用户定义的默认构造函数通常给所有成员提供隐式初始值。
For example, this is how you might define one for the Stock
class:
// Default constructor
Stock::Stock() {
std::cout << "Stock::Stock()" << "\n";
company_ = "default name";
shares_ = 0;
share_val_ = 0.0;
total_val_ = 0.0;
}
When you design a class, you should usually provide a default constructor that implicitly initializes all class members.
在设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数。
After you’ve used either method (no arguments or default values for all arguments) to create the default constructor, you can declare object variables without initializing them explicitly.
使用上述任何一种方式 (没有参数或所有参数都有默认值) 创建了默认构造函数后,便可以声明对象变量,而不对它们进行显式初始化。
Stock first; // calls default constructor implicitly
Stock second = Stock(); // calls it explicitly
Stock *stock_ptr = new Stock; // calls it implicitly
However, you shouldn’t be misled by the implicit form of the nondefault constructor.
然而,不要被非默认构造函数的隐式形式所误导。
Stock first("Concrete Conglomerate"); // calls constructor
Stock second(); // declares a function
Stock third; // calls default constructor
The first declaration here calls the nondefault constructor, that is, the one that takes arguments. The second declaration states that second()
is a function that returns a Stock
object. When you implicitly call the default constructor, you don’t use parentheses.
第一个声明调用非默认构造函数,即接受参数的构造函数。第二个声明指出,second()
是一个返回 Stock
对象的函数。隐式地调用默认构造函数时,不要使用圆括号。
4. Destructors (析构函数)
When you use a constructor to create an object, the program undertakes the responsibility of tracking that object until it expires. At that time, the program automatically calls a special member function bearing the formidable title destructor. The destructor should clean up any debris, so it actually serves a useful purpose. For example, if your constructor uses new
to allocate memory, the destructor should use delete
to free that memory. The Stock constructor doesn’t do anything fancy like using new
, so the Stock
class destructor doesn’t really have any tasks to perform. In such a case, you can simply let the compiler generate an implicit, do-nothing destructor, which is exactly what the first version of the Stock
class does. On the other hand, it’s certainly worth looking into how to declare and define destructors, so let’s provide one for the Stock
class.
用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数,该函数的名称令人生畏 - 析构函数。析构函数完成清理工作,因此实际上很有用。例如,如果构造函数使用 new
来分配内存,则析构函数将使用 delete
来释放这些内存。Stock
的构造函数没有使用 new
,因此析构函数实际上没有需要完成的任务。在这种情况下,只需让编译器生成一个什么都不做的隐式析构函数即可。
formidable [ˈfɔː(r)mɪdəb(ə)l]:adj. 可怕的,令人敬畏的,难对付的
debris [ˈdebriː]:n. 碎片,残骸,破片,残渣
Like a constructor, a destructor has a special name: It is formed from the class name preceded by a tilde (~
). Thus, the destructor for the Stock
class is called ~Stock()
. Also like a constructor, a destructor can have no return value and has no declared type. Unlike a constructor, a destructor must have no arguments.Thus, the prototype for a Stock destructor must be this.
和构造函数一样,析构函数的名称也很特殊:在类名前加上 ~
。因此,Stock
类的析构函数为 ~Stock()
。和构造函数一样,析构函数也可以没有返回值和声明类型。与构造函数不同的是,析构函数没有参数,因此 Stock
析构函数的原型必须是这样的。
~Stock();
Because a Stock
destructor has no vital duties, you can code it as a do-nothing function:
Stock::~Stock()
{ }
However, just so that you can see when the destructor is called, let’s code it this way:
// Destructor
Stock::~Stock() {
std::cout << "Stock::~Stock(): " << company_ << "\n";
}
Normally your code shouldn’t explicitly call a destructor. If you create a static storage class object, its destructor is called automatically when the program terminates. If you create an automatic storage class object, as the examples have been doing, its destructor is called automatically when the program exits the block of code in which the object is defined. If the object is created by using new, it resides in heap memory, or the free store, and its destructor is called automatically when you use delete to free the memory. Finally, a program can create temporary objects to carry out certain operations; in that case, the program automatically calls the destructor for the object when it has finished using it.
通常不应在代码中显式地调用析构函数。如果创建的是静态存储类对象,则其析构函数将在程序结束时自动被调用。如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块时 (该对象是在其中定义的) 自动被调用。如果对象是通过 new
创建的,则它将驻留在栈内存或自由存储区中,当使用 delete
来释放内存时,其析构函数将自动被调用。最后,程序可以创建临时对象来完成特定的操作,在这种情况下,程序将在结束对该对象的使用时自动调用其析构函数。
Because a destructor is called automatically when a class object expires, there ought to be a destructor. If you don’t provide one, the compiler implicitly declares a default constructor and, if it detects code that leads to the destruction of an object, it provides a definition for the destructor.
由于在类对象过期时析构函数将自动被调用,因此必须有一个析构函数。如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数的定义。
ought [ɔːt]:auxv. 应该,本应,总应该,早应该
5. Improving the Stock
Class (改进 Stock 类)
5.1. The Header File (头文件)
The file also uses the #ifndef
technique to protect against multiple inclusion of this file.
#ifndef
技术来防止多重包含。
- stock.h
#ifndef STOCK_H_
#define STOCK_H_
#include <string>
// class declaration
class Stock {
private:
std::string company_;
int shares_;
double share_val_;
double total_val_;
void SetTot() { total_val_ = shares_ * share_val_; }
public:
Stock(); // Default constructor
Stock(const std::string &company, const long num = 0, const double price = 0.0);
~Stock();
void Buy(const long num, const double price);
void Sell(const long num, const double price);
void Update(const double price);
void Show()const;
const Stock &TopVal(const Stock &stock) const;
}; // Note semicolon at the end
#endif
5.2. The Implementation File (实现文件)
It includes the stock.h
file in order to provide the class declaration. Recall that enclosing the filename in double quotation marks instead of in brackets causes the compiler to search for it at the same location where your source files are located.
它包含了文件 stock.h
,以提供类声明,将文件名放在双引号而不是方括号中意味着编译器将源文件所在的目录中搜索它。
- stock.cpp
#include <iostream>
#include "stock.h"
// Default constructor
Stock::Stock() {
std::cout << "Stock::Stock()" << "\n";
company_ = "default name";
shares_ = 0;
share_val_ = 0.0;
total_val_ = 0.0;
}
Stock::Stock(const std::string &company, const long num, const double price) {
std::cout << "Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): " << company << "\n";
company_ = company;
if (num < 0) {
std::cout << "Number of shares_ can't be negative; " << company_ << " shares_ set to 0.\n";
shares_ = 0;
}
else {
shares_ = num;
}
share_val_ = price;
SetTot();
}
// Destructor
Stock::~Stock() {
std::cout << "Stock::~Stock(): " << company_ << "\n";
}
void Stock::Buy(const long num, const double price) {
if (num < 0) {
std::cout << "Number of shares_ purchased can't be negative. " << "Transaction is aborted.\n";
}
else {
shares_ += num;
share_val_ = price;
SetTot();
}
}
void Stock::Sell(const long num, const double price) {
if (num < 0) {
std::cout << "Number of shares_ sold can't be negative. " << "Transaction is aborted.\n";
}
else if (num > shares_) {
std::cout << "You can't sell more than you have! " << "Transaction is aborted.\n";
}
else {
shares_ -= num;
share_val_ = price;
SetTot();
}
}
void Stock::Update(const double price) {
share_val_ = price;
SetTot();
}
void Stock::Show() const {
// set format to #.###
std::ios_base::fmtflags original = std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::streamsize precision = std::cout.precision(3);
std::cout << "Company: " << company_ << "\n Shares = " << shares_;
std::cout << " Share Price = $" << share_val_;
// set format to #.##
std::cout.precision(2);
std::cout << " Total Worth = $" << total_val_ << '\n';
// restore original format
std::cout.setf(original, std::ios_base::floatfield);
std::cout.precision(precision);
}
const Stock & Stock::TopVal(const Stock & stock) const {
if (stock.total_val_ > total_val_) {
return stock; // argument object
}
else {
return *this; // invoking object
}
}
5.3. A Client File (客户文件)
Because it simply uses the Stock
class, this listing is a client of the Stock
class. Like stock.cpp
, it includes the stock.h
file to provide the class declaration.
由于它只是使用 Stock
类,因此是 Stock
类的客户。和 stock.cpp
一样,它也包含了文件 stock.h
以提供类声明。
- sample.cpp
#include <iostream>
#include "stock.h"
int main() {
{
std::cout << "Using constructors to create new objects\n";
Stock stock1("first", 12, 20.0); // syntax 1
stock1.Show();
Stock stock2 = Stock("second", 2, 2.0); // syntax 2
stock2.Show();
std::cout << "\nAssigning stock1 to stock2\n";
stock2 = stock1;
std::cout << "Listing stock1 and stock2:\n";
stock1.Show();
stock2.Show();
std::cout << "\nUsing a constructor to reset an object\n";
stock1 = Stock("third", 10, 50.0); // temp object
std::cout << "\nRevised stock1:\n";
stock1.Show();
std::cout << "Done\n";
}
return 0;
}
Using constructors to create new objects
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): first
Company: first
Shares = 12 Share Price = $20.000 Total Worth = $240.00
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): second
Company: second
Shares = 2 Share Price = $2.000 Total Worth = $4.00
Assigning stock1 to stock2
Listing stock1 and stock2:
Company: first
Shares = 12 Share Price = $20.000 Total Worth = $240.00
Company: first
Shares = 12 Share Price = $20.000 Total Worth = $240.00
Using a constructor to reset an object
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): third
Stock::~Stock(): third
Revised stock1:
Company: third
Shares = 10 Share Price = $50.000 Total Worth = $500.00
Done
Stock::~Stock(): first
Stock::~Stock(): third
请按任意键继续. . .
Some compilers may produce a program with the following initial output, which has one additional line:
Using constructors to create new objects
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): first
Company: first
Shares = 12 Share Price = $20.000 Total Worth = $240.00
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): second
Stock::~Stock(): second << additional line
Company: second
Shares = 2 Share Price = $2.000 Total Worth = $4.00
...
Automatic variables such as stock1
and stock2
expire when the program exits the block that contains their definitions. Without the extra braces, that block would be the body of main()
, so the destructors would not be called until after main()
completed execution. In a windowing environment, this would mean that the window would close before the last two destructor calls, preventing you from seeing the last two messages. But with the braces, the last two destructor calls occur before the return statement is reached, so the messages are displayed.
main()
的开头和末尾多了一个大括号。诸如 stock1
和 stock2
等自动变量将在程序退出其定义所属代码块时消失。如果没有这些大括号,代码块将为整个 main()
,因此仅当 main()
执行完毕后,才会调用析构函数。在窗口环境中, 这意味着将在两个析构函数调用前关闭,导致您无法看到最后两条消息。但添加这些大括号后,最后两个析构函数调用将在到达返回语句前执行,从而显示相应的消息。
5.4. Program Notes (程序说明)
The statement
Stock stock1("first", 12, 20.0); // syntax 1
creates a Stock
object called stock1
and initializes its data members to the indicated values.
The following statement uses another syntax to create and initialize an object called stock2
:
Stock stock2 = Stock("second", 2, 2.0); // syntax 2
The C++ Standard gives a compiler a couple ways to execute this second syntax.
C++ 标准允许编译器使用两种方式来执行第二种语法。
- One is to make it behave exactly like the first syntax.
一种是使其行为和第一种语法完全相同。 - The second way is to allow the call to the constructor to create a temporary object that is then copied to
stock2
.Then the temporary object is discarded. If the compiler uses this option, the destructor is called for the temporary object, producing this output instead:Stock::~Stock(): second
另一种方式是允许调用构造函数来创建一个临时对象,然后将该临时对象复制到stock2
中,并丢弃它。如果编译器使用的是这种方式,则将为临时对象调用析构函数,因此生成下面的输出:Stock::~Stock(): second
The compiler that produced this output disposed of the temporary object immediately, but it’s possible that a compiler might wait longer, in which case the destructor message would be displayed later.
生成上述输出的编译器可能立刻删除临时对象,但也可能会等一段时间,在这种情况下,析构函数的消息将会过一段时间才显示。
The following statement illustrates that you can assign one object to another of the same type:
stock2 = stock1; // object assignment
As with structure assignment, class
object assignment, by default, copies the members of one object to the other. In this case, the original contents of stock2
are overwritten.
与给结构赋值一样,在默认情况下,给类对象赋值时,将把一个对象的成员复制给另一个。在这个例子中,stock2
原来的内容将被覆盖。
When you assign one object to another of the same class, by default C++ copies the contents of each data member of the source object to the corresponding data member of the target object.
在默认情况下,将一个对象赋给同类型的另一个对象时,C++ 将源对象的每个数据成员的内容复制到目标对象中相应的数据成员中。
For example, the program has this statement in main()
:
stock1 = Stock("third", 10, 50.0);
The stock1
object already exists. Therefore, instead of initializing stock1
, this statement assigns new values to the object. It does so by having the constructor create a new, temporary object and then copying the contents of the new object to stock1
. Then the program disposes of the temporary object, invoking the destructor as it does so, as illustrated by the following annotated output.
stock1
对象已经存在,因此这条语句不是对 stock1
进行初始化,而是将新值赋给它。这是通过让构造程序创建一个新的、临时的对象,然后将其内容复制给 stock1
来实现的。随后程序调用析构函数,以删除该临时对象。
Stock::~Stock(): second << additional line
Some compilers might dispose of the temporary object later, delaying the destructor call.
有些编译器可能要过一段时间才删除临时对象,因此析构函数的调用将延迟。
Finally, at the end, the program displays this:
Done
Stock::~Stock(): first
Stock::~Stock(): third
When the main()
function terminates, its local variables (stock1
and stock2
) pass from your plane of existence. Because such automatic variables go on the stack, the last object created is the first deleted, and the first created is the last deleted.
函数 main()
结束时,其局部变量 (stock1
and stock2
) 将消失。由于这种自动变量被放在栈中,因此最后创建的对象将最先被删除,最先创建的对象将最后被删除
The output points out that there is a fundamental difference between the following two statements:
Stock stock2 = Stock("second", 2, 2.0); // syntax 2
stock1 = Stock("third", 10, 50.0); // temp object
The first of these statements invokes initialization; it creates an object with the indicated value, and it may or may not create a temporary object. The second statement invokes assignment. Using a constructor in an assignment statement in this fashion always causes the creation of a temporary object before assignment occurs.
第一条语句是初始化,它创建有指定值的对象,可能会创建临时对象 (也可能不会)。第二条语句是赋值,像这样在赋值语句中使用构造函数总会导致在赋值前创建一个临时对象。
If you can set object values either through initialization or by assignment, choose initialization. It is usually more efficient.
如果既可以通过初始化,也可以通过赋值来设置对象的值,则应采用初始化方式。通常这种方式的效率更高。
5.5. C++11 List Initialization (C++11 列表初始化)
With C++11, can you use the list-initialization syntax with classes? Yes, you can, providing the brace contents match the argument list of a constructor:
Stock yong = {"first", 100, 45.0};
Stock qiang {"second"};
Stock cheng {};
The braced lists in the first two declarations match the following constructor:
Stock(const std::string &company, const long num = 0, const double price = 0.0);
Therefore, that constructor will be used to create the two objects. For Stock qiang {"second"};
, the default values of 0 and 0.0 will be used for the second and third arguments.The third declaration matches the default constructor, so Stock cheng {};
is constructed using it.
创建对象 Stock qiang {"second"};
时,第二和第三个参数将为默认值 0 和 0.0。第三个声明与默认构造函数匹配,因此将使用该构造函数创建对象 Stock cheng {};
。
In addition, C++11 offers a class called std::initializer_list
that can be used as a type for a function or method parameter.This class can represent a list of arbitrary length, providing all the entries are of the same type or can be converted to the same type.
另外,C++11 还提供了名为 std::initializer_list
的类,可将其用作函数参数或方法参数的类型。这个类可表示任意长度的列表,只要所有列表项的类型都相同或可转换为相同的类型。
5.6. const
Member Functions (const
成员函数)
Consider the following code snippet:
const Stock yongqiang = Stock("NVIDIA Corporation");
yongqiang.Show();
The Show()
method doesn’t have any arguments for const to qualify. Instead, the object it uses is provided implicitly by the method invocation.What is needed is a new syntax, one that says a function promises not to modify the invoking object. The C++ solution is to place the const
keyword after the function parentheses.
Show()
方法没有任何参数。相反,它所使用的对象是由方法调用隐式地提供的。需要一种新的语法保证函数不会修改调用对象。C++ 的解决方法是将 const
关键字放在函数的括号后面。
That is, the Show()
declaration should look like this:
void Show()const; // promises not to change invoking object
Similarly, the beginning of the function definition should look like this:
void Stock::Show() const // promises not to change invoking object
Class functions declared and defined this way are called const
member functions. Just as you should use const
references and pointers as formal function arguments whenever appropriate, you should make class methods const
whenever they don’t modify the invoking object.
以这种方式声明和定义的类函数被称为 const
成员函数。 就像应尽可能将 const
引用和指针用作函数形参一样,只要类方法不修改调用对象,就应将其声明为 const
。
5.7. Constructors and Destructors in Review (构造函数和析构函数小结)
A constructor is a special class member function that’s called whenever an object of that class is created. A class constructor has the same name as its class, but through the miracle of function overloading, you can have more than one constructor with the same name, provided that each has its own signature, or argument list. Also a constructor has no declared type. Usually a constructor is used to initialize members of a class object. Your initialization should match the constructor’s argument list.
构造函数是一种特殊的类成员函数,在创建类对象时被调用。构造函数的名称和类名相同,但通过函数重载,可以创建多个同名的构造函数,条件是每个函数的特征标 (参数列表) 都不同。另外,构造函数没有声明类型。通常,构造函数用于初始化类对象的成员,初始化应与构造函数的参数列表匹配。
For example, suppose the Dragon
class has the following prototype for a class constructor:
Dragon(const char * fname, const char * lname); // constructor prototype
In this case, you can use it to initialize new objects as follows:
Dragon yong = bozo("First", "Second"); // primary form
Dragon qiang("Third", "Fourth"); // short form
Dragon *ptr = new Dragon("Fifth", "Sixth"); // dynamic object
If C++11 rules are in effect, you can use list initialization instead (如果编译器支持 C++11,则可使用列表初始化):
Dragon yong = { "First", "Second" }; // C++11
Dragon qiang{ "Third", "Fourth" }; // C++11;
Dragon *ptr = new Bozo{ "Fifth", "Sixth" }; // C++11
If a constructor has just one argument, that constructor is invoked if you initialize an object to a value that has the same type as the constructor argument.
如果构造函数只有一个参数,则将对象初始化为一个与参数的类型相同的值时,该构造函数将被调用。
For example, suppose you have this constructor prototype:
Loong(int age);
Then you can use any of the following forms to initialize an object:
Loong yong = tien(44); // primary form
Loong qiang(66); // secondary form
Loong cheng = 32; // special form for one-argument constructors
A constructor that you can use with a single argument allows you to use assignment syntax to initialize an object to a value (接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值):
Classname object = value;
This feature can cause problems, but it can be blocked.
这种特性可能导致问题,可关闭这项特性。
A default constructor has no arguments, and it is used if you create an object without explicitly initializing it. If you fail to provide any constructors, the compiler defines a default constructor for you. Otherwise, you have to supply your own default constructor.
默认构造函数没有参数,因此如果创建对象时没有进行显式地初始化,则将调用默认构造函数。如果程序中没有提供任何构造函数,则编译器会为程序定义一个默认构造函数;否则,必须自己提供默认构造函数。
It can have no arguments or else it must have default values for all arguments (默认构造函数可以没有任何参数。如果有,则必须给所有参数都提供默认值):
Dragon(); // default constructor prototype
Loong(const int age = 0); // default for Bistro class
The program uses the default constructor for uninitialized objects:
Dragon strong; // use default
Dragon *ptr = new Dragon; // use default
Just as a program invokes a constructor when an object is created, it invokes a destructor when an object is destroyed. You can have only one destructor per class. It has no return type (not even void), it has no arguments, and its name is the class name preceded
by a tilde.
就像对象被创建时程序将调用构造函数一样,当对象被删除时,程序将调用析构函数。每个类都只能有一个析构函数。析构函数没有返回类型 (连 void
都没有),也没有参数,其名称为类名称前加上 ~
。
For example, the Dragon
class destructor has the following prototype:
~Dragon(); // class destructor
Class destructors that use delete
become necessary when class constructors use new
.
如果构造函数使用了 new
,则必须提供使用 delete
的析构函数。
References
[1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/
[2] C++ Primer Plus, 6th Edition, https://www.informit.com/store/c-plus-plus-primer-plus-9780321776402
- Class Constructors and Destructors