首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C++中,为什么编译器不理解基类对象在编译时指向哪个对象?

在C++中,为什么编译器不理解基类对象在编译时指向哪个对象?
EN

Stack Overflow用户
提问于 2017-09-05 03:05:18
回答 2查看 80关注 0票数 0

在C++中,为什么编译器不理解基类对象在编译时指向哪个对象?

为了前夫。

代码语言:javascript
复制
int Add(int nX, int nY)
{
    return nX + nY;
}
int Subtract(int nX, int nY)
{
    return nX - nY;
}

int main()
{
    // Create a function pointer and make it point to the Add function
    int (*pFcn)(int, int) = Add;
    cout << pFcn(5, 3) << endl; // add 5 + 3
    pFcn = Subtract;
    cout<< pFcn(5,3)<<endl // pefrom 5-3
    return 0;
}

上面的代码片段是后期绑定或动态绑定的示例。

在上面的示例中,只有在编译时我们才知道第一个Add函数将通过pFcn调用&然后减法函数将调用。那么,为什么它被调用为动态绑定的示例,即使编译器知道只在编译时调用哪个函数?

我的问题也是关于虚拟功能的。考虑跟随前任,

代码语言:javascript
复制
class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};
int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}                                               

在这里,动态绑定正在发生。要调用的函数是在运行时决定的。那么,为什么编译器不能在编译时决定只调用哪个函数呢?

EN

回答 2

Stack Overflow用户

发布于 2017-09-05 03:11:24

在一般情况下,使用函数指针请求后期绑定,但是一个足够聪明的编译器可以优化该请求和早期绑定,如果它能够证明只能绑定一个函数的话。这是就像-优化规则允许的:

  • 程序要求延迟绑定。
  • 但是早期绑定不会改变程序的可观察行为。早期绑定是可取的,因为它更快,使程序更小,并需要更少的内存(pFcn可以被删除)。
  • 因此,可以执行早期绑定。

相反,可以考虑在哪里传递函数指针,因为编译器无法早期绑定,因为它不够聪明,无法检测到是否有可能,或者因为它正在与无法观察到的代码交互:

代码语言:javascript
复制
using BinaryIntegerOperator = int (*)(int, int);

int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }

extern int accumulate(
    int initial,
    int *first,
    std::size_t count,
    BinaryIntegerOperator op
);

现在您可以传递AddSubtract作为第四个参数。被调用的函数可能不是同一个二进制文件的一部分(可能是动态链接库的一部分),因此后期绑定是不可避免的-- accumulate()应该已经由不同的编译器编译了。

票数 2
EN

Stack Overflow用户

发布于 2017-09-05 07:12:11

在编译系统能够证明可以调用一个函数的情况下,允许(但不需要)删除间接调用并直接调用该函数。这既适用于函数指针,也适用于虚拟方法调度。通常,对于虚拟方法,优化将作为链接时间优化的一部分进行,其中链接器意识到给定类的一个实例只有一个虚拟方法,或者可能没有覆盖虚拟方法的任何类。

我不确定这两种优化都被认为是极其重要的,但我希望这两种优化都发生在使用最好的编译器的简单情况下。(第一个示例将脱离相当标准的编译器优化技术。为vtable执行此操作几乎肯定需要一个特定的构建优化传递,因为编译器必须知道vtable在运行时是如何修改的,而不是如何修改的。)

一般来说,机制的使用简化了,并不意味着不使用命名机制。这就是为什么人们可能会说函数指针是动态绑定,即使编译器可以证明它没有有趣的动态行为。

考虑到上面更新的代码,请考虑以下几点:

代码语言:javascript
复制
int main(int argc, char **argv) {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    // Use test on argc to abstract arbitrary computation.
    Base *one_or_the_other = (argc > 1) ? bDerived : bBase;
    one_or_the_other->NonVirtual();
    one_or_the_other->Virtual();
}

编译器无法在编译时确定调用第二次分派的虚拟方法。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46046427

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档