这是 C++ 与“基于继承的经典 OOP”的争议点之一。
有两个方面必须考虑:
- typedef 为同一类型引入了另一个名称:
std::map<Solver::EnumValue, double>
并且SmValueProb
- 在所有效果上 - 完全相同并且可以互换使用。
- 一个类引入了一种(原则上)与其他任何东西无关的新类型。
类关系由类“组成”的方式定义,以及是什么让隐式操作和转换成为可能与其他类型。
在特定的编程范式(如 OOP,与“inhritance”和“is-a”关系的概念相关联)继承、隐式构造函数、隐式转换等之外,都做同样的事情:让一个类型被使用跨另一种类型的接口,从而定义了跨不同类型的可能操作网络。这是(一般来说)“多态性”。
存在各种编程范式来说明应该如何构建这样的网络,每个都试图优化编程的特定方面,例如表示或运行时可替换对象(经典 OOP)、编译时可替换对象(CRTP)的表示、使用不同类型的流派算法函数(通用编程),使用“纯函数”来表示算法组合(函数和 lambda“捕获”)。
所有这些都规定了一些关于必须如何使用语言“特性”的“规则”,因为 - 作为 C++ 多范式 - 其特性仅满足范式的要求,从而使一些肮脏的东西变得开放。
正如Luchian所说,继承 std::map 不会产生纯 OOP 可替换类型,因为对基指针的删除将不知道如何销毁派生部分,因为析构函数不是设计为虚拟的。
但是 - 事实上 - 这只是一个特例:也pbase->find
不会调用你自己的最终覆盖的find
方法,std::map::find
不是虚拟的。(但这不是未定义的:它被很好地定义为很可能不是您想要的)。
真正的问题是另一个:“经典 OOP 替代原则”在您的设计中是否重要?换句话说,你是否打算互换使用你的类和它们的基础,函数只接受一个std::map*
orstd::map&
参数,假装这些函数调用 std::map 函数导致调用你的方法?
- 如果是,继承不是要走的路。std::map 中没有虚方法,因此运行时多态性将不起作用。
- 如果不是,那就是:您只是在编写自己的类,重用 std::map 行为和接口,无意交换它们的用法(特别是,您不是用 new 分配自己的类,并用 delete 应用删除它们到 std::map 指针),只提供一组以
yourclass&
或yourclass*
作为参数的函数,这很好。它甚至可能比 typedef 更好,因为您的函数不能再与 std::map 一起使用,从而分离了功能。
替代方案可以是“封装”:即:使地图和类的显式成员允许地图作为公共成员访问,或者使其成为具有访问器函数的私有成员,或者自己重写类中的地图接口。你最终得到了一个不相关的类型,它具有相同的接口和它自己的行为。以重写可能有数百个方法的东西的整个接口为代价。
笔记:
对于任何考虑丢失 vitual dtor 的危险的人来说,注意 tat 包含公众可见性并不能解决问题:
class myclass: public std::map<something...>
{};
std::map<something...>* p = new myclass;
delete p;
UB 非常喜欢
class myclass
{
public:
std::map<something...> mp;
};
std::map<something...>* p = &((new myclass)->mp);
delete p;
第二个示例与第一个示例有相同的错误,只是不太常见:它们都假装使用指向部分对象的指针来操作整个对象,部分对象中没有任何内容让您能够知道“包含一”是。