【问题标题】:If in malloc object is not created then why does this code works?如果没有在 malloc 中创建对象,那么为什么这段代码有效?
【发布时间】:2015-03-19 17:16:30
【问题描述】:

当我们在 c++ 中对类使用 malloc/free 时,不会创建对象。那么为什么这段代码有效呢? 如果一个对象没有被创建,那么它一定不能给出下面提到的输出。

class Test
{
public:
    Test()
    {
        cout << "Test : ctor\r\n";
    }
    ~Test()
    {
        cout << "Test : dtor\r\n";
    }
    void Hello()
    {
        cout << "Test : Hello World\r\n";
    }
};

int main()
{

    cout << "2\n";
    Test* t2 = (Test*) malloc(sizeof Test);
    t2->Hello();
    free(t2);

    return 0;
}


OUTPUT:
Hello World

【问题讨论】:

  • 你的代码不起作用,因为你的构造函数和析构函数中的 cout 语句没有被执行。
  • 为什么在 C++ 代码中使用malloc? (还有演员表!)

标签: c++


【解决方案1】:

未定义的行为是简短的回答。长答案是,因为您的类没有直接调用它们的虚拟方法,所以只是一个带有隐式 this 参数的函数调用,该参数指向您分配的内存。也就是说,如果您的任何方法要访问 this,则会导致更多未定义行为,因为尚未构造该对象。

从哥伦布和 LRIO 公然盗取:

[C++11: 3.8/1]: 对象的生命周期是对象的运行时属性。如果一个对象是一个类或聚合类型,并且它或其成员之一由非平凡默认构造函数的构造函数初始化,则称该对象具有非平凡初始化。 [ 注意: 由普通复制/移动构造函数进行的初始化是非普通初始化。 ——尾注]

关键位是it,指向对象的指针可以包含数据,而不仅仅是存储成员的地方。由实现决定它是什么。

【讨论】:

  • @LightnessRacesinOrbit 很公平。但如果没有确切地解释发生了什么,它就不是问题的答案,而只是一个评论。
  • @Mgetz 你能否指出这个类的对象应该如何构造以及它如何影响函数的调用?
【解决方案2】:

当我们在 c++ 中对类使用 malloc/free 时,不会创建对象。那为什么这段代码有效呢?

这取决于您如何定义“作品”,这是一个非常模糊的术语,在此类问题中您通常应该避免使用它。

我认为代码“工作”:它有一个错误。它被打破。在任何给定的执行运行中,它都可能失败。它可能会崩溃。它可以打开一个虫洞。

什么是错误?错误是您正在调用未定义的行为。您为Test 对象分配了内存,但您从未创建过Test 对象来放置。然后,您对内存执行操作,就好像它包含 Test 对象一样,但它没有。

[C++11: 12.1/5]: [..]如果不是用户提供的默认构造函数是微不足道的[..] 否则默认构造函数是非-琐碎。

[C++11: 3.8/1]: 对象的生命周期是对象的运行时属性。 如果一个对象是类或聚合类型,并且它或其成员之一由非平凡默认构造函数的构造函数初始化,则称该对象具有非平凡初始化。 strong> [ 注意: 由普通的复制/移动构造函数初始化是非普通的初始化。 —结束注释] T 类型对象的生命周期开始于:

  • 获得了具有适合 T 类型的正确对齐和大小的存储,并且
  • 如果对象有非平凡初始化,则其初始化完成

T 类型对象的生命周期在以下时间结束:

  • 如果 T 是具有非平凡析构函数 (12.4) 的类类型,则析构函数调用开始,或者
  • 对象占用的存储空间被重用或释放。

[C++11: 3.8/3]: 本国际标准中赋予对象的属性仅在其生命周期内适用于给定对象。 [..]

[C++11: 3.8/5]: 在对象的生命周期开始之前,但在分配对象将占用的存储空间之后 [..] 任何指向该对象所在存储位置的指针或被定位可以使用,但仅限于有限的方式。 [..] 这样的指针可能会被取消引用,但生成的左值只能以有限的方式使用,如下所述。如果出现以下情况,则程序具有未定义的行为:

  • [..]
  • 指针用于访问非静态数据成员或调用对象的非静态成员函数
  • [..]

你看到的是机会成功:你的程序没有错误的假象。这是因为你的计算机执行的不是C++程序,而是机器码,有时在从C++抽象到机器码的翻译过程中,会丢失先决条件的破坏。

更具体地说,您的计算机并不关心您的程序是否以这种特定方式被破坏,因为它不需要做任何依赖于您现有对象的事情。这些成员函数不会触及任何实例数据,因此您的计算机永远不会意识到代码是错误的。

这并不意味着代码“有效”。

【讨论】:

    【解决方案3】:

    来自 C++11 FD 的所有引用。

    您的代码几乎没问题。问题是您的类具有非平凡的初始化,因为它具有用户提供的因此非平凡的默认构造函数 - [basic.life]/1:

    如果一个对象属于 类或聚合类型,它 [..] 由 一个普通的默认构造函数以外的构造函数。

    因此该对象在整个程序中都不是活动的,这在上面的引用之后得到了澄清:

    T 类型对象的生命周期开始于:

    • 存储与 获得类型 T 的正确对齐和大小,并且
    • 如果对象 有非平凡的初始化,它的初始化已经完成。

    ...因此根据 [basic.life]/5 的 UB:

    在对象的生命周期开始之前但在存储之后 对象将占用的已分配 [..] 任何指针 指对象将或曾经位于的存储位置 可以使用,但只能以有限的方式使用。 [..] 否则,这样的指针 指分配的存储(3.7.4.2),并且使用指针就像 指针类型为void*,定义明确。这样的指针可能是 取消引用,但生成的左值只能在有限的情况下使用 方式,如下所述。如果出现以下情况,则程序具有未定义的行为:

    • 指针用于访问非静态数据成员或调用 对象的非静态成员函数,或

    【讨论】:

    • 请引用您的来源。 Chapter & verse,那种东西。
    • @LightnessRacesinOrbit 当然,妈妈
    • @LightnessRacesinOrbit “好的,马上”。我不认为你是我母亲的形象,别担心。 (完成,顺便说一句)
    猜你喜欢
    • 1970-01-01
    • 2014-02-02
    • 2012-12-03
    • 2015-02-22
    • 1970-01-01
    • 2019-07-26
    • 2018-05-05
    • 2013-12-17
    • 1970-01-01
    相关资源
    最近更新 更多