背景
我一直在学习如何使用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类声明中(在Widget.cpp而不是其自己的头文件中)分配TEST的值。Impl的定义也包含在Widget.cpp中,我认为这是我的困惑之源。
通过简单地将TEST的赋值移动到Impl声明之外(但仍在Widget / Impl定义内),问题似乎已得到解决。
在下面的两个示例解决方案中,可以使用pimpl->TEST从Widget中访问TEST。
尝试将不同的字符串分配给TEST,即 pimpl->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 ++经验不足最初阻碍了我意识到这一点。 我希望这避免了两个主题上的任何混淆。