c++ - 使用 pimpl 习惯用法时如何创建私有(private)静态常量字符串

标签 c++ c++11 pimpl-idiom

背景

我一直在学习如何使用 Herb Sutter 在本页描述的更新的 c++11 方法来实现 pimpl 习语:https://herbsutter.com/gotw/_100/

我试图通过向私有(private)实现添加成员变量来修改此示例,特别是 std::string(尽管 char* 具有相同的问题)。

问题

由于使用了 static const 非整数类型,这似乎是不可能的。只能对整数类型进行类内初始化,但由于它是静态的,因此也不能在构造函数中进行初始化。

解决这个问题的方法是在头文件中声明私有(private)变量,并在实现中对其进行初始化,如下所示: C++ static constant string (class member)

但是,这个解决方案对我不起作用,因为它破坏了我试图通过 pimpl 习惯用法实现的封装。

问题

当使用 pimpl 惯用语时,如何在隐藏的内部类中隐藏一个非整数静态常量变量?

例子

这是我能想出的最简单(不正确)的例子来说明问题:

小部件.h:

#ifndef WIDGET_H_
#define WIDGET_H_

#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
private:
    class Impl;
    std::unique_ptr<Impl> pimpl;
};

#endif

小部件.cpp:

#include "Widget.h"
#include <string>

class Widget::Impl {
public:
    static const std::string TEST = "test";

    Impl() { };
    ~Impl() { };
};

Widget::Widget() : pimpl(new Impl()) { }
Widget::~Widget() { }

编译命令:

g++ -std=c++11 -Wall -c -o Widget.o ./Widget.cpp

请注意,此示例无法编译,因为变量 TEST 不能在声明时分配,因为它不是整数类型;但是,因为它是静态的,所以这是必需的。这似乎暗示它无法完成。

我整个下午都在搜索以前的问题/答案,但找不到任何提出保留 pimpl 习语信息隐藏属性的解决方案。

解决方案观察:

在我上面的例子中,我试图在 Impl 类声明中分配 TEST 的值,它在 Widget.cpp 内部而不是它自己的头文件中。 Impl 的定义也包含在 Widget.cpp 中,我相信这是我困惑的根源。

通过简单地将 TEST 的赋值移动到 Impl 声明之外(但仍在 Widget/Impl 定义内),问题似乎已解决。

在下面的两个示例解决方案中,可以使用

从 Widget 中访问 TEST

pimpl->测试

尝试将不同的字符串分配给 TEST,即

pimpl->TEST = "已更改"

导致编译器错误(它应该如此)。此外,尝试从 Widget 外部访问 pimpl->TEST 也会导致编译器错误,因为 pimpl 被声明为 Widget 私有(private)。

所以现在 TEST 是一个常量字符串,只能由 Widget 访问,没有在公共(public) header 中命名,并且完全按照需要在所有 Widget 实例之间共享一个拷贝。

解决方案示例(字符*):

在使用 char * 的情况下,请注意添加了另一个 const 关键字;这是防止将 TEST 更改为指向另一个字符串文字所必需的。

小部件.cpp:

#include "Widget.h"
#include <stdio.h>

class Widget::Impl {
public:
    static const char *const TEST;

    Impl() { };
    ~Impl() { };
};

const char *const (Widget::Impl::TEST) = "test";

Widget::Widget() : pimpl(new Widget::Impl()) { }
Widget::~Widget() { }

解决方案示例(字符串):

小部件.cpp:

#include "Widget.h"
#include <string>

class Widget::Impl {
public:
    static const std::string TEST;

    Impl() { };
    ~Impl() { };
};

const std::string Widget::Impl::TEST = "test";

Widget::Widget() : pimpl(new Widget::Impl()) { }
Widget::~Widget() { }

更新:

我现在意识到这个问题的解决方案与 pimpl 习惯用法完全无关,只是定义静态常量的标准 C++ 方法。我已经习惯了像 Java 这样的其他语言,在这些语言中,必须在声明常量的那一刻就定义常量,所以我对 C++ 的缺乏经验使我一开始没有意识到这一点。我希望这可以避免混淆这两个主题。

最佳答案

#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
private:
    class Impl;
    std::unique_ptr<Impl> pimpl;
};

/*** cpp ***/

#include <string>

class Widget::Impl {
public:
    static const std::string TEST;

    Impl() { };
    ~Impl() { };
};

const std::string Widget::Impl::TEST = "test"; 

Widget::Widget() : pimpl(new Impl()) { }
Widget::~Widget() { }

您可能需要考虑使 TEST 成为返回 const std::string& 的静态函数。这将允许您内联定义它。

关于c++ - 使用 pimpl 习惯用法时如何创建私有(private)静态常量字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45525402/

相关文章:

c++ - 使用 cmake 构建错误 : cannot find -lpthreads

c++ - c++ new 运算符可以在 Windows 上自动使用大页面吗?

c++11 - unique_ptr堆和栈分配

c++ - 计算 3D 平面圆的最小半径

C++ - 在模板类之外但在头文件中定义成员函数

c++ - 如何在 C++ 中划分元组

c++ - 当 shared_ptr 重置为包含的相同地址时,是否保证 weak_ptr 会过期?

c++ - 委派到私有(private)领域

c++ - 在不使用未命名命名空间的情况下在 header 中隐藏 C++ 类

C++ Pimpl 与纯虚拟接口(interface)性能对比