前向声明是行不通的

发布于 2024-09-28 22:43:47 字数 2088 浏览 3 评论 0原文

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

//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;
}

Below are two fragments (ready to compile) of code. In first fragment in which I'm using only forward declaration for a struct while deleting pointer to this struct from a Base class dtor for a Guest class isn't invoked.
In the second fragment when instead of forward declaration I use full definition of this Guest class using delete in Base works ase intended.
Why? Why does it make a difference? Isn't forward declaration suppose to be just a note for a compiler saying that the definition of this class/struct is somewhere else?
I'm very surprised that it just doesn't work intuitively.

//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;
}

//Second - full def

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

流云如水 2024-10-05 22:43:47

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

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

所以你不能对不完整的类型使用删除。它会调用析构函数,而编译器尚不知道它。

From the C++ standard (5.3.5/5):

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

So you cannot use delete on your incomplete type. It would call the destructor and the compiler is not yet aware of it.

冷情 2024-10-05 22:43:47

非正式地:编译器需要类定义才能正确删除对象,因为它需要知道如何调用该类的析构函数和/或删除运算符。

正式来说,5.3.5/5:

如果被删除的对象有
此时的类类型不完整
删除并且完整的类有一个
非平凡的析构函数或
解除分配函数,其行为是
未定义。

如果(例如)Guest 是 POD,那么你就可以了,但是你给了它一个析构函数,所以你就不行了。

Informally: the compiler needs the class definition in order to delete the object correctly, because it needs to know how to call the destructor and/or operator delete for that class.

Formally, 5.3.5/5:

If the object being deleted has
incomplete class type at the point of
deletion and the complete class has a
non-trivial destructor or a
deallocation function, the behavior is
undefined.

You'd be OK if (for example) Guest was POD, but you gave it a destructor, so you're not OK.

安人多梦 2024-10-05 22:43:47

您无法删除指向不完整类型的指针。删除是要求类型完整的操作之一。华泰

You cannot delete a pointer to an incomplete type. Delete is one of the operations which requires the type to be complete. HTH

漆黑的白昼 2024-10-05 22:43:47

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

You can't delete the Guest unless you know its definition. It's destructor won't be called.
Also, if Guest has defined a custom operator delete, it would be ignored.

空城之時有危險 2024-10-05 22:43:47

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

The type of ptr_ is incomplete when you invoke delete on it. This leads to undefined behavior. So your destructor may not be called. You can use Boost.checked_delete to avoid such scenarios.

喵星人汪星人 2024-10-05 22:43:47

(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.

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

(The stdafx.h header is not standard c++.)
If I compile with g++, the compiler generates:

 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.

Configure your compiler to compile at proper warning and error levels.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文