使用 pimpl 习惯用法时如何创建私有静态常量字符串
How to create a private static const string when using the pimpl idiom
背景
我一直在学习如何使用 Herb Sutter 在此页面上描述的更新的 c++11 方法来实现 pimpl 习语:https://herbsutter.com/gotw/_100/
我正在尝试通过向私有实现添加成员变量来修改此示例,特别是 std::string(尽管 char* 具有相同的问题)。
问题
由于使用了 static const non-integral 类型,这似乎是不可能的。 In-class 只能对整数类型进行初始化,但因为它是静态的,所以也不能在构造函数中进行初始化。
解决这个问题的方法是在header文件中声明私有变量,并在实现中对其进行初始化,如下所示:
C++ static constant string (class member)
但是,这个解决方案对我不起作用,因为它破坏了我试图通过 pimpl idiom 实现的封装。
问题
如何在使用 pimpl idiom 时在隐藏内部 class 中隐藏 non-integral static 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 不能在声明时赋值,因为它不是整数类型;但是,因为它是静态的,所以这是必需的。这似乎暗示它无法完成。
我整个下午都在搜索之前的 questions/answers,但找不到任何提出保留 information-hiding 属性 pimpl 习语的解决方案。
解决方案观察:
在我上面的示例中,我试图在 Impl class 声明中分配 TEST 的值,该声明位于 Widget.cpp 而不是它自己的 header 文件中。 Impl 的定义也包含在 Widget.cpp 中,我相信这是我困惑的根源。
只需将 TEST 的赋值移动到 Impl 声明之外(但仍在 Widget/Impl 定义内),问题似乎就解决了。
在下面的两个示例解决方案中,可以使用
从 Widget 中访问 TEST
pimpl->TEST
正在尝试将不同的字符串分配给 TEST,即
pimpl->TEST = "changed"
导致编译器错误(应该如此)。此外,尝试从 Widget 外部访问 pimpl->TEST 也会导致编译器错误,因为 pimpl 被声明为 Widget 私有。
所以现在TEST是一个常量字符串,只能被Widget访问,没有在public header中命名,并且在所有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++ 的缺乏经验使我一开始没有意识到这一点。我希望这可以避免混淆这两个主题。
#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
成为 returns 一个 const std::string&
的静态函数。这将允许您内联定义它。
您也可以在您的示例中将 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";
};
根据使用模式,它可能合适也可能不合适。如果不是,则按照其他答案中的说明定义变量。
背景
我一直在学习如何使用 Herb Sutter 在此页面上描述的更新的 c++11 方法来实现 pimpl 习语:https://herbsutter.com/gotw/_100/
我正在尝试通过向私有实现添加成员变量来修改此示例,特别是 std::string(尽管 char* 具有相同的问题)。
问题
由于使用了 static const non-integral 类型,这似乎是不可能的。 In-class 只能对整数类型进行初始化,但因为它是静态的,所以也不能在构造函数中进行初始化。
解决这个问题的方法是在header文件中声明私有变量,并在实现中对其进行初始化,如下所示: C++ static constant string (class member)
但是,这个解决方案对我不起作用,因为它破坏了我试图通过 pimpl idiom 实现的封装。
问题
如何在使用 pimpl idiom 时在隐藏内部 class 中隐藏 non-integral static 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 不能在声明时赋值,因为它不是整数类型;但是,因为它是静态的,所以这是必需的。这似乎暗示它无法完成。
我整个下午都在搜索之前的 questions/answers,但找不到任何提出保留 information-hiding 属性 pimpl 习语的解决方案。
解决方案观察:
在我上面的示例中,我试图在 Impl class 声明中分配 TEST 的值,该声明位于 Widget.cpp 而不是它自己的 header 文件中。 Impl 的定义也包含在 Widget.cpp 中,我相信这是我困惑的根源。
只需将 TEST 的赋值移动到 Impl 声明之外(但仍在 Widget/Impl 定义内),问题似乎就解决了。
在下面的两个示例解决方案中,可以使用
从 Widget 中访问 TESTpimpl->TEST
正在尝试将不同的字符串分配给 TEST,即
pimpl->TEST = "changed"
导致编译器错误(应该如此)。此外,尝试从 Widget 外部访问 pimpl->TEST 也会导致编译器错误,因为 pimpl 被声明为 Widget 私有。
所以现在TEST是一个常量字符串,只能被Widget访问,没有在public header中命名,并且在所有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++ 的缺乏经验使我一开始没有意识到这一点。我希望这可以避免混淆这两个主题。
#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
成为 returns 一个 const std::string&
的静态函数。这将允许您内联定义它。
您也可以在您的示例中将 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";
};
根据使用模式,它可能合适也可能不合适。如果不是,则按照其他答案中的说明定义变量。