C ++:从标准::地图继承(C++: Inheriting from std::map)

2019-06-24 05:00发布

我想从继承std::map ,但据我所知std::map还没有任何虚析构函数。

是不是因此可以调用std::map在我的析构函数中的析构函数明确,以确保正确的对象的破坏?

Answer 1:

析构函数没有被调用,即使它不是虚拟的,但是这不是问题。

你得到了一个未定义的行为,如果你试图通过一个指向删除您的类型的对象std::map

用的组合物,而不是继承, std容器并不意味着继承,你不应该。

我假设你想扩展的功能std::map (比如你要查找的最小值),在这种情况下,你有两个更好的,以及法律 ,选项:

1)作为建议,你可以使用,而不是组成:

template<class K, class V>
class MyMap
{
    std::map<K,V> m;
    //wrapper methods
    V getMin();
};

2)免费功能:

namespace MapFunctionality
{
    template<class K, class V>
    V getMin(const std::map<K,V> m);
}


Answer 2:

有一种错觉:继承-outside纯OOP的概念,即C ++不是 - 只不过是一个更“组合物与未命名的部件,具有衰变能力”。

由于没有虚函数(和析构函数是不是特别的,在这个意义上),使你的对象不是多态的,但如果你在做什么只是“重用行为和揭露原生界面”继承做你问什么。

析构函数不需要彼此显式调用,因为他们的呼叫总是由规范链。

#include <iostream>
unsing namespace std;

class A
{
public:
   A() { cout << "A::A()" << endl; }
   ~A() { cout << "A::~A()" << endl; }
   void hello() { cout << "A::hello()" << endl; }
};

class B: public A
{
public:
   B() { cout << "B::B()" << endl; }
   ~B() { cout << "B::~B()" << endl; }
   void hello() { cout << "B::hello()" << endl; }
};

int main()
{
   B b;
   b.hello();
   return 0;
}

将输出

A::A()
B::B()
B::hello()
B::~B()
A::~A()

一种制造嵌入乙与

class B
{
public:
   A a;
   B() { cout << "B::B()" << endl; }
   ~B() { cout << "B::~B()" << endl; }
   void hello() { cout << "B::hello()" << endl; }
};

将输出完全一样。

“不推导,如果析构函数不是虚拟的”是不是C ++强制性的后果,而只是一个普遍接受的不被写入(有没有什么关于它的规范:除了一个UB调用删除的基座上)规则C之前产生++ 99,当通过OOP动态继承和虚拟功能是唯一支持的编程范例C ++。

当然,世界各地的许多编程人员就他们的骨头与各类学校(教输入输出流作为原语一样,然后移动到数组和指针,并在最后一节课的老师说:“哦...... tehre也是有向量,字符串等先进功能“),今天STL,即使C ++ becamed多范型,仍坚持用这种纯粹的OOP规则。

在我的样本A ::〜A()不是虚正是因为A ::你好。 这是什么意思?

很简单:出于同样的原因调用A::hello不会导致调用B::hello ,调用A::~A()通过删除)不会导致B::~B() 如果你能接受你-in编程风格- 第一个断言,没有任何原因,你不能接受第二 。 在我的样本没有A* p = new B将接收delete p因为A ::〜A是不是虚拟的, 我知道这意味着什么

正是同样的原因,不会使,使用B,第二实施例A* p = &((new B)->a);delete p; 虽然这第二种情况下,与第一个完美的双,他们不是为了没有明显的原因有趣的人。

唯一的问题是“维护”,在 - 如果yopur代码由OOP programmer-会拒绝它,不是因为它是错误的本身看的感觉,而是因为他被告知这样做。

事实上,是因为大多数程序员beleave有太多的程序员不知道他们不能打电话删除一个指向基地 “不,如果析构函数不是虚派生”。 (很抱歉,如果这是不礼貌的,但30年以上的编程经验后,我看不到任何其他原因!)

但是你的问题是不同的:

调用B ::〜B()(通过删除或通过结束范围)将总是导致在A ::〜A(),因为A(它是否嵌入或继承) 是在任何情况下B部-的


继Luchian评论:未定义行为上面提到的在他的评论是关系到一个指针到一个-object's基,没有虚析构函数删除。

据OOP的学校,这导致规则“不,如果没有虚析构函数存在派生出来的”。

