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

发布于 2024-10-30 17:29:47 字数 1022 浏览 8 评论 0原文

假设我有如下内容:

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.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。

如果它是一个类,我们可以向前声明 some_complicated,但它可能是一个 typedef,所以这是不可能的。

我能想到的唯一方法是在不公开 b_ 也不在 a.hpp 中包含 Something_complicated.hpp 的情况下执行此操作:

a.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

#define get_complicated ((b_->x))

当然,我不必定义宏来解决这个问题?还有其他选择吗?

Lets say I have something like the following:

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; }

Unfortunately, in this case, a.cpp will fall to compile because "get_complicated()" is not a method of A.

So, we can try this:

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.

We could forward declare something_complicated if it is a class, but it's probably a typedef, so that is out.

The only way I can think of doing this without making b_ public nor including something_complicated.hpp in a.hpp is the following:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

ゞ记忆︶ㄣ 2024-11-06 17:29:47

最简单的解决方案可能是将复杂类型的引用包装在类中,在 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 并通过它处理复杂的事情会更简单。

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:

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;
};

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.

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. It would be simpler still to allow access to B when required, and get to the complicated thing through that.

梦魇绽荼蘼 2024-11-06 17:29:47

恐怕对于什么属于类、什么不属于类存在误解。

并非所有作用于类内部的方法都应该是类方法,毕竟我们已经有了 friend 函数。我知道很多人将 helper 方法声明为私有函数,但是这样做会引入不必要的依赖项(编译时)以及 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; }
}

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

注意:与匿名命名空间相比,我更喜欢静态函数,因为它在阅读时更明显。命名空间引入了范围,并且在筛选文件时不容易浏览范围。您的情况可能会有所不同,两者都提供相同的功能(功能)。

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. 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 friends.

When dealing with PIMPL, I tend not to use private functions. Instead, the choice is:

  • Making Impl (B in your case) a true class, with its own validation logic and true API
  • Using static free functions (or functions declared in an anonymous namespace)

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

#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).

苄①跕圉湢 2024-11-06 17:29:47

只是避免引用 a.hpp 中的 something_complicated 即可。

一种解决方案是将成员函数 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_; }
};

Just avoid referring to something_complicated in a.hpp.

One solution is to replace the member function get_complicated with a free function, or a static method of another class.

.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_; }
};
空心空情空意 2024-11-06 17:29:47

有什么问题:

a.hpp:

class B;

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

如果您的客户想要从 B 中得到一些复杂的东西,那么就让他们#include

What's wrong with:

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>.

原野 2024-11-06 17:29:47

如果它是一个类,我们可以转发声明 some_complicated,但它可能是一个 typedef,所以这是不可能的。

这正是您必须做的。我不明白 typedef 如何排除前向声明。

We could forward declare something_complicated if it is a class, but it's probably a typedef, so that is out.

This is exactly what you have to do. And I don't see how being a typedef rules out a forward declaration.

感悟人生的甜 2024-11-06 17:29:47

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

If you control something_complicated.hpp you could do what the standard library does: Create a something_complicated_fwd.hpp that has appropriate forward declarations, including the types that may or may not be typedefs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文