C++ 如何在使用pimpl习惯用法时创建私有静态常量字符串 背景

C++ 如何在使用pimpl习惯用法时创建私有静态常量字符串 背景,c++,c++11,pimpl-idiom,C++,C++11,Pimpl Idiom,我一直在学习如何使用Herb Sutter在本页描述的更新的c++11方法实现pimpl习惯用法: 我试图通过向私有实现添加一个成员变量来修改这个示例,特别是std::string(尽管char*也有同样的问题) 问题 由于使用静态常量非整数类型,这似乎是不可能的。只能对整型进行类内初始化,但由于整型是静态的,因此也无法在构造函数中进行初始化 此问题的解决方案是在头文件中声明私有变量,并在实现中对其进行初始化,如下所示: 但是,这个解决方案不适合我,因为它破坏了我试图通过pimpl习惯用法实现

我一直在学习如何使用Herb Sutter在本页描述的更新的c++11方法实现pimpl习惯用法:

我试图通过向私有实现添加一个成员变量来修改这个示例,特别是std::string(尽管char*也有同样的问题)

问题 由于使用静态常量非整数类型,这似乎是不可能的。只能对整型进行类内初始化,但由于整型是静态的,因此也无法在构造函数中进行初始化

此问题的解决方案是在头文件中声明私有变量,并在实现中对其进行初始化,如下所示:

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

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

例子 下面是我能想出的最简单(不正确)的例子来演示这个问题:

Widget.h:

#ifndef WIDGET_H_
#define WIDGET_H_

#include <memory>

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

#endif
请注意,此示例未能编译,因为变量TEST不是整型,所以无法在声明时赋值;但是,因为它是静态的,所以这是必需的。这似乎意味着这是不可能做到的

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

解决方案观察: 在上面的示例中,我试图在Impl类声明中分配TEST的值,该声明位于Widget.cpp内部,而不是它自己的头文件。Impl的定义也包含在Widget.cpp中,我相信这是我困惑的根源

通过简单地将测试的分配移到Impl声明之外(但仍然在Widget/Impl定义中),问题似乎得到了解决

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

pimpl->TEST

尝试将不同的字符串分配到测试中,即

pimpl->TEST=“changed”

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

所以现在TEST是一个常量字符串,它只能被一个小部件访问,在public头中没有命名,并且一个副本在小部件的所有实例之间共享,完全按照需要

解决方案示例(char*): 在使用char*的情况下,注意添加了另一个const关键字;这对于防止将测试更改为指向另一个字符串文字是必要的

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() { }
#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() { }
#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() { }
#包括“Widget.h”
#包括
类小部件::Impl{
公众:
静态常量字符*常量测试;
Impl(){};
~Impl(){};
};
常量字符*常量(小部件::Impl::TEST)=“TEST”;
Widget::Widget():pimpl(新Widget::Impl()){}
小部件::~Widget(){}
解决方案示例(字符串): 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() { }
#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() { }
#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() { }
#包括“Widget.h”
#包括
类小部件::Impl{
公众:
静态常量std::字符串测试;
Impl(){};
~Impl(){};
};
const std::string小部件::Impl::TEST=“TEST”;
Widget::Widget():pimpl(新Widget::Impl()){}
小部件::~Widget(){}
更新: <>我意识到这个问题的解决方案完全不与piml习惯用法无关,只是定义静态常量的标准C++方式。我已经习惯了其他语言,如java,在声明的时候必须定义常量,所以我的C++经验不足,使我无法实现这一点。我希望这可以避免对这两个主题的混淆。

\include
#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() { }
类小部件{ 公众: Widget(); ~Widget(); 私人: 类Impl; std::唯一的ptr pimpl; }; /***cpp***/ #包括 类小部件::Impl{ 公众: 静态常量std::字符串测试; Impl(){}; ~Impl(){}; }; const std::string小部件::Impl::TEST=“TEST”; Widget::Widget():pimpl(新Impl()){} 小部件::~Widget(){}

您可能想考虑制作<代码>测试> /Cord>一个静态函数,它返回<代码> const STD::String和。这将允许您以内联方式定义它。

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

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++禁止将字符串常量转换为“char *”/COD> >我编写的代码没有错误,所以我不知道你在说什么。在我的代码中没有从
std::string
char*
的转换。从C语言中构造一个C++字符串是很容易的。编译器错误指向第一个双引号包围“test”,所以它似乎在讨论将字符串赋给char *变量。这个解决方案似乎起作用,但是在编写了一些测试代码尝试之后,在实践中使用起来感觉非常尴尬。而且还需要对每个私有字符串进行函数调用,这表明它不能很好地扩展。这被认为是解决问题的“最佳实践”方法吗?你必须弄清楚你想要什么。如果在
pimpl
对象中声明字符串,可能是因为您不需要对它们进行公共访问。否则,你会在主类中声明它们。@richard hodges:很抱歉,我误读了你最初的回答,我以前的回答