【问题标题】:What's the issue with malloc() and virtual functions? [duplicate]malloc() 和虚函数有什么问题? [复制]
【发布时间】:2011-03-15 12:15:26
【问题描述】:

可能重复:
C++: why is new needed?

为什么当我的对象是包含虚函数的类的子对象时,我不能使用 malloc 为它们分配空间?这真是令人沮丧。有什么好的理由吗?

下面的程序说明了这个问题。它在第 27 行出现段错误,我在这里调用 aa->f()

#include <iostream>
#include <cstdlib>

class A 
{
public:
    virtual int f() {return 1;}
};

class B 
{
public:
    int f() {return 1;}
};

class Aa : public A {};

class Bb : public B {};

int main()
{
    Aa* aa = (Aa*)malloc(sizeof(Aa));
    Aa* aan = (Aa*)new Aa();
    Bb* bb = (Bb*)malloc(sizeof(Bb));
    std::cout << bb->f() << std::endl;
    std::cout << aan->f() << std::endl;
    std::cout << aa->f() << std::endl;
    return 0;
}

版本信息:g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5

【问题讨论】:

  • @Tomalak:如果可以的话,你应该投票关闭重复(我相信你可以使用 5k 代表)
  • 答案很简单。 new 是在 C++ 中创建新类实例的正确方法。 malloc 不是。
  • @David:这不是重复的。答案相同并不意味着问题相同。

标签: c++ malloc virtual-functions


【解决方案1】:

实现虚函数的常用方法是在对象的负偏移处有一个指向“虚表”或 vtable 的指针。需要这张表来确定要调用的虚函数。这就是为什么仅仅 malloc'ing 空间不起作用的原因。

【讨论】:

  • 嗯,是的。这就是我说的,不是吗?我明确地说了一个指向虚拟表的指针,而不是虚拟表本身......
  • 我也误读了您的回答。我认为英语是模棱两可的。它可以被读取(如您所愿)“......指向'虚拟表'的指针,该指针位于负偏移量”或者它可能被误读为“指向 vtable 的指针,该 vtable 处于负偏移量” .仍然值得 +1。
【解决方案2】:

malloc 只分配内存,不创建对象。所以,这条线

Aa* aa = (Aa*)malloc(sizeof(Aa));

分配足够大的内存区域以容纳A,但包含垃圾。正如其他人指出的那样,这也意味着不会设置指向vtable 的指针(我从@David Rodríguez 对另一个答案的评论中得到了那个),这是调度对虚拟函数的调用所必需的。由于B 不包含虚函数,所以不会出现这样的问题。 B 也会发生这种情况,但是,如果 B 包含任何需要初始化的数据成员,例如:

class B 
{
public:
    B() : foo(new int()) {}

    int f() {return *foo;}
private:
    int * foo;
};

线

Aa* aan = (Aa*)new Aa();

可以不用演员表:

Aa* aan = new Aa();

【讨论】:

    【解决方案3】:

    原因是malloc 对 C++ 构造函数一无所知,因此不会调用它们。您可以使用placement new 自己调用构造函数:

    int main()
    {
        Aa* aa = (Aa*)malloc(sizeof(Aa));
        new(aa)Aa;
    
        Aa* aan = (Aa*)new Aa();
    
        Bb* bb = (Bb*)malloc(sizeof(Bb));
        new(bb)Bb;
    
        std::cout << bb->f() << std::endl;
        std::cout << aan->f() << std::endl;
        std::cout << aa->f() << std::endl;
    
        aa->~Aa();
        free(aa);
    
        delete aan;
    
        bb->~Bb();
        free(bb);
    
        return 0;
    }
    

    请注意,您必须在释放此类内存之前手动调用析构函数。

    【讨论】:

    • +1 有人为我对 Placement New 的小幅回复给了 +1...你的代码真的不可读:-) :-) 这里和那里的一些空行可能会有所帮助。
    【解决方案4】:

    不要使用 malloc,使用 new - malloc 不调用构造函数。

    当您执行A * a = new A(); 时,编译器将分配内存,为 A 设置 vtable 指针并调用构造函数。当你调用一个虚函数时,vtable 是用来实际查找函数的。

    当您执行A * a = (A *) malloc(...); 时,编译器将分配内存,其中将包含随机数据。当你调用一个虚函数时,它会查看(垃圾)vtable并调用一些随机位置。

    具有虚函数的类在内部看起来是这样的:

    struct Foo {
      void * vtable;
      int aClassMemberVar;
    };
    

    调用虚函数查看“隐藏”的vtable指针,它指向class vtable,一个指向函数的指针链表。所以这个 vtable 指针必须被初始化,malloc 不会这样做。

    【讨论】:

    • 或者创建vtable,也就是这里的相关项。
    • 我认为区分虚拟方法表(每个类型唯一)和vtable指针(每个对象)很重要。在我知道的所有使用 vtable 的实现中,无论是否实例化该类型的任何对象,该表都存在。这里的特殊问题是该特定对象中的 vtable 指针 未初始化,当尝试调用任何虚拟方法时,位于随机地址的内存将被解释为 vtable i>.
    • @Tomalak: new 不会创建 vtable。
    • 我没有这么说。 Malloc 也不创建它。
    • @Tomalak:malloc、new 和构造函数都不会创建虚拟方法表(在我见过的所有实现中)。 vtable 通常在编译时创建,然后在链接时和操作系统将二进制文件加载到内存时进行调整,但它在任何该类型对象的任何实例化之前就存在。
    【解决方案5】:

    肯定是因为虚函数表没有正确创建?

    【讨论】:

    • 不会设置指向表的指针,但系统中会存在该表。
    • @David:说同样的事情,这种方式稍微好一点! :)
    【解决方案6】:

    malloc 不调用类的构造函数,因此您的对象未正确初始化,因此出现段错误。使用 C++ 时使用 new 分配内存。顺便说一句,不需要转换从new 返回的指针。

    【讨论】:

      【解决方案7】:

      很好的理由是称为虚拟表。具有虚方法的类型的对象有一个指针表,指向要调用的实际虚方法的地址。这些被称为虚拟表或 v-tables。

      【讨论】:

        猜你喜欢
        • 2011-06-16
        • 1970-01-01
        • 1970-01-01
        • 2016-12-02
        • 2013-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多