The this Pointer [this 指针] and An Array of Objects [对象数组]
- 1. The `this` Pointer (`this` 指针)
- 2. An Array of Objects (对象数组)
- References
1. The this
Pointer (this
指针)
class Stock {
private:
double total_val_;
...
public:
double Total() const { return total_val_; }
...
};
That is, you can use the Total()
method to obtain the value, but the class doesn’t provide a method for specifically resetting the value of total_val_
.
可以使用方法 Total()
来获得 total_val_
的值,但这个类没有提供专门用于重新设置 total_val_
的值的方法。
const Stock & Stock::TopVal(const Stock & stock) const {
if (stock.total_val_ > total_val_) {
return stock; // argument object
}
else {
return *this; // invoking object
}
}
The most direct way is to have the method return a reference to the object that has the larger total value.
最直接的方法是让方法返回一个引用,该引用指向 total_val_
较高的对象。
This function accesses one object implicitly and one object explicitly, and it returns a reference to one of those two objects. The const
in parentheses states that the function won’t modify the explicitly accessed object, and the const
that follows the parentheses states that the function won’t modify the implicitly accessed object. Because the function returns a reference to one of the two const
objects, the return type also has to be a const
reference.
该函数隐式地访问一个对象,而显式地访问另一个对象,并返回其中一个对象的引用。括号中的 const
表明,该函数不会修改被显式地访问的对象;而括号后的 const
表明,该函数不会修改被隐式地访问的对象。由于该函数返回了两个 const
对象之一的引用,因此返回类型也应为 const
引用。
top = stock1.TopVal(stock2);
top = stock2.TopVal(stock1);
The first form accesses stock1
implicitly and stock2
explicitly, whereas the second accesses stock1
explicitly and stock2
implicitly. Either way, the method compares the two objects and returns a reference to the one with the higher total value.
第一种格式隐式地访问 stock1
,而显式地访问 stock2
;第二种格式显式地访问 stock1
,而隐式地访问 stock2
。无论使用哪一种方式,都将对这两个对象进行比较,并返回 total_val_
较高的那一个对象。
The fact that the return type is a reference means that the returned object is the invoking object itself rather than a copy passed by the return mechanism.
返回类型为引用意味着返回的是调用对象本身,而不是其副本。
Here stock.total_val_
is the total value for the object passed as an argument, and total_val_
is the total value for the object to which the message is sent. If stock.total_val_
is greater than total_val_
, the function returns a reference to stock
. Otherwise, it returns a reference to the object used to evoke the method. In OOP talk, that is the object to which the total_val_
message is sent. If you make the call stock1.TopVal(stock2)
, then stock
is a reference for stock2
(that is, an alias for stock2
), but there is no alias for stock1
.
stock.total_val_
是作为参数传递的对象的总值,total_val_
是用来调用该方法的对象的总值。如果 stock.total_val_
大于 total_val_
,则函数将返回指向 stock
的引用;否则,将返回用来调用该方法的对象。在 OOP 中,是 total_val_
消息要发送给的对象。如果调用 stock1.TopVal(stock2)
,则 stock
是 stock2
的引用 (即 stock2
的别名),但 stock1
没有别名。
evoke [ɪˈvəʊk]:vt. 唤起,引起 (感情、记忆或形象)
The this
pointer points to the object used to invoke a member function. Basically, this is passed as a hidden argument to the method. Thus, the function call stock1.TopVal(stock2)
sets this
to the address of the stock1
object and makes that pointer available to the TopVal()
method. Similarly, the function call stock2.TopVal(stock1)
sets this
to the address of the stock2
object. In general, all class methods have a this
pointer set to the address of the object that invokes the method. Indeed, total_val_
in TopVal()
is just shorthand notation for this->total_val_
.
this
指针指向用来调用成员函数的对象,this
被作为隐藏参数传递给方法。这样,函数调用 stock1.TopVal(stock2)
将 this
设置为 stock1
对象的地址,使得这个指针可用于 TopVal()
方法。同样,函数调用 stock2.TopVal(stock1)
将 this
设置为 stock2
对象的地址。一般来说,所有的类方法都将 this
指针设置为调用它的对象的地址。确实,TopVal()
中的 total_val_
只不过是 this->total_val_
的简写。
Each member function, including constructors and destructors, has a this
pointer. The special property of the this
pointer is that it points to the invoking object. If a method needs to refer to the invoking object as a whole, it can use the expression *this
. Using the const
qualifier after the function argument parentheses qualifies this
as being a pointer to const
; in that case, you can’t use this
to change the object’s value.
每个成员函数 (包括构造函数和析构函数) 都有一个 this
指针。this
指针指向调用对象。如果方法需要引用整个调用对象,则可以使用表达式 *this
。在函数的括号后面使用 const
限定符将 this
限定为 const
,这样将不能使用 this
来修改对象的值。
What you want to return, however, is not this
because this
is the address of the object.You want to return the object itself, and that is symbolized by *this
. Recall that applying the dereferencing operator *
to a pointer yields the value to which the pointer points. Now you can complete the method definition by using *this
as an alias for the invoking object.
要返回的并不是 this
,因为 this
是对象的地址,而是对象本身,即 *this
。将解除引用运算符 *
用于指针,将得到指针指向的值。现在,可以将 *this
作为调用对象的别名来完成前面的方法定义。
- stock.h
#ifndef STOCK_H_
#define STOCK_H_
#include <string>
// class declaration
class Stock {
private:
std::string company_;
int shares_;
double share_val_;
double total_val_;
void SetTot() { total_val_ = shares_ * share_val_; }
public:
Stock(); // Default constructor
Stock(const std::string &company, const long num = 0, const double price = 0.0);
~Stock();
void Buy(const long num, const double price);
void Sell(const long num, const double price);
void Update(const double price);
void Show()const;
const Stock &TopVal(const Stock &stock) const;
}; // Note semicolon at the end
#endif
- stock.cpp
#include <iostream>
#include "stock.h"
// Default constructor
Stock::Stock() {
std::cout << "Stock::Stock()" << "\n";
company_ = "default name";
shares_ = 0;
share_val_ = 0.0;
total_val_ = 0.0;
}
Stock::Stock(const std::string &company, const long num, const double price) {
std::cout << "Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): " << company << "\n";
company_ = company;
if (num < 0) {
std::cout << "Number of shares_ can't be negative; " << company_ << " shares_ set to 0.\n";
shares_ = 0;
}
else {
shares_ = num;
}
share_val_ = price;
SetTot();
}
// Destructor
Stock::~Stock() {
std::cout << "Stock::~Stock(): " << company_ << "\n";
}
void Stock::Buy(const long num, const double price) {
if (num < 0) {
std::cout << "Number of shares_ purchased can't be negative. " << "Transaction is aborted.\n";
}
else {
shares_ += num;
share_val_ = price;
SetTot();
}
}
void Stock::Sell(const long num, const double price) {
if (num < 0) {
std::cout << "Number of shares_ sold can't be negative. " << "Transaction is aborted.\n";
}
else if (num > shares_) {
std::cout << "You can't sell more than you have! " << "Transaction is aborted.\n";
}
else {
shares_ -= num;
share_val_ = price;
SetTot();
}
}
void Stock::Update(const double price) {
share_val_ = price;
SetTot();
}
void Stock::Show() const {
// set format to #.###
std::ios_base::fmtflags original = std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::streamsize precision = std::cout.precision(3);
std::cout << "Company: " << company_ << "\n Shares = " << shares_;
std::cout << " Share Price = $" << share_val_;
// set format to #.##
std::cout.precision(2);
std::cout << " Total Worth = $" << total_val_ << '\n';
// restore original format
std::cout.setf(original, std::ios_base::floatfield);
std::cout.precision(precision);
}
const Stock & Stock::TopVal(const Stock & stock) const {
if (stock.total_val_ > total_val_) {
return stock; // argument object
}
else {
return *this; // invoking object
}
}
2. An Array of Objects (对象数组)
You declare an array of objects the same way you declare an array of any of the standard types.
声明对象数组的方法与声明标准类型数组相同。
Stock yongqiang[4]; // creates an array of 4 Stock objects
Recall that a program always calls the default class constructor when it creates class objects that aren’t explicitly initialized. This declaration requires either that the class explicitly define no constructors at all, in which case the implicit do-nothing default constructor is used, or, as in this case, that an explicit default constructor be defined. Each element - yongqiang[0]
, yongqiang[1]
, and so on - is a Stock
object and thus can be used with the Stock
methods.
当程序创建未被显式初始化的类对象时,总是调用默认构造函数。上述声明要求,这个类要么没有显式地定义任何构造函数 (在这种情况下,将使用不执行任何操作的隐式默认构造函数),要么定义了一个显式默认构造函数。每个元素 (yongqiang[0]
、yongqiang[1]
等) 都是 Stock
对象,可以使用 Stock
方法。
yongqiang[0].Update(); // apply Update() to 1st element
yongqiang[3].Show(); // apply Show() to 4th element
const Stock * top = yongqiang[2].TopVal(yongqiang[1]);
// compare 3rd and 2nd elements and set top to point at the one with a higher total value
You can use a constructor to initialize the array elements. In that case, you have to call the constructor for each individual element.
可以用构造函数来初始化数组元素。在这种情况下,必须为每个元素调用构造函数。
const int NUM = 4;
Stock stocks[NUM] = {
Stock("first", 12.5, 20),
Stock("second", 200, 2.0),
Stock("third", 130, 3.25),
Stock("fourth", 60, 6.5)
};
Here the code uses the standard form for initializing an array: a comma-separated list of values enclosed in braces. In this case, a call to the constructor method represents each value. If the class has more than one constructor, you can use different constructors for different elements.
这里的代码使用标准格式对数组进行初始化:用括号括起的、以逗号分隔的值列表。其中,每次构造函数调用表示一个值。如果类包含多个构造函数,则可以对不同的元素使用不同的构造函数。
const int NUM = 10;
Stock stocks[NUM] = {
Stock("first", 12.5, 20),
Stock(),
Stock("second", 130, 3.25),
};
This initializes stocks[0]
and stocks[2]
using the Stock(const std::string &company, const long num = 0, const double price = 0.0);
constructor as well as stocks[1]
using the Stock();
constructor. Because this declaration only partially initializes the array, the remaining seven members are initialized using the default constructor.
上述代码使用 Stock(const std::string &company, const long num = 0, const double price = 0.0);
初始化 stocks[0]
and stocks[2]
,使用构造函数 Stock();
初始化 stock[1]
。由于该声明只初始化了数组的部分元素,因此余下的 7 个元素将使用默认构造函数进行初始化。
初始化对象数组的方案是:首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中。因此,要创建类对象数组,则这个类必须有默认构造函数。
Because const Stock &TopVal(const Stock &stock) const;
examines just two objects at a time, the program uses a for
loop to examine the whole array. Also it uses a pointer-to-Stock to keep track of which element has the highest value.
由于 const Stock &TopVal(const Stock &stock) const;
每次只检查两个对象,因此程序使用 for
循环来检查整个数组。另外,它使用 const Stock * top
指针来跟踪值最高的元素。
- sample.cpp
#include <iostream>
#include "stock.h"
const int NUM = 4;
int main() {
{
// Create an array of initialized objects
Stock stocks[NUM] = {
Stock("first", 12, 20.0),
Stock("second", 200, 2.0),
Stock("third", 130, 3.25),
Stock("fourth", 60, 6.5)
};
std::cout << "\nStock holdings:\n";
for (int n = 0; n < NUM; ++n) {
stocks[n].Show();
}
// Set pointer to first element
const Stock * top = &(stocks[0]);
for (int n = 1; n < NUM; ++n) {
top = &top->TopVal(stocks[n]);
}
// Now top points to the most valuable holding
std::cout << "\nMost valuable holding:\n";
top->Show();
}
return 0;
}
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): first
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): second
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): third
Stock::Stock(const std::string &company, const long num = 0, const double price = 0.0): fourth
Stock holdings:
Company: first
Shares = 12 Share Price = $20.000 Total Worth = $240.00
Company: second
Shares = 200 Share Price = $2.000 Total Worth = $400.00
Company: third
Shares = 130 Share Price = $3.250 Total Worth = $422.50
Company: fourth
Shares = 60 Share Price = $6.500 Total Worth = $390.00
Most valuable holding:
Company: third
Shares = 130 Share Price = $3.250 Total Worth = $422.50
Stock::~Stock(): fourth
Stock::~Stock(): third
Stock::~Stock(): second
Stock::~Stock(): first
请按任意键继续. . .
Incidentally, knowing about the this
pointer makes it easier to see how C++ works under the skin. For example, the original Unix implementation used a C++ front-end cfront
that converted C++ programs to C programs.
知道 this
指针就可以更深入了解 C++ 的工作方式。例如,最初的 UNIX 实现使用 C++ 前端 cfront
将 C++ 程序转换为 C 程序。
C++ method definition:
void Stock::Show() const {
// set format to #.###
std::ios_base::fmtflags original = std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::streamsize precision = std::cout.precision(3);
std::cout << "Company: " << company_ << "\n Shares = " << shares_;
std::cout << " Share Price = $" << share_val_;
// set format to #.##
std::cout.precision(2);
std::cout << " Total Worth = $" << total_val_ << '\n';
// restore original format
std::cout.setf(original, std::ios_base::floatfield);
std::cout.precision(precision);
}
C-style definition:
void Show(const Stock * this) {
// 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: " << this->company_ << "\n Shares = " << this->shares_;
std::cout << " Share Price = $" << this->share_val_;
// set format to #.##
std::cout.precision(2);
std::cout << " Total Worth = $" << this->total_val_ << '\n';
// restore original format
std::cout.setf(original, std::ios_base::floatfield);
std::cout.precision(precision);
}
That is, it converted a Stock::
qualifier to a function argument that is a pointer to Stock
and then uses the pointer to access class members.
将 Stock::
限定符转换为函数参数 (指向 Stock
的指针),然后用这个指针来访问类成员。
Similarly, the front end converted function calls like
top.Show();
to this:
Show(&top);
In this fashion, the this
pointer is assigned the address of the invoking object. The actual details might be more involved.
将调用对象的地址赋给了 this
指针,实际情况可能更复杂些。
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