我一直在学习如何使用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中,我相信这是我混淆的原因。
只需在Impl声明之外移动TEST的赋值(但仍在Widget / Impl定义中),问题似乎就解决了。
在下面的两个示例解决方案中,可以使用
从Widget中访问TEST pimpl->TEST
尝试将不同的字符串分配给TEST,即
pimpl->TEST = "changed"
导致编译器错误(应该如此)。此外,尝试从Widget外部访问pimpl-&gt; TEST也会导致编译器错误,因为pimpl被声明为Widget的私有。
所以现在TEST是一个常量字符串,只能由Widget访问,不在公共头中命名,并且Widget的所有实例之间共享一个副本,完全符合要求。
在使用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 ++的经验不足使我无法实现这一点。我希望这可以避免对这两个主题产生任何混淆。
答案 0 :(得分:3)
#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&
的静态函数。这将允许您内联定义。
答案 1 :(得分: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";
};
根据使用模式,可能适合与否。如果没有,则按照另一个答案中的说明定义变量。