C++ 避免空洞指针

C++ 避免空洞指针,c++,c++11,void-pointers,C++,C++11,Void Pointers,我正在用C++11实现我自己的编程语言 我设计的数据类型之一是Token类。它意味着存储从源文件读取的令牌,以及令牌的内容、类型和遇到它的行 令牌可以是单字符符号、长字符串、数字或名称。因此,它需要能够存储不同的数据类型,一个字符表示符号,一个双精度表示数字,一个std::string表示名称和字符串 我实现这一点的方法是将该值存储在一个void指针中,并添加一个custome enum属性,该属性有助于理解将该void指针强制转换为什么类型 当然,我可以为令牌的每个子类型都创建一个类,但在某个

我正在用C++11实现我自己的编程语言

我设计的数据类型之一是
Token
类。它意味着存储从源文件读取的令牌,以及令牌的内容、类型和遇到它的行

令牌可以是单字符符号、长字符串、数字或名称。因此,它需要能够存储不同的数据类型,一个字符表示符号,一个双精度表示数字,一个std::string表示名称和字符串

我实现这一点的方法是将该值存储在一个void指针中,并添加一个custome enum属性,该属性有助于理解将该void指针强制转换为什么类型

当然,我可以为令牌的每个子类型都创建一个类,但在某个时刻,我无论如何都需要将它们存储为
令牌*
,这意味着我仍然需要一个枚举,它可以帮助我知道我应该将
令牌*
转换为什么类型

下面是它的实际代码:

enum token_type {
    symbol,
    number,
    name,
    string
};

struct Token {
    void* value = nullptr;
    token_type type;
    unsigned int line;

    Token(void* new_value, token_type new_type, unsigned int new_line):
        value(new_value), type(new_type), line(new_line)
        {}

    ~Token() {
        switch (type) {
            case symbol:
                delete (char*) value;
                break;
            case number:
                delete (double*) value;
                break;
            case name:
            case string:
                delete (std::string*) value;
        }
    }
};

实现这一点的好设计模式是什么,它可以避免使用空指针和(可能)枚举?每个人都不断告诉我这种设计是错误的,但我没有得到任何关于如何实际改善这种情况的建议,所以我在这里询问。

您可以按如下方式删除该类型:

class Token {
    using Deleter = void(void*);
    using Func = void(*)(void*);

    template<typename T>
    static void proto(void *ptr) {
        T t = static_cast<T*>(ptr);
        // do whatever you want here...
        // ... or use specializations.
    }

public:
    template<typename T>
    Token(T* value):
        value{value, [](void *ptr) { delete static_cast<T*>(ptr); }},
        func{&proto<T>}
    {}

    void operator()() {
        func(value.get());
    }

private:
    std::unique_ptr<void, Deleter> value;
    Func func;
};
令牌可以是单字符符号、长字符串、数字或名称

无论何时,只要有一个对象可以是许多事物中的一个,它就是一个和类型/不相交的并集/变量。直接映射到:

using Token = variant<char, std::string, int, Name>;
使用Token=variant;
(其中
variant
这里是或,在C++1z中是新的,)。这是一个类模板,它在内部跟踪它是哪种类型,并以类型安全的方式向您公开它

我实现这一点的方法是将该值存储在一个void指针中,并添加一个custome enum属性,该属性有助于理解将该void指针强制转换为什么类型


variant
看作基本上就是这样做的-除了
void*
之外,
variant
是一种值类型。它是可复制的,可移动的,可分配的,可破坏的。它拥有自己的数据。如果您阅读了Boost教程,它将解释如何以类型安全的方式访问底层存储

你有没有看过
boost::variant
?嗯,大多数编译器都被空洞的指针淹没了,所以我不会说使用它们一定是错误的,但是既然你知道你需要的数据类型,你可以研究使用variant或类似的数据类型。@hay不确定我能不能使用boost。我正在使用Linux仿真器开发Android,而boost不在那里。也许我应该手动下载。我还是不确定它的便携性。你可以试试。Boost有一个它可以构建的编译器列表。如果你能访问C++17,你就有了std::variant。@Hayt刚试过。是的,我可以使用c++17的g++编译标志(
-std=c++1z
)。你能在此基础上得出答案吗?什么?你能多加几句(我是说很多)评论吗<代码>在类中使用对我来说是全新的。还有,我将如何使用它?@user6245072
使用
声明。。。从来没听过。很好,我会在几分钟内添加更多细节。等等,不,我知道他们,我只是从来没有在课堂上见过他们。
value{value)
也是一个错误吗?这是一个类型擦除的空函数对象。我不确定这如何解决OP的令牌问题,该令牌可以是4种类型中的一种。@user6245072沉默示例中的一些警告。:-…如果使用变量,可以安全地删除这些行。
using Token = variant<char, std::string, int, Name>;