c++ - 防止 C++(或 C++0x)中的 header 爆炸

标签 c++ pointers c++11 smart-pointers raii

假设有如下通用代码:

y.hpp:

#ifndef Y_HPP
#define Y_HPP

// LOTS OF FILES INCLUDED

template <class T>
class Y 
{
public:
  T z;
  // LOTS OF STUFF HERE
};

#endif

现在,我们希望能够在我们创建的类(比如 X)中使用 Y。但是,我们不希望 X 的用户必须包含 Y header 。

所以我们定义了一个类 X,像这样:

x.hpp:

#ifndef X_HPP
#define X_HPP

template <class T>
class Y;

class X
{
public:
  ~X();
  void some_method(int blah);
private:
  Y<int>* y_;
};

#endif

请注意,因为 y_ 是一个指针,我们不需要包含它的实现。

实现在x.cpp中,单独编译:

x.cpp:

#include "x.hpp"
#include "y.hpp"

X::~X() { delete y_; }
void X::someMethod(int blah) { y_->z = blah; }

因此,现在我们的客户只需包含“x.hpp”即可使用 X,而无需包含也不必处理所有“y.hpp” header :

main.cpp:

#include "x.hpp"

int main() 
{
  X x;
  x.blah(42);
  return 0; 
}

现在我们可以分别编译main.cppx.cpp了,编译main.cpp的时候不需要包括 y.hpp

然而,对于这段代码,我不得不使用原始指针,而且,我不得不使用删除。

所以这是我的问题:

(1) 有没有办法让 Y 成为 X 的直接成员(不是指向 Y 的指针),而无需包含 Y header ? (我强烈怀疑这个问题的答案是否定的)

(2) 有没有一种方法可以使用智能指针类来处理分配给堆的 Y? unique_ptr 似乎是显而易见的选择,但是当我更改 x.hpp

中的行时

来自:

Y<int>* y_; 

到:

std::unique_ptr< Y<int> > y_;

并包含,并使用 c++0x 模式编译,我得到错误:

/usr/include/c++/4.4/bits/unique_ptr.h:64: error: invalid application of ‘sizeof’ to incomplete type ‘Y<int>’ 
/usr/include/c++/4.4/bits/unique_ptr.h:62: error: static assertion failed: "can't delete pointer to incomplete type"

那么是否可以通过使用标准智能指针而不是原始指针以及自定义析构函数中的原始删除来做到这一点?

解决方案:

Howard Hinnant 说得对,我们需要做的就是按以下方式更改 x.hppx.cpp:

x.hpp:

#ifndef X_HPP
#define X_HPP

#include <memory>

template <class T>
class Y;

class X
{
public:
  X(); // ADD CONSTRUCTOR FOR X();
  ~X();
  void some_method(int blah);
private:
  std::unique_ptr< Y<int> > y_;
};

#endif

x.cpp:

#include "x.hpp"
#include "y.hpp"

X::X() : y_(new Y<int>()) {} // ADD CONSTRUCTOR FOR X();
X::~X() {}
void X::someMethod(int blah) { y_->z = blah; }

而且我们很擅长使用 unique_ptr。谢谢霍华德!

解决方案背后的基本原理:

如果我错了,人们可以纠正我,但这段代码的问题是隐式默认构造函数试图默认初始化 Y,并且因为它对 Y 一无所知,所以它不能这样做。通过明确地说我们将在别处定义一个构造函数,编译器认为“好吧,我不必担心构造 Y,因为它是在别处编译的”。

真的,我应该首先添加一个构造函数,没有它我的程序就会出错。

最佳答案

您可以使用 unique_ptrshared_ptr处理不完整的类型。如果你使用 shared_ptr , 你必须概述 ~X()正如你所做的那样。如果你使用 unique_ptr你必须概述 ~X()X() (或您用来构造 X 的任何构造函数)。它是 X 的隐式生成的默认 ctor要求完整类型Y<int> .

两者都是 shared_ptrunique_ptr保护您免于在不完整的类型上意外调用 delete。这使得它们优于不提供此类保护的原始指针。原因unique_ptr需要概述 X()归结为它有一个静态删除器而不是动态删除器。

编辑:更深入的说明

由于静态删除器与动态删除器的差异 unique_ptrshared_ptr , 两个智能指针需要 element_type在不同的地方完成。

unique_ptr<A>要求 A 完整:

  • ~unique_ptr<A>();

但不适用于:

  • unique_ptr<A>();
  • unique_ptr<A>(A*);

shared_ptr<A>要求 A 完整:

  • shared_ptr<A>(A*);

但不适用于:

  • shared_ptr<A>();
  • ~shared_ptr<A>();

最后,隐式生成 X() ctor 将调用智能指针默认 ctor 智能指针 dtor(以防 X() 抛出异常 - 即使我们知道它不会)。

底线:X 的任何成员调用一个智能指针成员,其中 element_type必须要完整,必须概述来源,其中 element_type做完了。

还有关于 unique_ptr 的酷事和 shared_ptr是,如果您猜错了需要概述的内容,或者如果您没有意识到正在隐式生成一个需要完整 element_type 的特殊成员,这些智能指针会告诉您(有时措辞不当)编译时错误。

关于c++ - 防止 C++(或 C++0x)中的 header 爆炸,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5460186/

相关文章:

c++ boost序列化类相互引用

c++ - 如何脱离 std::experimental::optional?

c++ - 使用 boost 库在我的 PC 中获取我的以太网设备 IP 地址列表

c - C : *source++, (*source)++, *(source)++ 中的差异

c++ - 什么时候应该动态或自动实例化对象?

c++ - 基于基类方法的生成类中的运算符重载

c++ - for循环中的产品序列

c++ - 当通过指针初始化创建对象实例时,为什么 std::string 属性为空

c++ - 宏上的用户定义文字运算符

c++ - std::enable_if 如何阻止成员模板的声明?