Class Constructors and Destructors (類的構造函數和析構函數)

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() 的開頭和末尾多了一個大括號。諸如 stock1stock2 等自動變量將在程序退出其定義所屬代碼塊時消失。如果沒有這些大括號,代碼塊將為整個 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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38247.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38247.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38247.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MT1597 平行線

題目 用下面的數據類型表示線&#xff1a; struct POINT { //點 int x, y; //坐標值x和y } ; struct LINE { //線 POINT s, e; //線的兩端 } ; 輸入2個線段的端點的坐標值x和y&#xff0c;判斷兩條線段所在直線是否為平行線。如果兩線段共線&#xff0c;判為不平行。 輸入…

強強聯合:Apache Kylin與Impala的集成之道

&#x1f517; 強強聯合&#xff1a;Apache Kylin與Impala的集成之道 在大數據時代&#xff0c;Apache Kylin和Impala都是分析型數據庫的佼佼者&#xff0c;分別以預計算的OLAP引擎和高性能的SQL on Hadoop解決方案而聞名。將兩者集成&#xff0c;可以充分利用Kylin的預計算能…

C程序設計譚浩強第五版

第三章 程序習題 1、第2題2、第2題3、第3題4、第4題 1、第2題 假如我國國民生產總值的年增長率為7%&#xff0c; 計算10年后我國國民生產總值與現在相比增長多少百分比。計算公式為 p ( 1 r ) n p (1r)^n p(1r)n ,其中r為年增長率&#xff0c;n為年數&#xff0c;p為與現在…

thinkphp通過with查詢,并通過關聯表進行篩選

