这可能是一个基本问题,但我还没有在任何地方看到它。 在C ++中,假设我有以下代码:
#include <iostream>
using namespace std;
class B {
public:
void f();
};
class C {
public:
void f();
};
class D : public B, public C{
public:
void f();
};
void B::f(){cout << "bbb" << endl;}
void C::f(){cout << "ccc" << endl;}
void D::f(){cout << "ddd" << endl;}
int main () {
B *d = new D;
delete d;
d->f();
return 0;
}
这很好用并输出&#34; bbb&#34; 。但是如果我要改变
的顺序class D : public B, public C{
到
class D : public C, public B{
我会收到&#34; Aborted(core dumped)&#34; 错误。这是什么原因?有人可以进一步解释基类顺序的重要性吗?
我知道这是多重继承,但我正在避免钻石问题,所以不确定发生了什么。
答案 0 :(得分:5)
班级B
不是多态的(例如,不提供任何virtual
方法)。因此,编译器假定d
指向的对象的动态类型是B
,并且它尝试根据该B
对象的地址删除内存,较长的匹配实际D
对象的地址。正如人们所写的那样:
D * d = new D;
delete static_cast<B *>(d);
那里有未定义的行为。当D
继承的类的顺序为B
,C
时,为什么它似乎有效?在您的情况下,B
部分的地址可能D
1}}对象碰巧与D
对象本身的地址相同。没有析构函数(显式或隐式)定义,因此只调用了一个释放函数,它可能接受了相应分配函数提供的指针。
另请注意,根据[basic.life]
类似地,在对象的生命周期开始之前但在对象将占用的存储之后已经分配,或者在对象的生命周期结束之后以及在重用或释放对象占用的存储之前,任何glvalue可以使用引用原始对象但仅限于有限的方式。对于正在构建或销毁的对象,请参阅[class.cdtor]。否则,这样的glvalue指的是已分配的存储([basic.stc.dynamic.deallocation]),并且使用不依赖于其值的glvalue的属性是明确定义的。如果出现以下情况,该程序具有未定义的行为:
- glvalue用于访问对象,或
- glvalue用于调用对象的非静态成员函数,或
- glvalue绑定到对虚基类的引用([dcl.init.ref]),或
- glvalue用作dynamic_cast的操作数或typeid的操作数。
因此,在代码d->f();
之后调用delete d;
也会导致未定义的行为。
答案 1 :(得分:4)
未定义的行为,纯粹而简单。
d
delete
后,您无法使用D
。虽然它很奇怪,它“以某种方式工作”而不是另一种方式,但这是未定义行为的本质。 (您可以通过注意第一种方式B
的地址是delete
的地址 - 请注意您的类不是多态类型 - 来推测原因,但是第二种情况不是。而delete d;
d->f();
只是释放记忆,而不是立即擦拭它。)
但实际上,
class D : public B, public C
不会结束。即使您确实交换了这些语句的顺序,也应该使基类析构函数 virtual ,以便删除正确的内存。
除其他事项外, class D : public C, public B
和D
会在构建class Typer {
constructor(typingSpeed, content, output) {
this.typingSpeed = typingSpeed;
// Parses a NodeList to a series of chained promises
this.parseHtml(Array.from(content), output);
};
makePromise(node, output) {
if (node.nodeType == 1) // element
{
// When a new html tag is detected, append it to the document
return new Promise((resolve) => {
var tag = $(node.outerHTML.replace(node.innerHTML, ""));
tag.appendTo(output);
resolve(tag);
});
} else if (node.nodeType == 3) // text
{
// When text is detected, create a promise that appends a character
// and sleeps for a while before adding the next one, and so on...
return this.type(node, output, 0);
} else {
console.warn("Unknown node type");
}
}
parseHtml(nodes, output) {
return nodes.reduce((previous, current) => previous
.then(() => this.makePromise(current, output)
.then((output) => this.parseHtml(Array.from(current.childNodes), output))), Promise.resolve());
}
type(node, output, textPosition) {
var textIncrement = textPosition + 1;
var substring = node.data.substring(textPosition, textIncrement);
if (substring !== "") {
return new Promise(resolve => setTimeout(resolve, this.typingSpeed))
.then(() => output.append(substring))
.then(() => this.type(node, output, textIncrement));
}
return Promise.resolve(output);
}
}
时交换构建基类的 order 。