使用智能指针和继承的构造函数

时间:2016-12-11 13:08:20

标签: c++ c++11 constructor smart-pointers forward-declaration

根据这个question,如果所有构造函数和析构函数都不是内联的(然后需要完全定义的类型),则可以转发声明智能指针。当没有提供析构函数时,编译器将声明一个并提供内联定义,然后要求智能指针中的类型在标头中完全已知。这同样适用于默认构造函数。

但是,我发现它也适用于继承的构造函数,这对我来说有点混乱。考虑:

class Base
{
public:
    Base(); //defined in cpp
};

class SomeClass;

class Derived : public Base
{
    using Base::Base;
    ~Derived(); //defined in cpp

    std::unique_ptr<SomeClass> ptr;
};

除非明确声明Derived构造函数并仅在源文件中定义,否则不会编译。为什么? Base构造函数不是内联的,据我所知,using指令应该以与其他成员类似的方式导致构造函数的“继承”。或者编译器是否将其解释为“为我声明与Base中相同的构造函数并将其定义为内联”?

3 个答案:

答案 0 :(得分:3)

最后一句是你的回答。编译器将using Base::Base;解释为

“我希望Derived拥有与Base具有相同签名集的构造函数。请为我定义它们。”

答案 1 :(得分:1)

首先让我们用最少量的代码重现问题:

#include <memory>

class SomeClass;

int main()
{
  std::unique_ptr<SomeClass> ptr;
}

错误:

In file included from /opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/memory:81:0,
from <source>:1:
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = SomeClass]':
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:236:17:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = SomeClass; _Dp = std::default_delete<SomeClass>]'
<source>:7:30:   required from here
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'SomeClass'
static_assert(sizeof(_Tp)>0,
^
Compiler exited with result code 1

这里的问题相同(为了证明它与继承无关):

#include <memory>

class SomeClass;

class NotDerived
{
//    ~NotDerived(); //defined in cpp

    std::unique_ptr<SomeClass> ptr;
};

int main(){
  NotDerived d;
}

错误:

In file included from /opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/memory:81:0,
from <source>:1:
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = SomeClass]':
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:236:17:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = SomeClass; _Dp = std::default_delete<SomeClass>]'
<source>:5:7:   required from here
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'SomeClass'
static_assert(sizeof(_Tp)>0,
^
Compiler exited with result code 1

现在让我们记住unique_ptr到底是什么:

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

并且default_delete ...

  

在ptr

上调用delete

指针(delete)上的SomeClass会想要破坏Someclass,因此需要调用SomeClass::~SomeClass

你尚未申报。因此错误。

为什么这有关系?

因为在您的代码中,如果您没有为Derived声明析构函数,则会生成一个默认的析构函数,当然这将调用ptr的析构函数。

此时,编译器需要SomeClass的完整定义,以便它知道如何销毁它。

通过在Derived中声明析构函数,您将此问题推迟到Derived::~Derived的实现。

答案 2 :(得分:0)

对于Derivated,您没有声明构造函数,因此创建了默认的构造函数,即内联。

默认构造函数或任何其他构造函数调用基础构造函数的事实是另一个与非内联的Base构造函数无关的主题。

Basicaly在类的构造函数和基类中的构造函数之间没有C ++中的连接,除了基类中的那个在派生类中的那个之前执行的事实,并且,如果没有明确的话声明,将调用基类中的默认值。