2

我一直在为考试学习 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 = A *b; 现在对我来说应该调用 A 集是有意义的,因为我脑子里有这种情况, objectoftypeA->set(objectoftypeA)所以我不应该研究虚拟方法,因为这两个对象是基类?

无论如何,正如您所看到的,我有很多困惑,所以如果我犯了愚蠢的错误,请多多包涵.

4

2 回答 2

4

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

也许关键是同名的不同overlods实际上是不同的功能。

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

由于B::set永远不会被覆盖,因此无论是否覆盖都没有区别virtualvirtual同一班级的成员根本不互相交谈。

于 2012-07-08T14:38:18.600 回答
2

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

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

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

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

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

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

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

第四种情况又是第三种。参数的类型(指针,而不是指向的 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"

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

于 2012-07-08T16:00:39.757 回答