什么我指出的是,在这里,是那所学校的原因取决于每个OOP面向对象必须是多态的,一切是多态的必须是指针寻址的基础上,允许对象替代的事实。 通过使这些说法,认为学​​校是故意使无效之间的交叉试图推导和非替代的,因此,一台纯面向对象的程序将不会遇到UB。

我的立场,只是承认,C ++不仅是OOP,而不是所有的C ++对象必须是OOP默认为导向,并承认OOP并不总是必要的需求,也承认C ++继承并不总是必要的维修工作应OOP代换。

的std ::地图是不是多态的,所以它不是更换。 MyMap中是一样的:NOT多态性,不能更换。

它只是重复使用的std ::地图和公开相同的std ::地图界面。 和继承只是为了避免重写功能,只是调用重用那些很长的样板路。

MyMap中不会有虚析构函数为的std ::地图没有之一。 这-to我 - 就足以告诉C ++程序员,这些都不是多态的对象,并且不得在其他的地方可以使用一个。

我不得不承认,这个位置由最的C ++专家,今天不共享。 但我认为(我只是个人意见),这只是他们的历史,这涉及到OOP的教条服务不是因为C ++需要,只是因为。 对我来说,C ++是不是一个纯粹的OOP语言和不一定必须始终遵循OOP范例,在OOP后面没有或需要上下文。



Answer 3:

我想从继承std::map [...]

为什么呢?

有两个原因,传统的继承:

  • 重用其接口(并且因此,编码针对它的方法)
  • 重用其行为

前者是没有意义的这里map没有任何virtual方法,所以你不能在继承修改其行为; 而后者是使用继承的仅在末端复杂的维护的曲解。


没有你的使用目的(在你的问题缺乏上下文)的一个清晰的思路,我会假设你真正想要的是提供一个地图状容器,具有一定的奖金操作。 有两种方法来实现:

  • 组成:创建一个新的对象,其中包含 std::map ,并提供足够的接口
  • 延伸:您创建上运行新的免费功能std::map

后者是简单的,但它也更开放:原有界面std::map仍然是广开; 因此它是不适合操作限制

前者是更重量级的,毫无疑问的,但提供了更多的可能性。

它是由你来决定这两种方法更适合。



Answer 4:

@Matthieu M你说

我想从性病::地图继承[...]

为什么呢?

有两个原因,传统的继承:

  1. 重用其接口 (并且因此,编码针对它的方法)
  2. 重用其行为

前者是没有意义的这里的地图没有任何虚方法,所以你不能在继承修改其行为; 而后者是使用继承的仅在末端复杂的维护的曲解。

对于“前者”:

clear()函数是虚拟的,对我来说它使一个很大的意义一个std::map<key,valueClass*>::clear()在派生类中重写与删除所有指向一个迭代器调用基类前值类的实例clear()以防止意外的内存泄漏,这是我所实际使用的一招。 至于为什么会有人想使用地图类型指针,以及多态,而不是被重新分配的引用意味着,不能在STL容器中使用。 你可能反而建议使用的reference_wrapper或智能指针比如shared_ptr (C ++ 11层的功能),但是当你写,你希望有人仅限于C ++编译器98将能够使用,这些库不,除非你打算把在具有推动作用,这也可能是不期望的要求的选项。 如果你真的想在地图拥有其内容的独家所有权,那么你不想使用的reference_wrapper或智能指针的大多数实现是。

关于“后者”:

如果你想有一个地图的指针是自动指向内存中删除,然后再利用“所有”其他地图的行为和压倒一切的明确使得很多的意义,我,当然,你还需要覆盖委派/拷贝构造函数克隆指出,当您复制地图,这样你就不是双删除尖到的实例对象valueClass

但是,这仅需要编码来实现的极少量。

我也用一个受保护typedef std::map<key,valueClass*> baseClassMap; 作为第一线2的派生类的地图的声明的,以使得我可以调用baseClassMap::clear(); 在覆盖clear()这个循环之后函数删除的所有实例valueClass*包含在导出地图,这使得维修的情况下,类型更容易valueClass*都没有改变。

问题是 ,虽然它可能具有良好的编码习惯的适用性有限,我不认为这是公平地说,这是从来没有从地图下降是一个好主意。 但是,也许你有我没有关于如何实现相同的自动内存管理的效果而无需添加额外的源代码显著量想过(如聚集一个更好的主意std::map )。



文章来源: C++: Inheriting from std::map