【问题标题】:How to create a private static const string when using the pimpl idiom使用 pimpl 习惯用法时如何创建私有静态 const 字符串
【发布时间】:2018-01-13 11:45:58
【问题描述】:

背景

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

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

问题

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

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

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

问题

使用 pimpl 习惯用法时,如何在隐藏的内部类中隐藏非整数静态 const 变量?

示例

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

Widget.h:

#ifndef WIDGET_H_
#define WIDGET_H_

#include <memory>

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

#endif

Widget.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-&gt;TEST

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

pimpl-&gt;TEST = "changed"

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

所以现在 TEST 是一个常量字符串,只能由 Widget 访问,不在公共标头中命名,并且在所有 Widget 实例之间共享一个副本,完全符合需要。

解决方案示例(char *):

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

Widget.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() { }

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

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++ 的缺乏经验使我最初无法意识到这一点。我希望这可以避免对这两个主题造成任何混淆。

【问题讨论】:

  • 只是想知道:这与 Pimpl 有什么关系?在我看来,这与简单的“如何定义静态 const 字符串”没有区别
  • 您说得对,我在学习过程中发现了这一点,但没有在解决方案中提及,我在原帖中添加了注释以进行澄清。感谢您指出这一点。

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


【解决方案1】:

您也可以在示例中将const 替换为constexpr,它会编译。

class Widget::Impl {
public:
    static constexpr std::string TEST = "test";  // constexpr here

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

更新:

好吧,看来我错了……当我需要常量时,我​​总是存储原始字符串。

class Widget::Impl {
public:
    static constexpr char * const TEST = "test";
};

根据使用模式,它可能合适或不合适。如果不是,则按照其他答案中的说明定义变量。

【讨论】:

  • 错误:constexpr 变量不能有非文字类型'const std::string'
  • 这种方法的 std::string 版本给了我一个关于非文字类型的类似错误。 char* 方法也不起作用,错误为ISO C++ forbids converting a string constant to 'char*'
  • 我写的代码编译没有错误,所以我不知道你在说什么。我的代码中没有从std::stringchar * 的转换。从 C 字符串构造 C++ 字符串很容易。
  • 编译器错误指向“test”周围的第一个双引号,所以它似乎是在谈论将文字字符串分配给 char* 变量。
【解决方案2】:
#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&amp; 的静态函数。这将允许您内联定义它。

【讨论】:

  • 这个方案看起来确实可行,但是写了一些测试代码试用了一下,在实践中使用起来感觉很别扭,而且还需要对每个私有字符串进行函数调用,暗示它不会不能很好地扩展。这是否被认为是解决问题的“最佳实践”方法?
  • 你必须弄清楚你想要什么。如果您在pimpl 对象中声明字符串,可能是因为您无论如何都不需要公开访问它们。否则,您将在主类中声明它们。
  • @richard-hodges:对不起,我看错了您的原始回复,我之前的评论是参考将 TEST 设为静态函数的建议;我在您的代码示例中进行了更改,它按预期工作,添加了另一个 const。
  • @hexsorcerer 完全没问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 1970-01-01
  • 2015-06-16
  • 2017-03-29
相关资源
最近更新 更多