4

我无法理解导致 C++ 和 C# 之间这种差异的原因。

首先,我们有一个基类包含虚函数的示例。

class Base
{
protected:
    int super;
public:
    virtual int f() = 0;
};

class Derived : public Base
{
public:
    int extraA;
    int f(){ return 1; }
};

int main()
{
    Derived *d = new Derived();

    std::vector<Base*> v;
    v.push_back(d);

    for(int i=0; i < v.size() ;i++)
    {
            // Output "Derived"
            std::cout << typeid(*v[i]).name() << std::endl;
    }

    return 0;
}

正如预期的那样,它的输出是“Derived”。

如果我们删除 f(),这将不再有效。输出是“基础”。例子:

class Base
{
protected:
    int super;
};

class Derived : public Base
{
public:
    int extraA;
};

int main()
{
    Derived *d = new Derived();

    std::vector<Base*> v;
    v.push_back(d);

    for(int i=0;i<v.size();i++)
    {
            // Output "Base"
            std::cout << typeid(*v[i]).name() << std::endl; 
    }

    return 0;
}

我对此的理解是,拥有一个虚函数会导致编译器向对象添加一个 vptr,该对象指向 vtable。vtable 包含要调用的正确函数的地址 (Derived::f()) - (以及对象的类型信息?)

现在是有趣的部分 - 与 C# 的比较。在这里,“Base”和“Derived”基本上是类似于第二个 C++ 示例的空类:

public static void Main()
{
        Derived d = new Derived();
        IList<Base> v = new List<Base>();
        mList.Add(d);

        for (int i = 0; i < v.Count; i++)
        {
            // Output: "Derived"
            System.Console.WriteLine(v.ElementAt(i).GetType()); 
        }
}

因此,我的问题是:我对 C++ 部分的理解是否正确,当 C++ 不正确时,C# 如何正确识别对象的类型?

4

3 回答 3

7

正如您所说:C++ 仅在您的类具有virtual函数时才启用运行时多态性和类型识别,这意味着(在常见的实现中)将 avptr添加到类中(这与 C++ 的哲学相一致“您无需支付你不需要的东西”)。

(以及对象的类型信息?)

然而,在类的 vtable 的第一个槽中存储指向 RTTI 记录的指针是很常见的——我想说这是标准要求 RTTI 仅在类是多态的情况下才能工作的原因之一(尽管,像往常一样,这一切都取决于编译器)。

顺便说一句,RTTI 并不是虚拟调度正常工作所必需的,如果你调用一个虚函数,编译器所要做的就是call ptr使用从 vtable 的正确槽中获取的指针;RTTI 记录仅在检查类层次结构dynamic_cast以及通过typeid.

相反,在 C# 中,默认情况下每个类都是多态的,并且具有与之关联的反射元数据,因此无需执行任何特定操作来启用多态/类型识别。

于 2012-03-15T20:43:32.183 回答
3

在 C++ 中,运行时类型信息仅适用于层次结构中的父类至少具有一个虚函数的类型。“vtable”指针指向虚函数表并标识类型。(原则上,至少,我不记得该标准对如何实现虚函数进行了严格的规定)如果根本没有虚函数,那么为了提高效率,这些信息就被忽略了。

在 C# 中,类型信息总是存在的,不管是否有虚函数。

于 2012-03-15T20:44:40.347 回答
2

C++ 和 C# 之间的差异是深刻而巨大的,这只是差异百科全书中的一个脚注。

也就是说,在 C# 中,每个类都必须继承自具有虚函数的 Object,因此 C# 永远不会出现对象没有虚函数的情况。但是,C++ 可以。通常。所以在 C++ 中没有地方放置运行时类型标识信息。

于 2012-03-15T20:45:17.830 回答