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.mzph.cn/diannao/38247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端小案例,用锚点(哈希值)实现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请求&…

ELK 企业实战7

ELKkafkafilebeat企业内部日志分析系统 1、组件介绍 1、Elasticsearch&#xff1a; 是一个基于Lucene的搜索服务器。提供搜集、分析、存储数据三大功能。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff…

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.…

5-linux文件路径与文件目录系统

目录 ①文件路径 目录跳转 绝对路径与相对路径 ②文件目录系统 目录系统组成 目录命名规则 命令补充 ls命令补充 file filename查看文件类型 less查看文本文件 ①文件路径 目录跳转 pwd:查看当前工作目录。 cd:改变目录。 ls:列出目录内容。 [root########## ~]# …

某易六月实习笔试

第一题 下面代码需要更改的地方已指出。 解题思路 模拟题&#xff0c;用双指针记录双方当前式神&#xff0c;再记录一下当前谁先手&#xff0c;直到有一方指针越界。 把下面代码now1变为now(now1)%2就行。 第二题 解题思路 01背包变种&#xff0c;只是背包的容量变为多个维度…

CLAY或许是今年最值得期待的3D生成模型,号称质量最好+布线最好+支持的输入模态最多+支持材质生成。

CLAY是一种大规模可控生成模型,用于创建高质量的3D资产,它结合了多分辨率变分自编码器和简化的潜在扩散变压器,通过多种输入形式生成详细的3D几何结构和物理渲染材质。 CLAY或许是今年最值得期待的3D生成模型,号称质量最好+布线最好+支持的输入模态最多+支持材质生成。 相…

vue2+three.js实现火焰效果

// 火焰getFireMaterial() {const vertex ${ShaderChunk.logdepthbuf_pars_vertex} bool isPerspectiveMatrix(mat4) {return true; } varying vec4 m_pos; varying vec2 vUv; varying vec3 _flame; uniform float uTime; vec2 hash( vec2 p ){p vec2( dot(p,vec2(150.1,350…

EDA期末复习——基础知识

个人名片&#xff1a; &#x1f393;作者简介&#xff1a;嵌入式领域优质创作者&#x1f310;个人主页&#xff1a;妄北y &#x1f4de;个人QQ&#xff1a;2061314755 &#x1f48c;个人邮箱&#xff1a;[mailto:2061314755qq.com] &#x1f4f1;个人微信&#xff1a;Vir2025WB…

SaaS 出海:Databend Cloud 的定位与实践

提到 “SaaS 出海”这个词大家肯定并不陌生&#xff0c;SaaS 企业将业务拓展到海外市场已经成为许多 SaaS 公司的重要战略方向。随着企业对于灵活性、可扩展性以及成本效益需求的不断增长&#xff0c; SaaS 模式提供了理想的解决方案。对于寻求出海机会的 SaaS 企业来说&#x…

神州信息与国科量子联合进军量子网络应用服务市场(中国军民两用通信技术展览会)

量子通信&#xff0c;智联未来 —— 神州信息与国科量子共启安全通信新纪元 在信息技术飞速发展的今天&#xff0c;信息安全已成为全球关注的焦点。神州数码信息服务股份有限公司&#xff08;神州信息&#xff09;与国科量子通信网络有限公司&#xff08;国科量子&#xff09;…

【地理库 Turf.js】

非常全面的地理库 &#xff0c; 这里枚举一些比较常用&#xff0c;重点的功能&#xff0c; 重点功能 提供地理相关的类&#xff1a;包括点&#xff0c;线&#xff0c;面等类。 测量功能&#xff1a;点到线段的距离&#xff0c;点和线的关系等。 判断功能&#xff1a; 点是否在…