Seb*_*ann 22 c++ inheritance stdmap virtual-destructor
我想继承std::map
,但据我所知std::map
,没有任何虚拟析构函数.
因此可以std::map
在我的析构函数中显式调用析构函数以确保正确的对象破坏吗?
Luc*_*ore 33
析构函数确实被调用,即使它不是虚拟的,但这不是问题.
如果您尝试通过指向a的指针删除类型的对象,则会得到未定义的行为std::map
.
使用组合而不是继承,std
容器不应该被继承,你不应该.
我假设您想要扩展功能std::map
(假设您想要找到最小值),在这种情况下,您有两个更好,更合法的选项:
1)根据建议,您可以使用组合:
template<class K, class V>
class MyMap
{
std::map<K,V> m;
//wrapper methods
V getMin();
};
Run Code Online (Sandbox Code Playgroud)
2)免费功能:
namespace MapFunctionality
{
template<class K, class V>
V getMin(const std::map<K,V> m);
}
Run Code Online (Sandbox Code Playgroud)
Emi*_*lia 17
存在一种误解:继承 - 除了纯粹的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;
}
Run Code Online (Sandbox Code Playgroud)
将输出
A::A()
B::B()
B::hello()
B::~B()
A::~A()
Run Code Online (Sandbox Code Playgroud)
用A嵌入到B中
class B
{
public:
A a;
B() { cout << "B::B()" << endl; }
~B() { cout << "B::~B()" << endl; }
void hello() { cout << "B::hello()" << endl; }
};
Run Code Online (Sandbox Code Playgroud)
这将输出完全相同.
"如果析构函数不是虚拟的,则不导出"不是C++强制性结果,而只是一个普遍接受的未编写的(在规范中没有任何内容:除了基础上的UB调用删除)之前出现的规则++ 99,当动态继承和虚函数的OOP是C++唯一支持的编程范式时.
当然,世界各地的许多程序员都用这种学校制作骨头(同样教导iostream作为原始语言,然后移动到数组和指针,在最后一课,老师说"哦... tehre也是具有向量,字符串和其他高级功能的STL")今天,即使C++成为多范式,仍然坚持使用这种纯OOP规则.
在我的示例中,A ::〜A()与A :: hello完全不是虚拟的.这是什么意思?
简单:出于同样的原因,调用A::hello
不会导致调用B::hello
,调用A::~A()
(通过删除)将不会导致B::~B()
.如果你能接受 - 编程风格 - 第一个断言,你没有理由不接受第二个.在我的样本中没有A* p = new B
会收到,delete p
因为A ::〜A不是虚拟的,我知道这意味着什么.
正是同样的原因,不会使,使用第二个例子中的B,A* p = &((new B)->a);
用delete p;
,虽然这第二种情况,与第一个完美的双,看起来没有明显的理由不感兴趣的人.
唯一的问题是"维护",在某种意义上 - 如果由OOP程序员查看yopur代码 - 将拒绝它,不是因为它本身是错误的,而是因为他被告知要这样做.
实际上,"如果析构函数不是虚拟的,则不导出"是因为大多数程序员都认为有太多的程序员不知道他们不能在指向基类的指针上调用delete.(对不起,如果这不礼貌,但经过30多年的编程经验,我看不到任何其他原因!)
但你的问题是不同的:
调用B :: ~B()(通过删除或通过范围结束)将始终产生A ::〜A(),因为A(无论是嵌入还是继承)在任何情况下都是B的一部分.
遵循Luchian评论:在他的评论中上面提到的未定义行为与删除指向对象的指针 - 没有虚拟析构函数.
根据OOP学校,这导致规则"如果不存在虚拟析构函数则不导出".
我在这里指出的是,该学校的原因取决于每个面向OOP的对象必须是多态的,并且一切都是多态的,必须通过指向基址的指针来寻址,以允许对象替换.通过做出这些断言,该学校故意试图使派生和不可替换之间的交叉无效,这样纯粹的OOP程序就不会经历那个UB.
简单地说,我的立场承认C++不仅仅是OOP,并且默认情况下并非所有C++对象都必须面向OOP,并且承认OOP并不总是必需的,并且承认C++继承并不总是必须为OOP服务代换.
std :: map不是多态的,因此它不可替换.MyMap是相同的:NOT polymorphic并且不可替换.
它只需重用std :: map并公开相同的std :: map接口.继承只是避免重写函数的冗长模板的方法,这些函数只调用重用函数.
MyMap不会有虚拟dtor,因为std :: map没有.对我而言,这足以告诉C++程序员这些不是多态对象,不能在另一个对象的位置使用.
我不得不承认,大多数C++专家今天都没有这个立场.但我认为(我唯一的个人观点)这只是因为他们的历史,与OOP作为服务的教条有关,而不是因为C++的需要.对我来说,C++不是纯粹的OOP语言,并且在不遵循或不需要OOP的上下文中,不一定总是遵循OOP范例.
Mat*_* M. 12
我想继承
std::map
[...]
为什么?
继承有两个传统原因:
前者没有任何意义,因为map
没有任何virtual
方法,所以你不能通过继承来修改它的行为; 后者是对继承使用的歪曲,这最终只会使维护变得复杂.
如果没有明确的预期用法(在你的问题中没有上下文),我会假设你真正想要的是提供一个类似地图的容器,并有一些额外的操作.有两种方法可以实现这一目标:
std::map
,并提供足够的接口std::map
后者更简单,但它也更开放:原始界面std::map
仍然是敞开的; 因此它不适合限制操作.
毫无疑问,前者更重量级,但提供了更多的可能性.
由您决定哪两种方法更合适.