2

可以说我有以下内容:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
}

一个.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

something_complicated& A::get_complicated() { return b_->x; }

不幸的是,在这种情况下,a.cp​​p 将无法编译,因为“get_complicated()”不是 A 的方法。

所以,我们可以试试这个:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
  something_complicated& A::get_complicated();
}

但随后 a.hpp 无法编译,因为 something_complicated 未定义。

如果它是一个类,我们可以转发声明 something_complicated,但它可能是一个 typedef,所以它已经过时了。

在不公开 b_ 或在 a.hpp 中包含 something_complicated.hpp 的情况下,我能想到的唯一方法如下:

一个.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

#define get_complicated ((b_->x))

当然我不必定义一个宏来解决这个问题?有什么选择吗?

4

6 回答 6

2

最简单的解决方案可能是将对复杂类型的引用包装在一个类中,将其声明在 中a.hpp,然后将其定义在something_complicated.hpp.

a.hpp:

class B;
class complicated_ref;

class A
{
public:
  complicated_ref get_complicated();
private:
  std::unique_ptr<B> b_;
};

something_complicated.hpp:

// ... complicated definitions ...
typedef whatever something_complicated;

struct complicated_ref
{
    complicated_ref(something_complicated & thing) : ref(thing) {}
    something_complicated & ref;
};

现在a.cpp,任何需要使用复杂类型的东西都必须包含它的标题,但任何只想使用的东西class A都不需要。

这是假设某些客户有充分的理由A访问复杂的东西,但B每个人都无法访问。在需要时允许访问B并通过它来处理复杂的事情会更简单。

于 2011-04-04T12:27:55.200 回答
1

只是避免提及something_complicatedin a.hpp

get_complicated一种解决方案是用自由函数或另一个类的静态方法替换成员函数。

.h

class A_impl_base {
    A_impl_base() {}

    friend class A_impl; // all instances of A_impl_base are A_impl
}; // this stub class is the only wart the user sees

class A
{
private:
    std::unique_ptr< A_impl_base > b_; // this is not a wart, it's a pimpl

    friend class A_impl;
}

.cpp

class A_impl : A_impl_base {
     static A_impl &get( A &obj ) { return * obj.b_; }
     static A_impl const &get( A const &obj ) { return * obj.b_; }
};
于 2011-04-04T12:09:06.183 回答
1

有什么问题:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
public:
  B& get_B();
}

如果您的客户想从 B 中得到一些复杂的东西,那就让他们吧#include <something_complicated.hpp>

于 2011-04-04T13:28:27.503 回答
1

恐怕对什么属于班级,什么不属于班级有误解。

并非所有作用于类内部的方法都应该是类方法,毕竟我们已经有了friend函数。我知道很多人将辅助方法声明为私有函数,但是这样做会引入不必要的依赖项(编译时)和friends 的可见性问题。

在处理 PIMPL 时,我倾向于不使用私有函数。相反,选择是:

  • 制作ImplB在您的情况下)一个真正的类,具有自己的验证逻辑和真正的 API
  • 使用static自由函数(或在匿名命名空间中声明的函数)

两者都很好,并且使用看起来最合适的那个。即:

  • 方法:处理验证问题时
  • 自由函数:用于可以用上述方法表示的计算

我有意寻找尽可能少的方法,因为这些是唯一可以搞砸我的类不变量的方法,而且它们越少,我就越有信心保持这些不变量。

就您而言,由您决定哪种方法最适合您。

在行动:

a.cpp

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

static something_complicated& get_complicated(B& b) { return b_.x; }

// or anonymous namespace instead
namespace {
  something_complicated& get_complicated(B& b) { return b_.x; }
}

和你拥有的没什么不同,是吗?

注意:我更喜欢静态函数而不是匿名命名空间,因为它在阅读时更明显。命名空间引入了作用域,并且在筛选文件时不容易一目了然。您的里程可能会有所不同,两者都提供相同的功能(对于功能)。

于 2011-04-04T19:33:12.003 回答
0

如果它是一个类,我们可以转发声明 something_complicated,但它可能是一个 typedef,所以它已经过时了。

这正是你必须做的。而且我看不出作为 typedef 是如何排除前向声明的。

于 2011-04-04T12:11:16.220 回答
0

如果您可以控制something_complicated.hpp,您可以执行标准库所做的事情:创建something_complicated_fwd.hpp具有适当前向声明的 a,包括可能是也可能不是 typedef 的类型。

于 2011-04-04T14:10:22.290 回答