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 initializationstruct Thing
{char * ptr;int num;
};
Thing forever = {"widget", 99}; // Valid initializationStock 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 constructorStock second(); // declares a functionStock 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 constructorStock(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 formatstd::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 1stock1.Show();Stock stock2 = Stock("second", 2, 2.0); // syntax 2stock2.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 objectstd::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: firstShares = 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: secondShares = 2 Share Price = $2.000 Total Worth = $4.00Assigning stock1 to stock2
Listing stock1 and stock2:
Company: firstShares = 12 Share Price = $20.000 Total Worth = $240.00
Company: firstShares = 12 Share Price = $20.000 Total Worth = $240.00Using a constructor to reset an object
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): third
Stock::~Stock(): thirdRevised stock1:
Company: thirdShares = 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: firstShares = 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: secondShares = 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