Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么成员函数指针不同于C++;?_C++_Oop_Multiple Inheritance_Language Lawyer_Member Function Pointers - Fatal编程技术网

C++ 为什么成员函数指针不同于C++;?

C++ 为什么成员函数指针不同于C++;?,c++,oop,multiple-inheritance,language-lawyer,member-function-pointers,C++,Oop,Multiple Inheritance,Language Lawyer,Member Function Pointers,起初,有C. C有结构、表达式和函数来封装它们。而且很好。 但C也有goto和switch的大小写,以及使用后的语法,所以可能不是很好 它也有指针,因为别名和指针运算而导致牙齿咬伤 但它也有函数指针,允许运行时调度,随之而来的是很多欢乐。 现在,数据可以指示代码,也可以指示数据,两者都是一流的(或相近的)。 对于任何可以被指向的东西,都可以用同一个指针指向:神圣的虚空* 所有人在荣誉上都是平等的 然后C++出现,并将数据和代码绑定到对象中。 瞧,这只是语法上的糖分,因为函数和方法没有太大区别,

起初,有C.
C有结构、表达式和函数来封装它们。而且很好。
但C也有goto和switch的大小写,以及使用后的语法,所以可能不是很好

它也有指针,因为别名和指针运算而导致牙齿咬伤
但它也有函数指针,允许运行时调度,随之而来的是很多欢乐。
现在,数据可以指示代码,也可以指示数据,两者都是一流的(或相近的)。
对于任何可以被指向的东西,都可以用同一个指针指向:神圣的虚空*

所有人在荣誉上都是平等的

然后C++出现,并将数据和代码绑定到对象中。 瞧,这只是语法上的糖分,因为函数和方法没有太大区别,
(不管太阳或神谕会告诉你什么)

Obj->Foo(int-val)与Foo(Obj*这个,int-val)大致相同,它们仍然相等,
在神圣的虚空之下*

然后,继承带来了冲突,因为外部派生类可能会添加到内部基类中。
但是,在这些简单的时间里,我们找到了一个解决方案:把每个基都放在派生之前。
然后,用同一个指针,我们可以同时指向孩子和父亲

然而,任何可以被指向的东西,都可以被指向神圣的虚空*

有了虚拟,我们失去了简单,徘徊了很久。
想知道如何处理钻石或不完全是椭圆的圆。
但即使我们抛弃了C的旧租户,每个指令都简化为简单的asm,
我们认为,有些事情应该看起来简单(即使在某种程度上,它们是复杂的)

因此,我们查看了出现的秘密VTables并认为“足够好了”。
因为虽然我们引入了隐藏数据,但降低了复杂性。
现在,通过虚拟机进行的任何呼叫都通过VTable重定向。
由于一个类的所有子对象都可以通过一个指针指向,这就足够了

但即使分派方法已经改变,我们仍然可以指向所有的东西,神圣的虚空*

但是,现在的许多人都认为一个严重的错误:多重继承。 一个指针已经不够了!两位基础父亲如何在一开始就成为父亲?
那么,VTable就不再足够了,因为我们怎么知道该指向哪个子对象呢
现在,钻石的问题比以前更严重,没有明显的解决办法,而我们以前的问题需要当前的代码来处理未来的前景

因此,每次虚拟调用都必须进行指针调整。
因为基类实际上可能是伪装的MI派生类,需要调整。
因此,为了支持少数使用MI的人,我们都付出了代价

突然间,神圣的虚空*再也无法储存迄今为止只是简单的糖了。
处理这种复杂性的方法是可怕的成员函数指针

beast需要自己的语法,因为没有其他语法可以满足。
这种语法很少使用,其优先级很低,以至于每次使用都需要Paran。
虽然在《神圣的标准》中,是什么邪恶的议会决定允许这种不幸的事情发生,
但是当从一个类型转换到另一个类型时,如果不调用大多数未定义的行为,就不会调用它

不仅如此,他们的语法和贪婪都是腐朽的,他们很胖,无法融入虚空*
因为知道指向什么对象的唯一方法是调整,
深入指针,并在每个VTable查找中进行检查

但是,兄弟们,情况并非如此。
实现的复杂性来自于一系列最特殊的决策

class Base1
{
public:
    virtual void foo();
};

class Base2
{
public:
    virtual void bar();
};

class Derived: public Base1, public Base2
{
public:
    void unrelated();
}
如图所示,调用foo()或bar()时必须调整派生的*;它不能同时指向Base1和Base2,就像简单的单继承一样。实际上,当从基类调用时,不可能正确预测需要多少偏移量,这就是为什么大多数人都有某种机制将其添加到vtable中

然而:

class Derived: public Base1, public Base2
{
public:
    void unrelated();

    virtual void foo() { Base1::foo(); }
    virtual void bar() { Base2::bar(); }
}
解决了这个问题,对原始对象模型没有任何更改
由于每个方法现在都存在,因此可以将其正确地添加到vtable中,并且在调用时,可以准确地知道要调整指针多少,从而允许调用继续进行,而不会造成任何麻烦
现在,无论是转换成员函数指针还是在转换时调用它,都定义得很好

最重要的是,任何可以指向的东西都可以被神圣的虚空指向*
所有成员函数指针都需要是一个普通函数指针,它接受一个特殊的第一个参数。
瞧,事情本来会很好的

要是我们生活在这样一个梦幻世界就好了

不幸的是,我们生活在胖成员函数指针、必须调整每次调用的vtable和无法正确创建委托或许多其他有用模式的成员函数指针中。在MSVC中,当您铸造它们时,它们会改变大小

这个问题是如此之大,以至于std::function可以在大多数实现中动态分配内存,因为这里列出了各种各样的问题。正如我所详述的,将thunks用于非越界方法将非常方便地解决这个问题,其代价是一些可内联的隐藏方法,一些可内联的更改,以及虚拟调度速度的可能(但很小)降低,以防函数未被覆盖,也无法完美内联

对于速度和空间的微小增长,我们已经在成员函数指针中创建了一个怪物,并且影响了六种其他语言不使用多重继承,尽管问题很容易解决,但阉割了成员函数指针