【问题标题】:c++ Class inheritance with functionsc ++类继承与函数
【发布时间】:2012-07-08 14:28:05
【问题描述】:

我一直在为考试学习 c++,我以为我已经理解了大多数 c++ 常见的误解,但我在过去的考试中遇到了一个让我发疯的练习,它结合了虚拟方法和继承以一种我似乎不明白的方式是代码:

    #include <iostream>

    class B;

class A {
    public:
    virtual A* set(A* a) = 0;
};

class B : public A {
    public:
    virtual A* set(B* b) {
            std::cout << "set1 has been called" << std::endl;
            b = this;
            return b;
    }

    virtual B* set(A* a) {
            std::cout << "set2 has been called" << std::endl;
            a = this;
            return this;
    }
};

int main(int argc, char *argv[]) {
    B *b = new B();
    A *a = b->set(b);
    a = b->set(a);
    a = a->set(b);
    a = a->set(a);
    return 0;
}

输出是

set1 has been called
set2 has been called
set2 has been called
set2 has been called

从我收集到的第一个调用 (b->set(b) ) 调用 B 类的第一个方法并返回 b 本身,然后这个 objectref 被强制转换为 A 意味着现在对象 b 现在是类型一种? 所以我有 A *a = A *b; 现在对我来说我应该调用 set A 是有道理的,因为我脑子里有这种情况 objectoftypeA-&gt;set(objectoftypeA) 所以我不应该研究虚拟方法,因为这两个对象是基类?

无论如何,你可以看到我有很多困惑,所以如果我犯了愚蠢的错误,我会很高兴,如果有人能解释这段代码发生了什么,我试图搜索网络,但我发现只有一个小而简单的例子不要惹麻烦。

【问题讨论】:

  • 如果您描述了您期望的内容,将会很有帮助。您刚刚向我们抛出了很多代码,并在一个连续的句子中描述了您确定的内容,这有点难以理解。很难知道从哪里开始回答。
  • 我希望第二次调用会再次调用 set1,但事实并非如此,所以我猜我的整个思路都是错误的
  • 对此我不太确定,但这个例子确实令人困惑。 IMO 我认为virtual B* set(A* a) 别名set2 将充当virtual A* set(A* a) = 0; 的实现,因为B* 可以隐式转换为A*。因此,一旦您收到来自set1A*,就会调用implmentatino,即set2。编辑:请注意,set1 由于其特殊参数而无法实现纯虚函数。尝试注释掉每个函数。我猜如果set2 不存在,`B *b = new B();` 会导致错误,但如果set1 不存在则不会。
  • 请不要自己写这样的代码:)
  • @jrok 这在现实生活中很容易发生,尽管在混合重载和覆盖时应该始终小心。 C++11提供了伪关键字finaloverride来防止不小心写出这种东西。

标签: c++ inheritance pointers virtual-functions


【解决方案1】:

该程序演示了如何查找成员函数。对象的静态类型决定了将被调用的函数重载:它执行名称查找。然后动态类型确定被调用的虚拟覆盖。

也许关键是同名的不同overlod实际上是不同的函数。

由于A 只有一个set 成员,所以无论参数是什么,当您调用a-&gt;set() 时,只会发生一件事。但是当你调用b-&gt;set() 时,有几个潜在的功能,并且选择了最好的一个。

因为B::set 永远不会被覆盖,所以它是否是virtual 没有区别。 virtual 同一班级的成员根本不互相交谈。

【讨论】:

  • @Cheersandhth.-Alf 是的,就是这样...... 90% 的工作都在编程方面之外。
【解决方案2】:

Potatoswatter 是对的,但我想我有一点“更清楚”的解释。我认为 OP 对动态类型查找与编译时在运行时发生的情况以及向上转换自动发生时发生的情况以及不发生时发生的情况感到困惑。

首先,返回类型不会影响调用哪个重载。你可能知道这一点,但它需要重复。返回类型不匹配会在编译时导致错误,但不会在运行时导致错误,并且不会影响调用哪个重载。另外值得注意的是,只要它是兼容的指针类型(在一个层次结构中),返回一个指针就不会“改变”它。它仍然是同一个指针,与将浮点数转换为整数不同,后者有实际变化。

现在来一一处理电话。这是我对过程的理解,不一定是标准,或者“真正”发生了什么。

当您调用 b-&gt;set(b) 时,编译器(不是运行时)会“寻找一个名为 set 的方法,其参数为 pointer to B”,它会找到与输出set1的那个。它是虚拟的,所以有代码检查类是否指向更低的对象,但没有,所以它只是调用它,并将this 指针返回到a

现在你打电话给b-&gt;set(a)。再次是编译器“b 是否有一个将 指针指向 A 的重载?”是的,它确实如此,所以它调用了“set2”方法。看到A* 的是编译器,因此此时调用是“确定的”。即使指针指向 B 类型的对象,编译器也不知道,也不关心。因此,参数的编译时类型决定了采用哪个重载方法。从那时起,在层次结构中,virtual 的位置位于this 指针的底层类型上,但仅向下。

但这里有不同的情况。试试这个:b-&gt;set(dynamic_cast&lt;B*&gt;(a)) 这应该调用“set1”方法,因为编译器肯定会有一个指向 B 的指针(即使它是 nullptr)。

现在是第三种情况:a-&gt;set(b)。这里发生的是编译器说“只有一个 set 方法,所以参数可以向上转换或构造为该类型吗?”答案是肯定的,因为BA 的孩子。所以这种转换是透明地发生的,编译器为 A 类型的 set 方法调用 ABSTRACT 调度程序。这发生在编译时 a 是指针的“真实”类型之前。然后在运行时,程序“遍历虚拟”并找到最低的一个,即发出“set2”的B-&gt;set(A*) 方法。不使用参数所指向的实际类型,只使用箭头运算符左侧的类型,这仅决定层次结构的向下程度。

第四个案例又是第三个。参数的类型(指针,而不是指向的 whta)是兼容的,所以它和以前一样。如果您想对此进行戏剧性的演示,请尝试以下操作:

a->set((A*)nullptr) // prints "set2 has been called"
b->set((A*)nullptr) // prints "set2 has been called"
b->set((B*)nullptr) // prints "set1 has been called"

参数指向的基础类型不影响动态调度。只有它们的“表面”类型会影响调用的重载。

【讨论】:

  • 我终于明白了,你的解释更简单,更针对我对c++的不了解我希望我的老师能这样解释,有些人忘记了每个人都是从没有知识开始的,需要一些事情的演练
猜你喜欢
  • 1970-01-01
  • 2015-06-23
  • 2012-09-20
  • 2012-10-16
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2012-10-30
  • 2014-12-27
相关资源
最近更新 更多