this指针
对于Stock类,还有很多工作要做。到目前为止,每种类成员函数都最多只涉及一个对象,即调用它的对象。但显然有时候方法可能不只涉及到一个对象,在这种情况下需要用到C++的this指针。
例如,Stock类虽然能显示各种数据,但缺乏分析能力,如果我们要设计一个输出哪一只股票的价格最高,由于程序无法直接访问total_val
,因此无法做出判断。我们设计两个函数,一个函数查看total_val
的值,另一个函数比较两个对象的total_val
的值。
1 | double total()const { return total_val; } |
另一个函数,我们定义一个成员函数,它查看两个Stock类对象,并返回股价较高的那个对象的引用。
由此我们可以引出以下两个问题来讨论:
- 如何将两个对象提供给成员函数呢?假设这个方法命名为topval(),则stock1.topval()将访问stock1的数据,则必须将第二个对象作为参数传递给它。出于效率方面的考虑,我们就可以按引用来传递参数。
- 如何返回答案呢?最直接的方案就是返回那个引用。因此可以这么写原型:
1
const Stock & topval(const Stock &s)const;
好的,假设实现完了topval细节,如何调用呢?很明显下面两个语句都是可行的:1
2top = stock1.topval(stock2);//隐式访问stock1,显示访问stock2
top = stock2.topval(stock1);//隐式访问stock2,显示访问stock1
这表示法也挺反人类的,但不要紧,我们这里就要着重介绍this指针的用法。
1 | const Stock & topval(const Stock &s)const |
s.total_va
是参数的成员,total_val
是调用这个方法的对象的成员。问题在于在else分支内,stock1
是没有别名的,如何称呼这个调用这个方法的对象自己呢?
C++解决这种问题的方法就是使用名称为this的特殊指针。this指针是指向用来调用成员函数的对象。这样,函数调用stock1.topval(stock2);
,this
指针就是stock1
对象的地址。一般来说,所以的类方法都将this指针设置为调用它的对象的地址。
1 | const Stock & topval(const Stock &s)const |
因为是返回的是整个引用,this只是地址,记得加上*运算符。
对象数组
类似于结构体数组,可以创建同一个类的多个对象,就是使用创建对象数组。1
Stock a[3];
数组里的每一个元素都是对象。都可以使用类方法。1
2
3a[0].update();
a[1].show();
const Stock * tops = a[2].topval(a[1]);
使用构造函数初始化数组元素。如果这样做,必须为每个元素调用构造函数1
2
3
4
5Stock a[3] = {
Stock("a",1,2);
Stock("b",3,4);
Stock("c",5,6);
}
如果有多种构造函数,可以对不同的元素使用不同的构造函数,没有调用构造函数的将使用默认构造函数。1
2
3
4
5Stock b[10] = {
Stock("d",7,8);
Stock();
Stock("e",9,10);
}
初始化的方案是:首先使用默认构造函数创建数组,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应元素中。要创建对象数组,要求这个类必须有默认构造函数。
作用域
在类定义的名称(成员名、成员函数名)的作用域都为整个类。
作用域在整个类的名称,只在该类中是可知的,在类外是不可知的。因此可以在不同类中使用相同类成员名而不会引起冲突。另外,类作用于以为着不能从外部直接访问类成员,公有成员函数也是如此。也就是说,要调用公有成员函数,必须通过对象。
作用域为类的常量
有时候,使符号常量的作用域为类很有用。例如,类声明用字面值30来指定数组的长度,由于该常量对所有对象来说都是相同的,因此创建一个所有对象共享的常量是个不错的主意。
错误的代码:
1 | class Bakery |
这是行不通的,因为声明类值描述对象形式,并没有创建对象。因此,将没有储存这个值的空间。然而,有两种方法可以实现这个目标,而且效果相同。
方法1
类中声明枚举。1
2
3
4
5
6class Bakery
{
private:
enum {Months = 12};
double costs[Months];
};
在类声明中的枚举的作用域为整个类,因此可以用枚举为整型常量提供类作用域的符号名称。而且这种方式声明枚举并不会创建类数据成员,也就是说,所以对象其实都不包含枚举,Months也只是一个符号名称,在类的代码里遇到Months编译器都是会用12来代替。
方法2
C++提供了另一种在类中定义常量的方式——使用关键字static。1
2
3
4
5
6
7
8class Bakery
{
private:
static const int Months;
double costs[Months];
};
const int Bakery::Months = 12;
这将创建一个静态变量Months,该常量将于其他静态变量存储在一起,不存储在对象中。因此,这只有一个Months常量,被所有的类对象共享。
ADT —— 抽象数据类型
Stock类非常具体。然而,程序员常常通过定义类来表示很多更通用的概念。例如,就实现抽象数据类型(abstract date type, ADT)而言,使用类就是非常好的方式。类概念非常适合ADT方法,可以用公有成员函数表示ADT操作的接口,公有接口应隐藏数据,而且使用通用的术语来表达,例如栈的压入,弹出等。私有数据成员负责存储ADT数据,需要表明数据的存储方式,可以使用常规数组、动态分配或者更高级的数据结构等。
下面是一个栈的class实现。
1 | //stack.h -- class defination for the Stack ADT |
Stock 设计改进
1 |
|
小结
- 面对对象编程强调的是程序如何表示数据。使用OOP方法解决问题的第一步是根据它与程序间的接口来描述数据。
- 公有成员函数,又称为方法,提供访问数据的途径。
- 类将数据和方法组合成一个单元,其私有性实现数据隐藏。
- 通常将类声明放在头文件中、定义成员函数的源代码放在方法文件中,便将接口描述与实现细节分开了。从理论上说,只需知道公有接口就可以使用类。
- 类是用户定义的类型,对象是类的实例。如果提供了构造函数,则在创建对象时可以初始化对象。如果提供了析构函数,则对象消亡时将执行析构函数。
- 如果希望成员函数对多个对象进行操作,则可以使用this指针。*this是这个对象的别名。
- 类很适合用来描述ADT。