C++学习笔记----9、发现继承的技巧(四)---- 多态继承(3)

6、未来考虑

        从面向对象设计的角度,新的SpreadsheetCell层次结构的实现确定是一次提升。然而,对于真实世界的spreadsheet程序来说这样的类层次结构还不够,有如下几个原因。

        首先,除了提升的设计,还缺失一个属性:从一个单元格类型转换为另一个的能力。通过分成两个类,单元格对象变得松散了。为了提供将DoubleSpreadsheetCell转化为StringSpreadsheetCell的能力,还要加一个转换构造函数,也叫做类型构造函数。与拷贝构造函数外观类似,但是它不是同一个类的对象的引用,而是一个同胞类的对象的引用。记住现在要声明一个缺省的构造函数,它可以被显式缺省,因为只要你自己一声明任何构造函数,编译器就不会再生成了。

export class StringSpreadsheetCell : public SpreadsheetCell
{
public:
	StringSpreadsheetCell() = default;

	StringSpreadsheetCell(const DoubleSpreadsheetCell& cell)
		: m_value{ cell.getString() }
	{
	}
    // Remainder omitted for brevity.	
};

        使用转换构造函数,给定一个DoubleSpreadsheetCell,可以容易地生成一个StringSpreadsheetCell。然而,不要给这些转换指针或引用搞晕。将一个同胞指针或引用从一个转换为另一个是不灵的,除非你重载了转换操作符。

        警告:层次结构上,总是可以向上转换,有时可以向下转换。通过改变cast操作符或使用reinterpret_cast()在层次结构之间转换是可能的,但都不推荐。

        其次,怎么实现单元格的重载的操作符是一个有趣的总是,有几种可能的方法。

        一种方法是实现单元格的每一种组合的操作符的一个版本。对于只有两个继承类,是可管理的。会有一个operator+函数来加两个double单元格,来加两个string单元格 ,以及来加一个double到一个string单元格 。对于每一种组合,要决定结果是什么。例如,两个double单元格相加的结果可以是两个值的算术相加。两个string单元格的相加结果可以是两个string联结的string,等等。

        另一个方法是决定通用表示。早期的实现已经在string上作为各种通用的表示标准化。单独的operator+可以通过利用通用表示囊括所有情况。

        另外一种方法是一个混合。operator+可以提供来添加两个DoubleSpreadsheetCell结果放在DoubleSpreadsheetCell。该操作符可以在double_spreadsheet_cell模块中实现,如下:

export DoubleSpreadsheetCell operator+(const DoubleSpreadsheetCell& lhs,
	const DoubleSpreadsheetCell& rhs)
{
	DoubleSpreadsheetCell newCell;
	newCell.set(lhs.getValue() + rhs.getValue());
	return newCell;
}

        该操作符可以测试如下:

		DoubleSpreadsheetCell doubleCell;
		doubleCell.set(8.4);
		DoubleSpreadsheetCell result{ doubleCell + doubleCell };
		println("{}", result.getString());  // Prints 16.800000

        当两个操作数中至少一个是StringSpreadsheetCell时提供第二个operator+。可以决定该操作符的结果应该总是一个string单元格。可以给string_spreadsheet_cell模块添加一个这样的操作符,可以实现如下:

export StringSpreadsheetCell operator+(const StringSpreadsheetCell& lhs,
	const StringSpreadsheetCell& rhs)
{
	StringSpreadsheetCell newCell;
	newCell.set(lhs.getString() + rhs.getString());
	return newCell;
}

        只要编译器有方法将特殊的单元格转换为StringSpreadsheetCell,操作符就管用。拿前面例子中使用DoubleSpreadsheetCell作参数的StringSpreadsheetCell构造函数,如果这是让operator+唯一工作的方式的话,编译器会自动执行转换。这意味着下面的代码将一个double单元格到一个string单元格是可以的,即使只提供了两个operator+的实现:一个是两个double单元格相加,一个是两个string单元格相加。

		DoubleSpreadsheetCell doubleCell;
		doubleCell.set(8.4);
		StringSpreadsheetCell stringCell;
		stringCell.set("Hello ");
		StringSpreadsheetCell result{ stringCell + doubleCell };
		println("{}", result.getString());  // Prints Hello 8.400000

        如果对多态还有一点儿不确信,以此例代码开始搞明白它。实验代码练习多态的多个方面是一个伟大的起点。

7、为干净的virtual成员函数提供实现

        从技术上来讲,为干净的virtual成员函数提供实现是可能的。该实现不能在类定义本身,必须在外部提供。虽然类保持抽象,任何继承类仍然要求提供干净的virtual成员函数的实现。由于类保持抽象,不能生成实例。干净的virtual成员函数的实现仍然可以被调用,例如,从继承类,下面的代码段展示了这一点:

class Base
{
public:
    virtual void doSomething() = 0; // Pure virtual member function.
};

// An out-­of-­class implementation of a pure virtual member function.
void Base::doSomething() { println("Base::doSomething()"); }

class Derived : public Base
{
public:
    void doSomething() override
    {
        // Call pure virtual member function implementation from base class.
        Base::doSomething();
        println("Derived::doSomething()");
    }
};

int main()
{
    Derived derived;
    Base& base { derived };
    base.doSomething();
}

        输出如预期:

Base::doSomething()
Derived::doSomething()

猜你喜欢

转载自blog.csdn.net/weixin_71738303/article/details/143173403