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