mixin类中的多态 - 虚函数

时间:2015-10-31 09:48:40

标签: c++ polymorphism mixins virtual-functions

我目前正在阅读关于mixin课程的内容,我认为我或多或少都会听取所有内容。我唯一不明白的是为什么我不再需要虚拟功能。 (请参阅herehere

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()?!?

3 个答案:

答案 0 :(得分:0)

  

N-&gt;设置(84); //这里调用哪个函数?

Number::set将在此处调用。

  

多态性是否适用于foo()?!?

不,没有virtual。如果您尝试使用该代码,则会得到一个未指定的值,因为根本不会设置before

LIVE

答案 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}}。