C++ 从基类调用派生类中的函数,无需强制转换,也无需虚拟方法

C++ Call a function in a derived class from base class without casting and no virtual methods

提问人:Xaq 提问时间:2/20/2015 最后编辑:Xaq 更新时间:2/21/2015 访问量:806

问:

我最近在一次采访中被问到这个问题:

#include <iostream>
class Base
{
public:
    void foo() { std::cout << "foo" << std::endl; }
};

class Derived : public Base
{
public:
    void bar() { std::cout << "bar" << std::endl; }
};

int main(int argc, const char *argv[])
{
    Base *p = new Derived;
    // additional code here
    return 0;
}

问题的条件是不能更改 Base 类和 Derived 类(例如,更改方法的名称、添加其他方法或将方法更改为 virtual)。 进一步的限制是不能使用任何类型的铸件。 必须使用指针 p。 除此之外,您可以编写任何其他代码,包括尽可能多的类,以确保使用 p 指向的对象调用“bar()”方法。

鉴于不允许演员阵容,我唯一能想到的是一个老派的:

Derived *d;
memcpy(&d, &p, sizeof p);
d->bar();

这甚至比石膏还要糟糕。 面试官斥责我,告诉我我甚至没有最基本的对象层次结构知识,因为我看不到这个问题的非常明显、微不足道的解决方案。

如果这个问题是重复的,我深表歉意;我见过关于从基类访问派生类中的方法的其他问题,但在我看到的所有情况下,答案都涉及对任何一个类的强制转换或修改。

他可能是对的;我已经用 C++ 编程超过 15 年了,但我看不到解决方案。可能是我从未遇到过它,因为我在这种情况下会使用强制转换:在这种情况下,它必须是一个static_cast,因为没有虚拟方法(甚至没有析构函数)可以允许dynamic_vast编译(它失败并显示一条消息:“'Base' is not a polymorphic type”

C++ 继承

评论

7赞 juanchopanza 2/20/2015
请修复代码,使其编译并有意义(例如,Base 和 Derived 之间是什么关系?
1赞 Blob 2/20/2015
也许关键是要让 Derived 派生自 Base?
1赞 juanchopanza 2/20/2015
你有什么理由不想修复代码吗?
2赞 T.C. 2/20/2015
你有没有问过面试官他的解决方案是什么?
2赞 Xaq 2/21/2015
对不起 -- 我修复了继承

答:

6赞 Luchian Grigore 2/20/2015 #1

简单行:

#define Base Derived

就在.(然后你可以调用它)mainbar

评论

2赞 chris 2/20/2015
我只能说哇。我讨厌那个面试问题。
1赞 juanchopanza 2/20/2015
仍然不会编译;-)
4赞 Luchian Grigore 2/20/2015
@chris回答这个问题,然后站起来说“如果这是你做事的方式,我可能不适合这里”:)
0赞 chris 2/20/2015
@LuchianGrigore,如果我不是一个绝望的合作学生,也许:)我大概能忍受四个月。
0赞 Xaq 2/21/2015
我不认为这是证明 C++ 继承的“简单、明显”知识的答案。
-1赞 Teddy Engel 2/20/2015 #2

答案可能是:这不会编译。

评论

0赞 Xaq 2/21/2015
我忘了指出 Derived 是公共基础。我现在已经修好了。
1赞 Blob 2/20/2015 #3

必须使用指针 p。除此之外,您可以编写任何其他代码,包括尽可能多的类,以确保使用 p 指向的对象调用“bar()”方法。

你说,有多少节课?

#include <iostream>

class Base
{
public:
    void foo() { std::cout << "foo" << std::endl; }
};

class Derived : public Base
{
public:
    void bar() { std::cout << "bar" << std::endl; }
};

int main()
{
    class Base
    {
    public:
        void bar() { std::cout << "bar" << std::endl; }
    };
    class Derived : public Base
    {
    };
    Base *p = new Derived;
    p->bar();
}

评论

1赞 Christian Hackl 2/20/2015
好吧,那是作弊。因为你实际上不再使用指针了,所以你把它变成了别的东西。虽然这位面试官可能已经预料到了这个答案。我想我们永远不会知道。
0赞 Blob 2/20/2015
@ChristianHackl 面试官肯定没想到答案。#define Base Derived
0赞 Christian Hackl 2/20/2015
是的,当然,这个答案也存在同样的问题。也许面试官确实预料到了,也许他没有。这只是一个非常愚蠢的问题,我为浪费时间试图进入这样的公司的 OP 感到难过。
0赞 Luchian Grigore 2/20/2015
“这个问题的条件是 Base 和 Derived 类不能被改变”——肯定会改变类。class Derived : public Base
2赞 Christian Hackl 2/21/2015
@Xaq:或者他想测试你对这个问题的反应,看看你如何应对上级薄弱的技术能力。这还是很差的面试风格。正如我所说,很高兴你不必在那里工作,并找到一家更好的公司,一家不与候选人玩游戏的公司。
0赞 Deduplicator 2/20/2015 #4

您可以使用 for type-punning:union

union {
    Base* bp;
    Derived* dp;
} my_union = {p};
my_union.dp->bar();

评论

0赞 Xaq 2/21/2015
我可以,但这基本上执行与memcpy相同的操作 - 不是他想要的
0赞 trenki 2/21/2015
那么他在寻找什么呢?
0赞 Xaq 2/21/2015
@trenki 他拒绝给我答案,只是说我可以根据需要创建尽可能多的新类来解决问题。我还实现了一个 MyDerived 类,该类是公共 Derived 的,并将 Base * 作为构造函数的参数,并使用 placement new 来使用它,但他也不喜欢这样。他一直说这很简单,我需要实现一些额外的类(不是在主类中)
2赞 463035818_is_not_an_ai 2/21/2015 #5

我可能会想出这样的东西:

void foobar(Base* b){
   Derived d;
   d.bar();    
}

int main(int argc, const char *argv[]){
    Base *p = new Derived;
    foobar(p);
    return 0;
}

如果面试官抱怨这太笨拙了,我会请他少问一些古怪的问题:P

不,真的,我确实认为这是对一个相当学术问题的有效回答。我正在使用 p 指向的对象(调用 foobar 函数)并确保调用了“bar()”。我不认为这样的练习值得一个更复杂的解决方案。从逻辑上讲,我的解决方案无法与面试官想到的任何解决方案区分开来。

评论

0赞 Deduplicator 2/21/2015
好吧,我认为这个问题实际上实际上要求一个“最严重的滥用规则”——答案,所以 +1。IOCCC 向您致意,这次是用 C++ 语言。