用新类型覆盖基类成员

时间:2013-12-14 13:38:14

标签: c++ derived-class return-type base-class

我正在尝试使用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.456DerivedClass2可能有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;
};

我想我想问的是,这是一个“好”的编码习惯吗?如果没有,返回多种类型或接受多种类型的函数是否有不同的方法?

3 个答案:

答案 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为例,我们意识到我们缺少一些东西:

  1. 例如,我们无法实施getattr,因为ObjectPtrMethod不同,
  2. 这是一个递归实现,但没有基础:我们缺少固有类型(通常为BoolIntegerString,...)
  3. 处理第一个问题相对容易,我们将对象转换为可调用

    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