假设我有以下内容:
a.hpp:
class B;
class A
{
private:
std::unique_ptr<B> b_;
}
a.cpp:
#include <something_complicated.hpp>
struct B
{
something_complicated x;
}
something_complicated& A::get_complicated() { return b_->x; }
不幸的是,在这种情况下,a.cpp将会编译,因为“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,所以这就是out。
我能想到这样做的唯一方法是在不使用b_ public但在a.hpp中包含something_complicated.hpp的情况下如下:
a.cpp:
#include <something_complicated.hpp>
struct B
{
something_complicated x;
}
#define get_complicated ((b_->x))
当然,我不必定义一个宏来解决这个问题?任何替代方案?
答案 0 :(得分: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
会更简单,并通过它来解决复杂问题。
答案 1 :(得分:1)
请勿在{{1}}中提及something_complicated
。
一种解决方案是使用自由函数或另一个类的静态方法替换成员函数a.hpp
。
<强>·H 强>:
get_complicated
<强>的.cpp 强>:
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;
}
答案 2 :(得分:1)
出了什么问题:
<强> a.hpp:强>
class B;
class A
{
private:
std::unique_ptr<B> b_;
public:
B& get_B();
}
如果您的客户希望从B中获得复杂的内容,请让他们#include <something_complicated.hpp>
。
答案 3 :(得分:1)
我担心对于什么属于班级有什么误解,什么没有。
并非所有作用于类内部的方法都应该是类方法,毕竟我们已经有friend
个函数。我知道很多人将帮助方法声明为私有函数,但这样做会引入不必要的依赖(编译时)和friend
的可见性问题。
在处理PIMPL时,我倾向于不使用私有函数。相反,选择是:
Impl
(在您的情况下为B
)成为一个真正的类,具有自己的验证逻辑和真正的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; }
}
与你的不一样,是吗?
注意:我更喜欢静态函数到匿名命名空间,因为它在阅读时更为明显。命名空间引入了范围,在筛选文件时不会轻易查看范围。您的里程可能会有所不同,两者都提供相同的功能(功能)。
答案 4 :(得分:0)
如果它是一个类,我们可以转发声明something_complicated,但它可能是一个typedef,所以这就是out。
这正是你必须要做的。而且我没有看到typedef如何排除前瞻声明。
答案 5 :(得分:0)
如果您控制something_complicated.hpp
,您可以执行标准库的操作:创建一个具有适当前向声明的something_complicated_fwd.hpp
,包括可能是也可能不是typedef的类型。