C++学习笔记----11、模块、头文件及各种主题(一)---- 模板概览与类模板(8)
2.7、从类模板派生
可以从类模板继承。如果派生类继承自模板自身,也必须是模板。还有别的选择,可以继承自类模板的特定实例,这种情况下继承类就不必为模板。还是前面的例子,假定你认为通用Grid类无法提供游戏面板足够的功能性。特别要提出的是,想要给游戏面板添加一个move()成员函数来将棋子从一个位置移动到面板的另一个位置。下面是GameBoard模板的类定义:
import grid;
import std;
export
template <typename T>
class GameBoard : public Grid<T>
{
public:
// Inherit constructors from Grid<T>.
using Grid<T>::Grid;
void move(std::size_t xSrc, std::size_t ySrc, std::size_t xDest, std::size_t yDest);
};
该GameBoard模板从Grid模板派生,因此继承了其所有的功能。不需要重写at(),getHeight(),或任何其他的成员函数。也不需要添加拷贝构造函数,operator=,或析构函数,因为在GameBoard中没有任何动态内存分配。还有,GameBoard显式从基类Grid<T>继承了构造函数。。
继承的语法看起来是正常的,除了基类为Grid<T>,而不是Grid之外。这个语法的原因是GameBoard模板不是真的派生于通用的Grid模板。而是,每一个特定类型的GameBoard模板的实例继承自同样类型的Grid实例。例如,如果要用ChessPiece类型来初始化一个GameBoard,那么编译器也会生成Grid<ChessPiece>的代码。:public Grid<T>语法说明该类继承自任何Grid实例,对于T类型的参数都是合理的。
下面是move()成员函数的实现:
template <typename T>
void GameBoard<T>::move(std::size_t xSrc, std::size_t ySrc, std::size_t xDest, std::size_t yDest)
{
Grid<T>::at(xDest, yDest) = std::move(Grid<T>::at(xSrc, ySrc));
Grid<T>::at(xSrc, ySrc).reset(); // Reset source cell
// Or:
// this->at(xDest, yDest) = std::move(this->at(xSrc, ySrc));
// this->at(xSrc, ySrc).reset();
}
注意:虽然有些编译器没有强化它,c++名字查找规则要求使用this指针或Grid<T>::在基类模板中来指向数据成员与成员函数。因此,我们使用Grid<T>::at()而不是简单的at()。
可以使用GameBoard模板如下:
GameBoard<ChessPiece> chessboard { 8, 8 };
ChessPiece pawn;
chessBoard.at(0, 0) = pawn;
chessBoard.move(0, 0, 0, 1);
注意:当然了,如果想要覆盖Grid的成员函数,就要在Grid类模板中把它们标识成virtual。
2.8、继承与特殊化
有些程序员发现模板继承与模板特殊化之间的区别令人不解。下表总结了其不同:
继承 | 特殊化 | |
重用代码? | 是的:继承类包含了所有基类的数据成员与成员函数。 | 不是:必须重写所有的特殊化要求的代码。 |
重用名字? | 不是:继承类的名字必须与基类名字不同。 | 是的:特殊化必须与原来的名字相同。 |
支持多态? | 是的:继承类的对象可以代表基类的对象。 | 不是:模板的每一个实例类型是一个不同的类型。 |
注意:继承用于扩展实现与多态。特殊化用于特定类型的客户化实现。
2.9、别名模板
我们都了解类型别名与typedef的概念。它们允许对特定类型给出其它的名字。我们复习一下,例如,可以对int类型给另外一个名字,写出如下的类型别名:
using MyInt = int;
类似地,可以用类型别名给类模板起另一个名字。假定你有如下的类模板:
template <typename T1, typename T2>
class MyClassTemplate { /* ... */ };
可以定义如下的类型别名,指定两个类模板类型参数:
using OtherName = MyClassTemplate<int, double>;
也可以使用typedef而不是这样的类型别名。
还有,也可以只指定一些类型而保持模板类型参数中的其它类型不变。这叫做别名模板。下面为示例:
template <typename T1>
using OtherName = MyClassTemplate<T1, double>;
在这种情况下,就不能使用typedef了。