C++开发中级


1058 浏览 6 years, 3 months

4.3 电子表格单元格的基类

版权声明: 转载请注明出处 http://www.codingsoho.com/

电子表格单元格的基类

由于所有电子表格单元格都是SpreadsheetCell基类的子类,首先编写这个类是个不错的主意。当设计基类的时候,应该考虑子类之间的关系。根据这些信息,可以提取共有特性并将其放到父类中。例如,字符串单元格以及双精度值单元格的共同点在于都包含了单个数据块。由于数据来自用户并且将显示给用户,这个值被设置为字符串,并且作为字符串获取。这些行为就是用来组成基类的共享功能。

初次尝试

SpreadsheetCell基类负责定义所有子类支持的行为。在本例中,所有单元格都应该能够将值设置为字符串。此外,所有单元格都能够将当前值作为字符串返回。基类定义中声明了这些方法,但是没有数据成员,原因将在后面解释。

class SpreadsheetCell
{
 public:

  SpreadsheetCell();
  virtual ~SpreadsheetCell();
  virtual void set(const std::string& inString);
  virtual std::string getString() const;
};

当开始编写这个类的.CPP文件时,很快就会遇到问题。由于电子表格单元格的基类既不包含double也不包含string,如何实现这个类呢?更宽泛地讲,如何定义这样一个父类,这个父类声明了子类支持的行为,但是并不定义这些行为的实现。

可能的方法之一是为这些行为实现“什么都不做”功能。例如,调用SpreadsheetCell基类的set()()方法将没有任何效果,因为基类没有任何成员需要设置。然而这种方法仍然存在问题。理想情况下,基类不应该有实例。调用set()方法应该总是有效,因为总是会基于DoubleSpreadsheetCell或者StringSpreadsheetCell调用这个方法。好的解决方案应该强制执行这一限制。

前面的代码在SpreadsheetCell类中声明了一个虚析构函数。如果不这么做,编译器将生成一个默认的非虚析构函数。这意味着如果您不亲自声明虚析构函数,当使用指针或者引用调用子类的析构函数时,就可能会出现问题,本章前面已经对此有过讲述。

纯虚方法以及抽象基类

纯虚方法(Pure virtual methods)在类定义中显式说明该方法不需要定义。如果将某个方法设置为纯虚的,就是告诉编译器当前类中不存在这个方法的定义。因此这个类就是抽象类,因为这个类没有实例。如果某个类包含了一个或者多个纯虚方法,那么就无法构建这种类型的对象。

采用专门的语法指定纯虚方法:方法声明后紧接着=0。在.cpp文件中不需要编写任何代码。

class SpreadsheetCell
{
 public:

  SpreadsheetCell();
  virtual ~SpreadsheetCell();
  virtual void set(const std::string& inString) = 0;
  virtual std::string getString() const = 0;
};

现在基类成为了抽象类,无法创建SpreadsheetCell对象,下面的代码将无法编译,并且将会给出类似于Cannot declare object of type ‘SpreadsheetCell' because one or more virtual functions are abstract:的错误。

SpreadSheetCell cell; // BUG, Attempts creating abstract class instance

然而,下面的代码将可以成功编译:

SpreadSheetCell* ptr; 

代码可以运行,因为在此要求实例化抽象超类的派生类,例如:

ptr = new SpreadSheetCell(); 

抽象类提供了一种禁止其他其他代理直接实例化对象的方法,而它的子类可以实例化对象。

基类源代码

SpreadsheetCell.cpp并不需要太多代码。当定义这个类的时候,大多数方法都是纯虚的—没有给出定义。构造函数以及析构函数例外,对于这个示例而言,构造函数只是占位符(placeholder),以防在将来可能需要的初始化。然而,前面已经讲过,virtual析构函数是必须的。

SpreadsheetCell::SpreadsheetCell(){}
SpreadsheetCell::~SpreadsheetCell(){}