简体   繁体   English

如何从类定义中忽略私有非虚拟方法?

[英]How to omit private non-virtual methods from class definition?

Lets say I have something like the following: 可以说我有以下内容:

a.hpp: a.hpp:

class B;

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

a.cpp: a.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

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

Unfortunately, in this case, a.cpp will fall to compile because "get_complicated()" is not a method of A. 不幸的是,在这种情况下,a.cp​​p将无法编译,因为“ get_complicated()”不是A的方法。

So, we can try this: 因此,我们可以尝试这样:

a.hpp: a.hpp:

class B;

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

But then a.hpp fails to compile because something_complicated isn't defined. 但是随后a.hpp无法编译,因为未定义something_complicated。

We could forward declare something_complicated if it is a class, but it's probably a typedef, so that is out. 如果它是一个类,我们可以向前声明something_complicated,但是它可能是typedef,所以就可以了。

The only way I can think of doing this without making b_ public nor including something_complicated.hpp in a.hpp is the following: 我可以想到不公开b_或在a.hpp中不包含something_complicated.hpp的唯一方法是:

a.cpp: a.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

#define get_complicated ((b_->x))

Surely I don't have to define a macro to get around this issue? 当然,我不必定义宏来解决此问题吗? Any alternatives? 有其他选择吗?

The easiest solution is probably to wrap a reference to the complicated type in a class, forward declare that in a.hpp , and define it in something_complicated.hpp . 最简单的解决方案可能是将对复杂类型的引用包装在类中,在a.hpp向前声明该a.hpp ,然后在something_complicated.hpp定义它。

a.hpp: a.hpp:

class B;
class complicated_ref;

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

something_complicated.hpp: something_complicated.hpp:

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

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

Now a.cpp and anything that needs to use the complicated type must include it's header, but anything that just wants to use class A does not need to. 现在, a.cpp和任何需要使用复杂类型的东西都必须包含其标头,但是任何只想使用class A东西都不需要。

This is assuming that there's a good reason for some clients of A to access the complicated thing, but for B to be inaccessible to everyone. 这是假设有充分的理由让A某些客户访问复杂的事物,而B却是每个人都无法访问的。 It would be simpler still to allow access to B when required, and get to the complicated thing through that. 允许在需要时访问B并通过它来处理复杂的事情仍然会更简单。

Just avoid referring to something_complicated in a.hpp . 只要避免引用a.hpp something_complicated a.hpp

One solution is to replace the member function get_complicated with a free function, or a static method of another class. 一种解决方案是将成员函数get_complicated替换为自由函数或另一类的静态方法。

.h : .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 : .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_; }
};

What's wrong with: 有什么问题:

a.hpp: a.hpp:

class B;

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

If your clients want to get something complicated out of B, then let them #include <something_complicated.hpp> . 如果您的客户希望从B中获得一些复杂的东西,请让他们#include <something_complicated.hpp>

I am afraid there is a misunderstand on what belong to the class, and what does not. 恐怕对什么属于班级,什么不属于班级有误解。

Not all methods that act on the internals of the class should be class methods, after all, we have friend functions already. 并不是所有作用于类内部的方法都应该是类方法,毕竟,我们已经有了friend函数。 I know that many people declare the helper methods as private functions, however doing so introduces needless dependencies (compile-time) and a visibility issue with friend s. 我知道很多人将帮助程序方法声明为私有函数,但是这样做会引入不必要的依赖关系(编译时)以及与friend的可见性问题。

When dealing with PIMPL, I tend not to use private functions. 在处理PIMPL时,我倾向于不使用私有函数。 Instead, the choice is: 相反,选择是:

  • Making Impl ( B in your case) a true class, with its own validation logic and true API 使Impl (在您的情况下为B )成为一个具有自己的验证逻辑和真实API的真实类
  • Using static free functions (or functions declared in an anonymous namespace) 使用static自由函数(或在匿名名称空间中声明的函数)

Both are good, and use whichever seems most appropriate. 两者都很好,可以使用最合适的方法。 Namely: 即:

  • methods: when dealing with validation issues 方法:处理验证问题时
  • free functions: for computing that can be expressed in terms of the aforementionned methods 自由函数:用于可以按照上述方法表示的计算

It is deliberate on my part to search to have as few methods as possible, because those are the only ones that can screw up my class invariants, and the less they are the more confident I can be that the invariants will be maintained. 我有意搜索尽可能少的方法,因为那些是唯一可以使我的类不变式搞砸的方法,而它们越少,我就越有信心要保持不变式。

In your case, it's up to you to decide which approach suits you best. 在您的情况下,由您决定哪种方法最适合您。

In Action: 在行动中:

a.cpp 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; }
}

Not so different from what you had, eh ? 与您所拥有的没有什么不同,是吗?

Note: I prefer static functions to anonymous namespaces because it's more obvious when reading. 注意:与匿名名称空间相比,我更喜欢静态函数,因为在阅读时它更明显。 Namespaces introduce scopes, and scope are not glanced easily when sifting through a file. 命名空间引入了作用域,并且在文件中筛选时不容易浏览范围。 Your mileage may vary, both offer identical functionality (for functions). 您的里程可能会有所不同,两者都提供相同的功能(针对功能)。

We could forward declare something_complicated if it is a class, but it's probably a typedef, so that is out. 如果它是一个类,我们可以向前声明something_complicated,但是它可能是typedef,所以就可以了。

This is exactly what you have to do. 这正是您要做的。 And I don't see how being a typedef rules out a forward declaration. 而且我不认为成为typedef会排除正向声明。

如果您控制something_complicated.hpp ,则可以执行标准库的操作:创建一个具有适当前向声明(包括可能为typedef或不为typedef的类型)的something_complicated_fwd.hpp

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM