独占指针和前置声明

17

假设我有两个类:

"Foo.h"

#pragma once    
class Foo
{
public:
    Foo()
    {

    };

    ~Foo()
    {

    };
};

"A.h"

#pragma once
#include <memory>

class Foo;

class A
{
public:
    A(){};
    ~A(){};

    std::unique_ptr<Foo> foo;
};

A拥有一个unique_ptr指向Foo。我不想在"A.h"中包含Foo,所以我使用前置声明。仅仅在"A.h"中使用类Foo的前置声明,我会得到一个编译时错误:

error C2027: use of undefined type 'Foo'
error C2338: can't delete an incomplete type  

我正在阅读这篇关于如何避免这个错误的文章,并将A的析构函数移动到它自己的.cpp文件中,其中我也包含了Foo:

"A.cpp"

#include "A.h"

#include "Foo.h"

A::A()
{

}

A::~A()
{

}

在"A.cpp"实现了A的析构函数后,我能够编译程序,因为Foo类在"A.cpp"中已知。这似乎是合理的,因为unique_ptr需要完整的类型才能调用它的析构函数。但令我惊讶的是,在注释掉A的构造函数后(包括"A.h"和"A.cpp"),我得到了同样的错误。这怎么可能?为什么编译器会抱怨不能调用Foo的析构函数,而A没有构造函数呢?
编辑: 我上传了4个文件,所以你可以测试这个程序。 我正在使用Visual Studio 2013的MSVC++。

http://www.filedropper.com/test_61


5
当您注释掉构造函数时,A 会有一个构造函数:编译器会为其提供一个默认构造函数,并且该构造函数会得到一个内联定义。 - dyp
1
当您注释掉构造函数时,编译器将隐式定义它作为内联函数(即在头文件中而不是.cpp文件中)。看起来您的编译器认为隐式定义的默认构造函数可能想要销毁unique_ptr,可能是因为构造函数的主体(或另一个成员)抛出异常并且需要再次销毁unique_ptr成员。您上面展示的代码不应该有这种影响,因为没有其他成员。 - Jonathan Wakely
1
在上面的代码中,默认构造函数不应该需要销毁它(所以如果编译器认为需要销毁,那就是编译器的错误),但是如果你的真实代码在unique_ptr成员之后定义了其他类成员,并且构造其中一个成员可能会因为异常而失败,那么unique_ptr成员就需要被销毁。你展示的是你正在测试的确切代码,还是一个简化版本,实际上并没有给出相同的错误? - Jonathan Wakely
2
哎呀,我错了,构造函数确实需要知道完整的类型,正如Chris Drew在下面所述的原因一样。因此,构造函数需要与析构函数一起定义为非内联函数。 - Jonathan Wakely
2
那么,当使用unique_ptr时,一个类的构造函数和析构函数始终需要完整的类型吗?我有点困惑,因为在网络上找到的所有文章都只提到了在析构函数中需要完整的类型(就像我在最初的问题中发布的文章中所述)。 - abcheudg234
显示剩余8条评论
2个回答

24
构造函数需要像析构函数一样访问删除器:异常安全要求构造函数能够在构造函数主体抛出异常的情况下回滚初始化所有成员的过程。
在非委托构造函数中,每个可能构造的类子对象的析构函数都有可能被调用。这项规定确保在抛出异常时可以为完全构造的子对象调用析构函数。
相关内容:

0

“A” 没有构造函数是不可能的。

如果您注释掉您编写的构造函数,编译器将为您创建一个默认构造函数,并且它不一定在您定义的位置。这会导致上述问题。


1
但是为什么我必须在A.cpp中定义构造函数呢?我知道编译器会抱怨需要在包含Foo.h的地方定义A的析构函数,以便它成为一个完整的类型,从而使A的析构函数能够实际销毁Foo。但我不明白为什么还要在A.cpp中定义A的构造函数。您可以通过复制这些类并在main()中实例化A来尝试该代码。当注释掉A的构造函数时,代码将无法编译。 - abcheudg234
3
您使用的是哪个编译器? - senex
1
我正在使用Visual Studio 2013的MSVC++编译器。 - abcheudg234
2
我正在使用Visual Studio 2013的MSVC++编译器。我知道@R.MartinhoFernandes也在使用。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接