C++开发初级
1036 浏览 5 years, 11 months
2.2 定义方法
版权声明: 转载请注明出处 http://www.codingsoho.com/定义方法
前面SpreadsheetCell类的定义足以让您创建类的对象。然而,如果您试图调用setValue()或者getValue()方法,链接器将发出警告,指出方法没有定义。这是因为类定义指明了方法的原型,但是没有定义方法的实现。与函数原型以及函数定义类似,您必须编写方法的原型以及定义。注意,类定义必须在方法定义之前。通常类定义在头文件中,方法定义在包含头文件的源文件中。下面是SpreadsheetCell类中两个方法的定义:
// SpreadsheetCell.cpp
#include "SpreadsheetCell.h"
void SpreadsheetCell::setValue(double inValue)
{
mValue = inValue;
}
double SpreadsheetCell::getValue() const
{
return mValue;
}
代码取自 SpreadsheetCellNumOnly\SpreadsheetCell.h
注意,在每个方法名称之前都出现了类名称以及两个冒号:
void SpreadsheetCell::setValue(double inValue)
::
被称为作用域解析运算符(scope resolution operator
)。在此环境中,这个语法告诉编译器将要定义的setValue()方法是SpreadsheetCell类的一部分。此外还要注意当定义方法的时候不要重复使用访问说明符。
如果您使用Microsoft Visual C++ IDE,会发现所有的CPP文件都以#include"stdafx.h"开始。
在VC++项目中,每个文件都应该以这一行开始,您自己包含的文件必须在这一行后面。如果您将自己包含的文件放在stdafx.h之前,这一行就会失效,编译器会给出各种错误。对预编译头文件概念的说明超出了本课程的讨论范围,更多细节请参考Microsoft提供的关于预编译头文件的文档。
访问数据成员
类的大多数方法,例如setValue()和getValue(),总是用来处理类的特定对象(static方法例外,稍后讨论)。类的方法可以访问对象所属类中所有的数据成员,在前面setValue()的定义中,无论哪个对象调用这个方法,下面这行代码都会改变mValue变量的值:
mValue = inValue;
如果两个不同的对象调用setValue(),这行代码(对每个对象执行一次)会改变两个不同对象内的变量值。
调用其他方法
内部的某个方法可以调用其他方法,考虑扩展后的SpreadsheetCell类。实际的电子表格应用程序允许在单元格中保存文本数据以及数字。当您试图将文本单元格作为数字处理时,电子表格会试着将文本转换为数字。如果这个文本不能代表一个有效 的值,单元格的值会被忽略。在这个程序中,非数字的字符串会生成值为0的单元格。为了让SpreadsheetCell支持文本数据,下面对类定义进行修改:
#include <string>
using std::string;
class SpreadsheetCell
{
public:
void setValue(double inValue);
double getValue() const;
void setString(string inString);
string getString() const;
protected:
string doubleToString(double inValue) const;
double stringToDouble(string inString) const;
double mValue;
string mString;
};
代码取自 SpreadsheetCellNumText\SpreadsheetCell.h
这个类版本可以同时存储文本和数字。如果客户将数据设置为string,就会转换为double,如果设置为double,就转换为string。如果文本值是一个无效数字,double值则为0。类定义显示了两个设置并获取单元格文本表示的新方法,还有两个将double转换为string、将string转换为double的新的受保护辅助方法。这些辅助方法使用了字符串流,这一内容将在后面详细讨论。下面是这些方法的实现。
#include "SpreadsheetCell.h"
#include <iostream>
#include <sstream>
using namespace std;
void SpreadsheetCell::setValue(double inValue)
{
mValue = inValue;
mString = doubleToString(mValue);
}
double SpreadsheetCell::getValue() const
{
return mValue;
}
void SpreadsheetCell::setString(string inString)
{
mString = inString;
mValue = stringToDouble(mString);
}
string SpreadsheetCell::getString() const
{
return mString;
}
string SpreadsheetCell::doubleToString(double inValue) const
{
ostringstream ostr;
ostr << inValue;
return ostr.str();
}
double SpreadsheetCell::stringToDouble(string inString) const
{
double temp;
istringstream istr(inString);
istr >> temp;
if (istr.fail() || !istr.eof()) {
return 0;
}
return temp;
}
代码取自 SpreadsheetCellNumText\SpreadsheetCell.cpp
注意,每个设置方法都调用了辅助方法来执行转换,这种技术使得mValue和mString都始终有效。
this指针
每个普通的方法调用都会传递一个指向对象的指针,这就是被称为“隐藏”参数的this。您可以使用这个指针访问数据成员或者调用方法,并且可以将其传递给其他方法或者函数。有时候还用它来消除名称的歧义。
例如,可以使用value而不是mValue作为SpreadsheetCell类的数据成员,并且可以用value而不是inValue作为setValue()方法的参数。在此情况下,看上去是这样的:
void SpreadsheetCell::setValue(double value)
{
value= value; //Ambigous
mString = doubleToString(value);
}
代码取自 SpreadsheetCellThis\ambiguous\SpreadsheetCell.cpp
高亮显示的行令人感到困惑。是哪个value呢:是作为参数传递的value,还是对象成员value?
前面的歧义行可以编译成功,不会有任何警告或者错误消息,但是得到的结果并不是您所期望的。
为了避免名称的歧义可以使用this指针:
void SpreadsheetCell::setValue(double value)
{
this->value= value;
mString = doubleToString(this->value);
}
代码取自 SpreadsheetCellThis\unambiguous\SpreadsheetCell.cpp
然而,如果使用命名约定,永远不会遇到这类名称冲突的问题。
如果对象的某个方法调用了某个函数(或者方法),而这个函数采用指向对象的指针做参数,那么可以使用this指针调用这个函数。例如,假定您编写了一个独立的printCell()函数(不是方法),如下所示:
void printCell(SpreadsheetCell* inCellp)
{
cout << inCellp->getString() << endl;
}
代码取自 SpreadsheetCellThis\unambiguous\SpreadsheetCell.cpp
如果您想在setValue()方法里调用printCell(),必须将this指针作为参数传递给printCell(),这个指针指向setValue()操作的SpreadsheetCell对象。
void SpreadsheetCell::setValue(double value)
{
this->value= value;
mString = doubleToString(this->value);
printCell(this);
}
代码取自 SpreadsheetCellThis\unambiguous\SpreadsheetCell.cpp
重载<<运算符比使用printCell()函数更加方便,重载<<之后,就可以使用下面的行输出SpreadsheetCell:cout << *this << endl;