The this Pointer (this 指针) and An Array of Objects (对象数组)

The this Pointer {this 指针} and An Array of Objects {对象数组}

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),则 stockstock2 的引用 (即 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

  • Knowing Your Objects: The this Pointer
  • An Array of Objects
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yongqiang Cheng

梦想不是浮躁,而是沉淀和积累。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值