我正在尝试使用C ++来模拟动态类型之类的东西。我正在接近继承类的问题。例如,函数可以定义为
BaseClass* myFunction(int what) {
if (what == 1) {
return new DerivedClass1();
} else if (what == 2) {
return new DerivedClass2();
}
}
基类和每个派生类将具有相同的成员,但具有不同的类型。例如,BaseClass
可能有int xyz = 0
(不表示任何内容),DerivedClass1
可能有double xyz = 123.456
,DerivedClass2
可能有bool xyz = true
。然后,我可以创建返回一种类型的函数,但实际上返回了几种不同的类型。问题是,当我尝试这样做时,我总是访问基类的xyz
版本。我已经尝试使用指针(void*
作为基础,而“正确”作为派生类),但是每次我想访问成员时,我都必须做*(double*)(obj->xyz)
这样的事情。最终变得非常混乱和不可读。
以下是我的代码大纲:
#include <iostream>
using std::cout;
using std::endl;
class Foo {
public:
Foo() {};
void* member;
};
class Bar : public Foo {
public:
Bar() {
member = new double(123.456); // Make member a double
};
};
int main(int argc, char* args[]) {
Foo* obj = new Bar;
cout << *(double*)(obj->member);
return 0;
};
我想我想问的是,这是一个“好”的编码习惯吗?如果没有,返回多种类型或接受多种类型的函数是否有不同的方法?
答案 0 :(得分:2)
实际上并不是这样做的方法。
有两种典型的方法可以实现类似于C ++中动态类型的东西:
Visitor
模式后者使用boost::variant
相当简单,前者在网上有很好的记录。我个人建议boost::variant
开始。
如果你想沿着完整的动态打字道路前进,那么事情会变得棘手。在动态类型中,对象通常表示为包含其他对象和函数的字典,函数接受对象的列表/字典并返回对象的列表/字典。用C ++建模它是可行的,但它会冗长......
如何用动态类型语言表示对象?
更通用的表示形式是语言将对象表示为一组值(通常是命名的)和一组方法(也命名为)。简化表示如下:
struct Object {
using ObjectPtr = std::shared_ptr<Object>;
using ObjectList = std::vector<ObjectPtr>;
using Method = std::function<ObjectList(ObjectList const&)>;
std::map<std::string, ObjectPtr> values;
std::map<std::string, Method> methods;
};
如果我们以Python为例,我们意识到我们缺少一些东西:
getattr
,因为ObjectPtr
与Method
不同,Bool
,Integer
,String
,...) 处理第一个问题相对容易,我们将对象转换为可调用:
class Object {
public:
using ObjectPtr = std::shared_ptr<Object>;
using ObjectList = std::vector<ObjectPtr>;
using Method = std::function<ObjectList(ObjectList const&)>;
virtual ~Object() {}
//
// Attributes
//
virtual bool hasattr(std::string const& name) {
throw std::runtime_error("hasattr not implemented");
}
virtual ObjectPtr getattr(std::string const&) {
throw std::runtime_error("gettattr not implemented");
}
virtual void setattr(std::string const&, ObjectPtr) {
throw std::runtime_error("settattr not implemented");
}
//
// Callable
//
virtual ObjectList call(ObjectList const&) {
throw std::runtime_error("call not implemented");
}
virtual void setcall(Method) {
throw std::runtime_error("setcall not implemented");
}
}; // class Object
class GenericObject: public Object {
public:
//
// Attributes
//
virtual bool hasattr(std::string const& name) override {
return values.count(name) > 0;
}
virtual ObjectPtr getattr(std::string const& name) override {
auto const it = values.find(name);
if (it == values.end) {
throw std::runtime_error("Unknown attribute");
}
return it->second;
}
virtual void setattr(std::string const& name, ObjectPtr object) override {
values[name] = std::move(object);
}
//
// Callable
//
virtual ObjectList call(ObjectList const& arguments) override {
if (not method) { throw std::runtime_error("call not implemented"); }
return method(arguments);
}
virtual void setcall(Method m) {
method = std::move(m);
}
private:
std::map<std::string, ObjectPtr> values;
Method method;
}; // class GenericObject
处理第二个问题需要播种递归:
class BoolObject final: public Object {
public:
static BoolObject const True = BoolObject{true};
static BoolObject const False = BoolObject{false};
bool value;
}; // class BoolObject
class IntegerObject final: public Object {
public:
int value;
}; // class IntegerObject
class StringObject final: public Object {
public:
std::string value;
}; // class StringObject
现在您需要添加功能,例如值比较。
答案 1 :(得分:0)
您可以尝试以下设计:
#include <iostream>
using std::cout;
using std::endl;
template<typename T>
class Foo {
public:
Foo() {};
virtual T& member() = 0;
};
class Bar : public Foo<double> {
public:
Bar() : member_(123.456) {
};
virtual double& member() { return member_; }
private:
double member_;
};
int main(int argc, char* args[]) {
Foo<double>* obj = new Bar;
cout << obj->member();
return 0;
};
但结果是Foo
类已经需要专门化,并且不再是 任何 类型的容器。
其他方法,例如,在基类中使用boost::any
答案 2 :(得分:0)
如果您需要动态解决方案,则应坚持使用void*
和尺寸或boost::any
。您还需要传递一些类型信息作为整数代码或字符串,以便您可以解码内容的实际类型。
另请参见属性设计模式。
例如,您可以查看zeromq套接字选项https://github.com/zeromq/libzmq/blob/master/src/options.cpp