避免头文件中私有方法所需的包含

时间:2015-07-30 12:38:01

标签: c++

与特定任务有关的类依赖于某个第三方库来执行此任务(假设为JSON序列化)。对于客户端,库的使用应该是透明的,并且在类的公共接口中没有引用第三方代码类或数据类型。

在我介绍私有方法之前,我可以将所需的{{1}}添加到.cpp文件中。但是,现在我根据第三方代码声明了一个私有方法,我必须将一些#includes拉到头文件中,这反过来导致在所有其他文件中包含相应的头文件,包括我的类的头文件。

我在考虑使用函数而不是私有方法,因此我不必在头文件中声明函数。当然,我必须将对我正在使用的字段的引用传递给这些函数。这是解决此问题的合理方法,还是在使用私有方法时是否有实现此类封装的最佳实践?

3 个答案:

答案 0 :(得分:4)

假设您必须添加这些私有方法

  • Json::Node AsJson() const;
  • std::string Serialize(const Json::Node& root) const;
  • Json::Node Unserialize(const std::string& document) const;
  • void InitFrom(const Json::Node&);

FORWARD DECLARATION

假设Jsonnamespace,在标题中您只需要转发声明

namespace Json
{
    class Node;
}

而不是

#include <3rdLibrary/Json.hpp>

如果Json是一个类,所以Json::Node是一个内部类,则不能使用前向声明

免费功能 如果函数不需要私有访问(以及其他一些非虚拟访问),您可以创建自由函数(在未命名的命名空间或static中)

在标题中没有任何内容,在cpp:

#include <3rdLibrary/JsonCpp.hpp>
namespace {
    Json::Node AsJson(const MyClass& myclass) {/**/}
    std::string Serialize(const MyClass& myclass, const Json::Node& root) {/**/}
    Json::Node Unserialize(const MyClass& myclass, const std::string& document) {/**/}
    void InitFrom(MyClass& myclass, const Json::Node&){/**/}
}
MyClass::foo()
{
    Serialize(*this, AsJson(*this));
    /**/
}

PIMPL IDIOM Pimpl成语是另一种选择

// header.hpp

class MyClass
{
public:
    ~MyClass();
    MyClass(const MyClass&); // = delete ?
    MyClass& operator =(const MyClass&); // = delete ?
    MyClass(MyClass&&); // = delete ?
    MyClass& operator =(MyClass&&); // = delete ?

    // previous public methods.
private:
    struct Pimpl;
    std::unique_ptr<Pimpl> pimpl;
};

来源

// MyClass.cpp

struct Pimpl
{
    // What you should have done in MyClass
};

MyClass::~MyClass() = default; // which destroys Pimpl and should know Pimpl definition
                               // that's why it is not in header

ReturnType MyClass::publicMethod(Args args) {return pimpl->publicMethod(args);} // Same for all public methods.

注意:可以用pimpl惯用法隐藏实现的某些部分

<强>接口

最后,类似于pimpl习语,你可以使用界面

// IMyClass.hpp
class IMyClass
{
public:
    virtual ~IMyClass() = default;
    IMyClass(const IMyClass&); // = delete ?
    IMyClass& operator =(const IMyClass&); // = delete ?
    IMyClass(IMyClass&&); // = delete ?
    IMyClass& operator =(IMyClass&&); // = delete ?

    // previous public methods but virtual.
};

std::unique_ptr<IMyClass> makeMyClass(Args);

并正常实现MyClass(使用override)(其标题仅由其cpp文件使用)

并实施

std::unique_ptr<IMyClass> makeMyClass(Args args) { return std::make_unique<MyClass>(args); }

注意:有可能只通过接口暴露某些部分(仅隐藏部分代码)。

答案 1 :(得分:2)

在实现文件中使用免费(非成员)函数,而不是私有方法。许多人认为私有方法无论如何都是代码味道。

这个article by Scott Meyers(@ Jarod42在评论中提到)是对如何以及为什么这样做的一个很好的讨论。

ETA:自从我读过Scott Meyers&#39;之后已经有一段时间了。文章,我忘了他的重点更多的是从类创建者的角度来看可能是类的公共接口的一部分的函数。但从类用户的角度考虑他的Wombat示例。您要做的是添加到您正在使用的课程界面,而不是将该实施细节公开给您的班级用户。

我所谈论的是隐藏的帮助者&#34;,不需要直接访问类的内部的函数,但是由类的公共函数使用做的工作,这听起来像你所描述的。这些函数仅在类的实现文件中可见,并且成员函数将向其数据成员传递副本或引用。

一个简单的例子可能是这样的:

namespace {

int frob( int coordinate )
{
    return coordinate * 3 + 9;
}

} // end anonymous namespace

void foo::shift_out()
{
    for ( auto & coord : m_coords )
    {
        coord = frob( coord );
    }
} 

因此,在此示例中,foo::shift_out()使用frob()执行必要的工作,而frob()class foo的内部和class foo实施内容一无所知知道frob()的存在。

至于私有方法是代码味道,在StackOverflow上搜索这个问题会给你一些很好的讨论。它被视为一个指示,即该类可能正在尝试承担过多的责任,它应该委托给其他类,然后用于组成原始类。

答案 2 :(得分:0)

PIMPL习语最适合你。在主接口类中,只显示公共成员并指向实现。

class MyImpl;

class InterfaceClass
{
public:
    // Public methods go here.

private:
    MyImpl *pImpl;
};

单独定义MyImpl类,它的标题不需要部分接口文件。