- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在阅读一本关于 C++ 模板的非常好的书时,我遇到了一个我不理解的关于替代模板的解释:
These are bad alternatives to templates in C++
- You can write general code for a common base type such as Object or void*.
Reason : If you write general code for a common base class you lose the benefit of type checking. In addition, classes may be required to be derived from special base classes, which makes it more difficult to maintain your code.
谁能用代码示例解释一下?
最佳答案
糟糕的不是通用基类型的概念。这是对“对象类”的使用,一切都必须派生自,或者更糟的是,编写采用 void*
的代码,然后假设指针指向什么,将类型转换为指向的指针一些其他类型并希望最好。容器就是最好的例子。
实现容器方法的正确方法是使用模板。例如:
template<typename T> void List<T>::append(const T& obj);
在 Object
基类的情况下,这意味着您放入容器中的任何内容都必须派生自 Object
,因为所有容器方法都使用 Object*
作为所述容器中的数据。所以你得到这样的方法:
void List::append(Object* obj);
这里有两件坏事:首先,无论您走到哪里,Object
类都必须与您的容器一起拖来拖去。其次,它是一个非常通用的名称,可能会与其他库中的 Object
类冲突。
此外,您的容器永远不能包含不是 Object
直接派生的类型,包括像 int
这样的原始类型和像 std::string< 这样的标准类型
。您必须将这些类型“包装”在 Object
子类中,然后您必须花时间编写代码以从这些包装对象中提取值,等等。后面很痛苦你不需要。
所以你可能认为你可以使用通用指针 void*
代替:
void List::append(void* obj);
但是当你这样做时,容器可能需要做很多它不能做的事情,因为它不知道 void*
指向什么:
等等。 (您可以使用虚方法在 Object*
情况下避免这些问题,例如声明如下内容:
virtual ~Object() {}
virtual Object* clone() const;
virtual int cmp(const Object* rhs) const;
在您的 Object
基类中,这些方法必须 被所有 子类覆盖。但是现在你的 Object
类不是很轻量级。)
在这两种情况下,您最好为容器的数据类型使用模板化类型。如果您担心代码膨胀并且容器中的代码不关心数据类型(因为它不关心数据,例如在计算包含的元素时),您可以将该代码放在基类中并让您的模板化容器类从中派生。但大多数时候没有人真正关心这种“膨胀”,因为它比您的可用内存小得多。
If you write general code for a common base class you lose the benefit of type checking.
由于您已对 Object*
或 void*
进行了类型转换,因此类型检查在大多数情况下都被排除在外了。 (注意:这有点过时了,因为在某些情况下,您可以使用运行时类型识别 (RTTI) 和 dynamic_cast
操作来执行事后类型检查,以确保您从容器是您期望的类型。但是上述所有限制仍然适用,因为容器仍然不知道它包含什么。)
关于c++ - 为什么为通用基类型编写通用代码是 C++ 中模板的糟糕替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25696133/
经过几个小时的(重新)搜索,我无法想出普通抽象类和使用模板模式之间的可解释区别。 我唯一看到的是: 使用抽象类时,您需要实现所有方法。但是在使用模板方法时,您只需要实现这两个抽象方法。 有人可以向我解
我正在尝试实现一种算法,该算法可找到以下形状给出的外多边形的每个单独边的对应区域。也就是说,1,2 边的相应区域是 [1,6,7,8,2],2,3 边的区域是 [2,8,3] 等等,CCW 或 CW
我正在尝试在派生 self 的 BaseController 类的任何 Controller 上自动设置一个属性。这是我的 Application_Start 方法中的代码。 UnitOfWork 属
我正在使用 mgcv 包通过以下方式将一些多项式样条拟合到一些数据: x.gam smooth$knots [1] -0.081161 -0.054107 -0.027053 0.000001
考虑以下代码: void foo(){ ..... } int main() { int arr[3][3] ; char string[10]; foo();
本书The c++ programming language有这个代码: class BB_ival_slider : public Ival_slider, protected BBslider {
是否有一个 package.json 属性可用于指定模块解析应启动的根文件夹? 例如,假设我们在 node_modules/mypackage/src/file1 中有一个安装。我们要导入的所有文件都
我正在尝试使用聚合函数来实现与 SQL 查询相同的结果: 查询语句: sqldf(" SELECT PhotoID, UserID,
我正在比较使用 LOESS 回归的两条线。我想清楚地显示两条线的置信区间,我遇到了一些困难。 我尝试过使用各种线型和颜色,但在我看来,结果仍然是忙碌和凌乱。我认为置信区间之间的阴影可能会使事情变得更清
给定这段代码 public override void Serialize(BaseContentObject obj) { string file = ObjectDataStoreFold
我正在构建某种工厂方法,它按以下方式将 DerivedClass 作为 BaseClass 返回: BaseClass Factory() { return DerivedClass(); }
当重写 class delegation 实现的接口(interface)方法时,是否可以调用通常从重写函数中委托(delegate)给的类?类似于使用继承时调用 super 的方式。 来自docum
我有一个基类 fragment (如下所示)。我在其他 3 个 fragment 类中扩展了此类,每个类都共享需要在这 3 个 fragment 中访问的相同 EditText。因此,我在基类中设置了
如何在不加载额外库的情况下在 R 中计算两个排列之间的 Kendall tau 距离(又名冒泡排序距离)? 最佳答案 这是一个 O(n.log(n)) 的实现,在阅读后拼凑而成,但我怀疑可能有更好的
情况 我创建了一个具有国际化 (i18n) 的 Angular 应用程序。我想在子域中托管不同的版本,例如: zh.myexample.com es.myexample.com 问题 当我使用命令 n
std::is_base_of 之间的唯一区别和 std::is_convertible是前者在 Base 时也成立是 私有(private)或 protected Derived 的基类.但是,您何
我创建了一个名为 baseviewcontroller 的父类(super class) uiviewcontroller 类,用于包含大多数应用屏幕所需的基本 UI。它包括一个自定义导航栏和一个“自
我是一名优秀的程序员,十分优秀!