C++学习笔记----11、模块、头文件及各种主题(一)---- 模板概览与类模板(8)

扫测资讯 2024-11-14 18:07   23 0

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了。