我目前正在阅读关于mixin课程的内容,我认为我或多或少都会听取所有内容。我唯一不明白的是为什么我不再需要虚拟功能。 (请参阅here和here)
E.g。 greatwolf在his answer写道,不需要虚函数。这是一个例子:(我刚刚复制了基本部分)
struct Number
{
typedef int value_type;
int n;
void set(int v) { n = v; }
int get() const { return n; }
};
template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE
{
typedef T value_type;
T before;
void set(T v) { before = BASE::get(); BASE::set(v); }
void undo() { BASE::set(before); }
};
typedef Undoable<Number> UndoableNumber;
int main()
{
UndoableNumber mynum;
mynum.set(42); mynum.set(84);
cout << mynum.get() << '\n'; // 84
mynum.undo();
cout << mynum.get() << '\n'; // 42
}
但是,如果我做这样的事情,现在会发生什么:
void foo(Number *n)
{
n->set(84); //Which function is called here?
}
int main()
{
UndoableNumber mynum;
mynum.set(42);
foo(&mynum);
mynum.undo();
cout << mynum.get() << '\n'; // 42 ???
}
mynum有什么价值?为什么?多态性是否适用于foo()
?!?
答案 0 :(得分:0)
N-&gt;设置(84); //这里调用哪个函数?
Number::set
将在此处调用。
多态性是否适用于foo()?!?
不,没有virtual
。如果您尝试使用该代码,则会得到一个未指定的值,因为根本不会设置before
。
答案 1 :(得分:0)
我在VS 2013中编译了你的代码,它给出了一个未指定的数字。
你的struct中没有构造函数,这意味着之前的变量没有被初始化。
答案 2 :(得分:0)
您的代码示例调用未定义的行为,因为您尝试在int
变量n
处于有效状态时进行读取。问题不在于印刷什么价值。您的程序不需要打印任何内容,也不需要做任何有意义的事情,尽管您可能正在使用一台机器,在该机器上,未定义的行为只会在n
中显示为一个看似随机的值,或者它将主要出现在该机器上为0。
如果您允许它检测到此类问题,您的编译器可能会给您一个重要提示,例如:
34:21: warning: 'mynum.Number::n' is used uninitialized in this function [-Wuninitialized]
但是,未定义的行为甚至在此之前就开始了。这是如何发生的,一步一步:
UndoableNumber mynum;
这也会使用未初始化的Number
创建n
子对象。 n
类型为int
,因此可以将其各个位设置为所谓的trap representation。
mynum.set(42);
这会调用派生类set
函数。在set
内部,尝试将before
成员变量设置为未初始化的n
值,并带有可能的陷阱表示:
void set(T v) { before = BASE::get(); BASE::set(v); }
但你无法安全地做到这一点。 before = BASE::get()
部分已经错误,因为Base::get()
使用可能的陷阱表示复制int
。 这已经是未定义的行为。
这意味着从现在开始,C ++作为编程语言不再定义会发生什么。关于你的其他计划的推理是没有意义的。
尽管如此,让我们假设片刻,副本一切正常。之后还会发生什么?
调用 Base::set
,将n
设置为有效值。 before
仍然处于之前的无效状态。
现在调用foo
:
void foo(Number *n) { n->set(84); //Which function is called here? }
base -class set
被调用,因为n
的类型为Number*
,set
为非虚拟强>
set
愉快地将n
成员变量设置为84.派生类before
仍然无效。
现在调用undo
函数并执行以下操作:
BASE::set(before);
完成此分配后,n
不再是84,而是设置为无效的before
值。
最后......
cout << mynum.get() << '\n';
get
返回无效值。你试着打印它。即使在没有int
的陷阱表示的机器上(您很可能使用这样的机器),这也会产生未指定的结果。
结论:
C ++作为一种语言并未定义您的程序的功能。它可以打印一些东西,不打印,崩溃或做任何感觉,都是因为你复制了一个未初始化的int
。
在实践中,在典型的最终用户机器上崩溃或做任何感觉都不太可能,但它仍然未定义将要打印的内容。
如果您希望在set
上调用派生类Number*
,则必须在{set
中设置virtual
Number
函数1}}。