【问题标题】:Forward declaration just won't do前向声明是行不通的
【发布时间】:2011-04-30 17:49:17
【问题描述】:

下面是两个代码片段(准备编译)。在第一个片段中,我只对结构使用前向声明,同时从 Guest 类的基类 dtor 中删除指向该结构的指针,但未调用。
在第二个片段中,我使用了这个 Guest 类的完整定义,而不是前向声明,在 Base 中使用 delete 来按预期工作。
为什么?为什么会有所作为?前向声明不应该只是编译器说明这个类/结构的定义在其他地方的注释吗?
我很惊讶它不能直观地工作。

//First just forward dclr  
#include "stdafx.h"
#include <iostream>
using std::cout;

struct Guest;

struct Base
{
    Guest* ptr_;
    Base(Guest* ptr):ptr_(ptr)
    {
        cout << "Base\n";
    }
    ~Base()
    {
        cout << "~Base\n";
        delete ptr_;
    }
};

struct Guest
{
    Guest()
    {
        cout << "Guest\n";
        throw std::exception();
    }
    Guest(int)
    {
        cout << "Guest(int)\n";
    }
    ~Guest()
    {
        cout << "~Guest\n";
    }
};

struct MyClass : Base
{
    Guest g;
    MyClass(Guest* g):Base(g)
    {
        cout << "MyClass\n";

    }
    ~MyClass()
    {
        cout << "~MyClass\n";
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        Guest* g = new Guest(1);
    MyClass mc(g);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what();
    }
    return 0;
}

//第二个-全高清

#include "stdafx.h"
#include <iostream>
using std::cout;

struct Guest
{
    Guest()
    {
        cout << "Guest\n";
        throw std::exception();
    }
    Guest(int)
    {
        cout << "Guest(int)\n";
    }
    ~Guest()
    {
        cout << "~Guest\n";
    }
};

struct Base
{
    Guest* ptr_;
    Base(Guest* ptr):ptr_(ptr)
    {
        cout << "Base\n";
    }
    ~Base()
    {
        cout << "~Base\n";
        delete ptr_;
    }
};



struct MyClass : Base
{
    Guest g;
    MyClass(Guest* g):Base(g)
    {
        cout << "MyClass\n";

    }
    ~MyClass()
    {
        cout << "~MyClass\n";
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        Guest* g = new Guest(1);
    MyClass mc(g);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what();
    }
    return 0;
}

【问题讨论】:

  • 你应该让你的基类构造函数在这两种情况下都是虚拟的。
  • @Space_C0wb0y:亲爱的,你不能在 C++ 中将构造函数设为虚拟 :)
  • @Armen: 话......烟雾和镜子。在我的脑海里是如此的清晰。 (对于那些想知道的人,应该是析构函数
  • @Space_C0wb0y:当然。这就是为什么那里有一个笑脸。 :)

标签: c++ forward-declaration


【解决方案1】:

来自 C++ 标准 (5.3.5/5):

如果要删除的对象在删除点具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义。

因此,您不能对不完整的类型使用 delete。它会调用析构函数,而编译器还不知道它。

【讨论】:

    【解决方案2】:

    通俗地说:编译器需要类定义才能正确删除对象,因为它需要知道如何为该类调用析构函数和/或operator delete

    正式地,5.3.5/5:

    如果被删除的对象有 不完整的类类型 删除和完整的类有一个 非平凡的析构函数或 释放函数,行为是 未定义。

    如果(例如)Guest 是 POD,你会没事的,但是你给了它一个析构函数,所以你不行。

    【讨论】:

      【解决方案3】:

      您不能删除指向不完整类型的指针。删除是需要类型完整的操作之一。高温

      【讨论】:

        【解决方案4】:

        除非您知道它的定义,否则您不能删除来宾。它的析构函数不会被调用。 此外,如果 Guest 定义了自定义运算符 delete,它将被忽略。

        【讨论】:

        • 不,声明就足够了。但是,前向声明是行不通的。
        • @kotlinski - class X { void f(); }; 这是 X 的定义,而 class X; 是声明
        • @kotilnski:恐怕你错了:X 现在是一个完整的类型。 X 的定义在那里。但是 X::f 尚未定义,只是声明了。一个类可以被定义,即使它的一些成员不是
        • @kotlinksi:术语检查。这是一个类声明:class Foo;,又名前向声明。这是一个类定义:class Foo { void foo(); };。它也是一个声明,因为每个定义都是一个声明,它包含一个成员函数foo的声明。正如Armen 所说,类定义完成了Foo 类型。这是函数foo的定义:void Foo::foo() {}。它不是 Foo 类型定义的一部分。
        • @kotlinksi:C++PL 是对的。在您引用的句子中,它没有提到 struct Guest;also 声明,但不是定义。所以当你说需要声明时,实际上需要定义。并非所有声明都是定义,因此在此示例中,声明是必要条件但不是充分条件。
        【解决方案5】:

        调用delete 时,ptr_ 的类型不完整。这会导致未定义的行为。所以你的析构函数可能不会被调用。您可以使用Boost.checked_delete 来避免此类情况。

        【讨论】:

          【解决方案6】:

          (stdafx.h 标头不是标准 c++。) 如果我用 g++ 编译,编译器会生成:

           warning: possible problem detected in invocation of delete operator:
           warning: invalid use of incomplete type ‘struct Guest’
           warning: forward declaration of ‘struct Guest’
           note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
          

          配置您的编译器以在适当的警告和错误级别进行编译。

          【讨论】:

          • int _tmain(int argc, _TCHAR* argv[])
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-19
          • 2011-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-18
          相关资源
          最近更新 更多