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;