今天我和朋友讨论了静态和动态类型语言之间的区别(有关静态和动态类型语言in this SO question之间差异的更多信息)。在那之后,我想知道在C ++中可以使用什么样的技巧来模拟这样的动态行为。
在C ++中,与其他静态类型语言一样,变量类型在编译时指定。例如,让我们说我必须从文件中读取大量数字,这些数字在大多数情况下非常小,足够小以适合unsigned short
类型。这是一个棘手的问题,少量的这些值要大得多,大到足以需要存储unsigned long long
。
由于我假设我要对所有这些进行计算,我希望所有这些都以相同的顺序存储在相同容器中的相同容器中,而不是从输入文件中读取它们。 即可。天真的方法是将它们存储在类型vector
的{{1}}中,但这意味着通常需要多达实际需要空间的4倍(unsigned long long
2个字节,{{ 1}} 8个字节)。
在动态类型语言中,变量的类型在运行时解释并强制转换为适合的类型。 如何在C ++中实现类似的功能?
我的第一个想法是通过指针来做,根据它的大小,我会用适当的类型存储数字。这有一个明显的缺点,即必须存储指针,但是因为我认为我还是要将它们存储在堆中,所以我认为这不重要。
我完全相信你们中的许多人能给我更好的解决方案......
unsigned short
编辑1: 问题不是关于保存内存,我知道在动态类型语言中,在示例中存储数字的必要空间将比在C ++中更多,但问题不在于它,它和#39; s关于使用一些c ++机制模拟动态类型语言。
答案 0 :(得分:6)
选项包括......
代码指定一组不同的,受支持的类型T0,T1,T2,T3 ......,以及 - 概念 - 创建管理类型
struct X
{
enum { F0, F1, F2, F3... } type_;
union { T0 t0_; T1 t1_; T2 t2_; T3 t3_; ... };
};
因为可以放入union
的类型存在限制,并且如果使用展示位置绕过它们 - new
需要注意确保充分对齐和正确的析构函数调用,通用实现变得更加复杂,使用boost::variant<>
通常更好。请注意,type_
字段需要一些空格,union
至少与sizeof t0_
,sizeof t1_
...的最大值相同,并且可能需要填充。
也可以使用一个模板化的构造函数和赋值运算符来调用typeid
并记录std::type_info
,从而允许将来的操作,例如“恢复特定值,如果是特定于某个特定的-类型”。获取此行为的最简单方法是使用boost::any
。
您可以使用虚拟析构函数和您需要的任何函数(例如virtual void output(std::ostream&)
)创建基本类型,然后为short
和long long
中的每一个派生一个类。存储指向基类的指针。
在您的特定场景中,您只有一些大数字:您可以执行一些操作,例如将short
值中的一个保留为哨兵,指示此位置的实际值可以按位重新创建移动和ORing以下4个值。例如......
10 299 32767 0 0 192 3929 38
...可编码:
10
299
// 32767 is a sentinel indicating next 4 values encode long long
(0 << 48) + (0 << 32) + (192 << 16) + 3929
38
此处的概念类似于国际字符集的UTF-8编码。这将非常节省空间,但它适合前向迭代,而不是随机访问索引la [123]
。
答案 1 :(得分:1)
您可以创建一个用于存储动态值的类:
enum class dyn_type {
none_type,
integer_type,
fp_type,
string_type,
boolean_type,
array_type,
// ...
};
class dyn {
dyn_type type_ = dyn_type::none_type;
// Unrestricted union:
union {
std::int64_t integer_value_;
double fp_value_;
std::string string_value_;
bool boolean_value_;
std::vector<dyn> array_value_;
};
public:
// Constructors
dyn()
{
type_ = dyn_type::none_type;
}
dyn(std::nullptr_t) : dyn() {}
dyn(bool value)
{
type_ = dyn_type::boolean_type;
boolean_value_ = value;
}
dyn(std::int32_t value)
{
type_ = dyn_type::integer_type;
integer_value_ = value;
}
dyn(std::int64_t value)
{
type_ = dyn_type::integer_type;
integer_value_ = value;
}
dyn(double value)
{
type_ = dyn_type::fp_type;
fp_value_ = value;
}
dyn(const char* value)
{
type_ = dyn_type::string_type;
new (&string_value_) std::string(value);
}
dyn(std::string const& value)
{
type_ = dyn_type::string_type;
new (&string_value_) std::string(value);
}
dyn(std::string&& value)
{
type_ = dyn_type::string_type;
new (&string_value_) std::string(std::move(value));
}
// ....
// Clear
void clear()
{
switch(type_) {
case dyn_type::string_type:
string_value_.std::string::~string();
break;
//...
}
type_ = dyn_type::none_type;
}
~dyn()
{
this->clear();
}
// Copy:
dyn(dyn const&);
dyn& operator=(dyn const&);
// Move:
dyn(dyn&&);
dyn& operator=(dyn&&);
// Assign:
dyn& operator=(std::nullptr_t);
dyn& operator=(std::int64_t);
dyn& operator=(double);
dyn& operator=(bool);
// Operators:
dyn operator+(dyn const&) const;
dyn& operator+=(dyn const&);
// ...
// Query
dyn_type type() const { return type_; }
std::string& string_value()
{
assert(type_ == dyn_type::string_type);
return string_value_;
}
// ....
// Conversion
explicit operator bool() const
{
switch(type_) {
case dyn_type::none_type:
return true;
case dyn_type::integer_type:
return integer_value_ != 0;
case dyn_type::fp_type:
return fp_value_ != 0.0;
case dyn_type::boolean_type:
return boolean_value_;
// ...
}
}
// ...
};
用于:
std::vector<dyn> xs;
xs.push_back(3);
xs.push_back(2.0);
xs.push_back("foo");
xs.push_back(false);
答案 2 :(得分:0)
An easy way to get dynamic language behavior in C++ is to use a dynamic language engine, e.g. for Javascript.
Or, for example, the Boost library provides an interface to Python.
Possibly that will deal with a collection of numbers in a more efficient way than you could do yourself, but still it's extremely inefficient compared to just using an appropriate single common type in C++.
答案 3 :(得分:-1)
The normal way of dynamic typing in C++ is a boost::variant
or a boost::any
.
But in many cases you don't want to do that. C++ is a great statically typed language and it's just not your best use case to try to force it to be dynamically typed (especially not to save memory use). Use an actual dynamically typed language instead as it is very likely better optimized (and easier to read) for that use case.