直接添加一個where條件,然后條件里面用表名.字段即可,非常方便 需要注意的一點是在fastadmin里面,$this->auth->getGroupIds()這樣獲取是會獲取到緩存里面的值,必須重新登錄之后才可以得到最新的用戶組,這個問題導致困擾了我一晚上 $usage $this->model->with([us…

Oracle數據庫教程

Oracle數據庫教程 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將深入探討Oracle數據庫的基礎知識、特性以及在Java中的應用。 什么是Oracle數據庫&…

前端小案例,用錨點(哈希值)實現Tab組件切換

在前端開發的世界里&#xff0c;使用現代化的技術和方法來實現常見的組件是非常重要的。今天&#xff0c;我們將通過一個具體的案例來展示如何使用現代化的CSS和ES6來創建一個優雅且功能豐富的Tab組件。本文將詳細介紹實現思路、代碼分析&#xff0c;并提供一些實用的開發技巧。…

25 防火墻基礎操作

1 防火墻進入WEB頁面操作 華三防火墻的默認用戶:admin/密碼:admin 將IP地址改在同一網段的信息 在防火墻的管理地址 GE/0/0/1&#xff1a;192.168.0.1 主機的地址是:192.168.0.101 思考一下為什么Ping不通 security-zone name Management import interface GigabitEthernet1/…

音視頻開發34 FFmpeg 編碼- 將h264和acc文件打包成flv文件

FFmpeg合成流程 示例本程序會?成?個合成的?頻和視頻流&#xff0c;并將它們編碼和封裝輸出到輸出?件&#xff0c;輸出格式是根據?件 擴展名?動猜測的。 示例的流程圖如下所示。 ffmpeg 的 Mux 主要分為 三步操作&#xff1a; avformat_write_header &#xff1a; 寫?…

Qt WPS(有源碼)

項目源碼地址&#xff1a;WPS完整源碼 一.項目詳情 該項目仿照WPS&#xff0c;實現了部分的功能&#xff0c;能夠很方便對文本和HTML進行修改&#xff0c;并且有打印功能&#xff0c;可以很方便的生成PDF。 應用界面 項目架構分析 這個項目主要可分為兩個部分&#xff0c;一…

使用RNN模型構建人名分類器

使用RNN模型構建人名分類器 1 項目需求和實現分析 短文本分類問題 2 數據處理三部曲 場景1&#xff1a;數據處理三部曲示意圖 場景2&#xff1a;三個字母onehot編碼形狀分析 3 構建RNN模型 4 構建訓練函數并進行訓練 - 有關模型、損失函數、優化器三者在pytorch中的表示 5…

葉老師的新水杯c++

題目描述 最近葉老師換了個帶吸管的水杯。 貝貝發現當葉老師使用帶吸管的水杯時&#xff0c;每天會喝 x 毫升的水。而使用不帶吸管的水杯時&#xff0c;每天會喝 y 毫升的水。 請問在 n 天的時間內&#xff0c;葉老師喝水量的上限與下限相差多少&#xff1f; 輸入 第一行為…

聚焦Python分布式爬蟲必學框架Scrapy打造搜索引擎(一)

Scrapy綜述 Scrapy總體架構 Scrapy架構圖(綠線是數據流向) 適用于海量靜態頁面的數據下載 Scrapy Engine(引擎): 負責Spider、ItemPipeline、Downloader、Scheduler中間的通訊&#xff0c;信號、數據傳遞等。 Scheduler(調度器): 它負責接受引擎發送過來的Request請求&…

java.io.PrintStream介紹

java.io.PrintStream 是 Java 標準庫中的一個類&#xff0c;用于輸出流中的打印。它提供了一組方便的方法&#xff0c;用于格式化輸出和寫入數據。PrintStream 類中最常見的使用方式是通過 System.out 和 System.err 進行標準輸出和錯誤輸出。 System.out 和 System.err 都是 …

ELK 企業實戰7

ELKkafkafilebeat企業內部日志分析系統 1、組件介紹 1、Elasticsearch&#xff1a; 是一個基于Lucene的搜索服務器。提供搜集、分析、存儲數據三大功能。它提供了一個分布式多用戶能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java開發的&#xff…

【linux】chmod修改文件權限詳解

目錄 一.linux中默認的文件權限 二.文件權限類別 三.chmod改變權限的兩種方式 一.linux中默認的文件權限 默認文件權限: 當你新建一個文件時&#xff0c;文件會有一個默認的權限&#xff0c;這個默認權限就是umask的值&#xff0c;umask一般默認是022&#xff0c;這個值設…

FastAPI教程——部署

部署 部署FastAPI應用程序相對容易。 部署是什么意思 部署應用程序意味著執行必要的步驟以使其可供用戶使用。 對于Web API來說&#xff0c;通常涉及將上傳到云服務器中&#xff0c;搭配一個性能和穩定性都不錯的服務器程序&#xff0c;以便你的用戶可以高效地訪問你的應用…

智慧校園-報修管理系統總體概述

智慧校園報修管理系統是專為優化教育機構內部維修報障流程而設計的信息化解決方案&#xff0c;它通過集成現代信息技術&#xff0c;為校園設施的維護管理帶來革新。該系統以用戶友好和高效運作為核心&#xff0c;確保了從報修請求提交到問題解決的每一個步驟都順暢無阻。 師生或…

分享畫布繪制矩形

簡介 實現功能&#xff0c;在畫布上繪制矩形&#xff0c;移動矩形。 在線演示 繪制矩形 實現代碼 <!DOCTYPE html><html><head> <title>繪制矩形</title> </head><body><div style"margin: 10px"><input typ…

FastDFS部署

版本介紹 安裝fastdfs共需要倆個安裝包 fastdfs-5.05.tar.gz libfastcommon-1.0.7.tar.gz編譯安裝 libfastcommon tar -xvf libfastcommon-1.0.7.tar.gz cd libfastcommon-1.0.7 make.sh make.sh install 3. 設置軟鏈接 libfastcommon.so默認安裝到了/usr/lib64/libfastcommon.…

探索AI視覺革新:深入目標檢測算法

一、目標檢測算法概述 1. 什么是目標檢測&#xff1f; 目標檢測是計算機視覺任務的一部分&#xff0c;其目標是在圖像或視頻中識別和定位特定物體的位置和類別。與簡單的圖像分類任務不同&#xff0c;目標檢測要求算法能夠準確地標記出圖像中每個物體的位置&#xff0c;通常用…